/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - eeschema file format support
 *  Copyright (C) 2024..2025 Aron Barath
 *  Copyright (C) 2022..2024 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust Fund in 2024)
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *  Contact:
 *    Project page: http://repo.hu/projects/sch-rnd
 *    contact lead developer: http://www.repo.hu/projects/sch-rnd/contact.html
 *    mailing list: http://www.repo.hu/projects/sch-rnd/contact.html
 */

static int eeschema_render_wires(read_ctx_t* ctx, csch_cgrp_t* const dst,
	gsxl_node_t* const objroot, gsxl_node_t* const pts)
{
	int got_it;
	float prev_x, prev_y;

	gsxl_node_t* xys = pts;

	dbg_printf("wires\n");

	got_it = 0;

	for(;xys;xys=xys->next)
	{
		if(strcmp(xys->str, "xy")==0)
		{
			float x, y;

			if(eechema_parse_xy(ctx, xys->children, &x, &y)!=0)
			{
				return -1;
			}

			if(got_it)
			{
				dbg_printf("  add (%f;%f) (%f;%f)\n",
					prev_x, prev_y, x, y);

				csch_alien_mknet(&ctx->alien, dst, prev_x, prev_y, x, y);
			}
			else
			{
				got_it = 1;
			}

			prev_x = x;
			prev_y = y;
		}
		else
		{
			unexpected_child(ctx, pts, xys);
			return -1;
		}
	}

	dbg_printf("done\n");

	return 0;
}

static csch_chdr_t* eeschema_render_polyline(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	gsxl_node_t* const pts, const char* stroke, const char* fill,
	int close_it, int relative_pts, float pos_x, float pos_y)
{
	int got_it;
	float first_x, first_y;
	float prev_x, prev_y;

	gsxl_node_t* xys = pts;

	csch_chdr_t* p = csch_alien_mkpoly(&ctx->alien, dst, stroke, fill);

	dbg_printf("mkpoly\n");

	got_it = 0;

	for(;xys;xys=xys->next)
	{
		if(strcmp(xys->str, "xy")==0)
		{
			float x, y;

			if(eechema_parse_xy(ctx, xys->children, &x, &y)!=0)
			{
				return NULL;
			}

			if(relative_pts)
			{
				x += pos_x;
				y += pos_y;
			}

			if(got_it)
			{
				dbg_printf("  append (%f;%f) (%f;%f)\n",
					prev_x, prev_y, x, y);

				csch_alien_append_poly_line(&ctx->alien, p,
					prev_x, prev_y, x, y);
			}
			else
			{
				first_x = x;
				first_y = y;

				got_it = 1;
			}

			prev_x = x;
			prev_y = y;
		}
		else
		{
			unexpected_child(ctx, pts, xys);
			return NULL;
		}
	}

	if(close_it)
	{
		dbg_printf("  append (%f;%f) (%f;%f)\n", prev_x, prev_y,
			first_x, first_y);

		csch_alien_append_poly_line(&ctx->alien, p, prev_x, prev_y,
			first_x, first_y);
	}

	dbg_printf("done\n");

	return p;
}

static csch_chdr_t* eeschema_render_rectangle(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	gsxl_node_t* const start, gsxl_node_t* const end, const char* stroke,
	const char* fill)
{
	float start_x, start_y;
	float end_x, end_y;

	if(eechema_parse_xy(ctx, start, &start_x, &start_y)!=0 ||
		eechema_parse_xy(ctx, end, &end_x, &end_y)!=0)
	{
		return NULL;
	}

	dbg_printf("mkrect (%f;%f) (%f;%f)\n", start_x, start_y, end_x, end_y);

	return csch_alien_mkrect(&ctx->alien, dst,
		start_x, start_y, end_x, end_y, stroke, fill);
}

static csch_chdr_t* eeschema_render_circle(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	gsxl_node_t* const center, gsxl_node_t* const radius, const char* stroke,
	const char* fill)
{
	float x, y;
	float r;

	if(eechema_parse_xy(ctx, center, &x, &y)!=0 ||
		eechema_parse_num(ctx, radius, &r)!=0)
	{
		return NULL;
	}

	dbg_printf("mkcircle (%f;%f) %f)\n", x, y, r);

	return csch_alien_mkcircle(&ctx->alien, dst, x, y, r, stroke, fill);
}

/* compute circle center and radius from 3 points on the circle -- */
/* return zero on success; non-zero on error (determinant==0) */
static int eeschema_solve_circle_eq(read_ctx_t* const ctx,
	float sx, float sy, float mx, float my, float ex, float ey,
	float* const out_cx, float* const out_cy, float* const out_r)
{
	/*
		(sx-cx)^2 + (sy-cy)^2 = r^2
		(mx-cx)^2 + (my-cy)^2 = r^2
		(ex-cx)^2 + (ey-cy)^2 = r^2

		==>

		sx^2 - 2sxcx + cx^2 + sy^2 - 2sycy + cy^2 = r^2
		mx^2 - 2mxcx + cx^2 + my^2 - 2mycy + my^2 = r^2
		ex^2 - 2excx + cx^2 + ey^2 - 2eycy + ey^2 = r^2

		==>

		sx^2 - mx^2 - 2sxcx + 2mxcx + sy^2 - my^2 - 2sycy + 2mycy = 0
		mx^2 - ex^2 - 2mxcx + 2excx + my^2 - ey^2 - 2mycy + 2eycy = 0

		==>

		(2mx - 2sx)cx + (2my - 2sy)cy = -(sx^2 - mx^2 + sy^2 - my^2)
		(2ex - 2mx)cx + (2ey - 2my)cy = -(mx^2 - ex^2 + my^2 - ey^2)

		==>

		smx * cx + smy * cy = smc
		mex * cx + mey * cy = mec
	*/

	float smx = 2*mx - 2*sx;
	float smy = 2*my - 2*sy;
	float smc = -(sx*sx - mx*mx + sy*sy - my*my);

	float mex = 2*ex - 2*mx;
	float mey = 2*ey - 2*my;
	float mec = -(mx*mx - ex*ex + my*my - ey*ey);

	/*
		eq system:

		smx*cx + smy*cy = smc
		mex*cx + mey*cy = mec

		we use Cramer's Rule to solve this:

		 coeff_mat       x_mat         y_mat
		| smx smy |   | smc smy |   | smx smc |
		| mex mey |   | mec mey |   | mex mec |
	*/

#define DET(a, b, c, d) ((a)*(d) - (b)*(c))

	float det_coeff = DET(smx, smy, mex, mey);
	float det_x_mat = DET(smc, smy, mec, mey);
	float det_y_mat = DET(smx, smc, mex, mec);

#undef DET

	if(fabs(det_coeff)<0.0001)
	{
		return -1;
	}

	*out_cx = det_x_mat / det_coeff;
	*out_cy = det_y_mat / det_coeff;

	{
		float a = sx - (*out_cx);
		float b = sy - (*out_cy);

		*out_r = sqrt(a*a + b*b);
	}

	return 0;
}

static int render_arc_segment(read_ctx_t* const ctx, csch_chdr_t* const arc,
	const float cx, const float cy, const float r, float start, float end)
{
	float delta = end - start;

/*
	if(delta<0.0)
	{
		float t = start;
		start = end;
		end = t;
		delta = -delta;
	}
*/

	if(delta>180.0)
	{
		dbg_printf("  MORE\n");
		delta -= 360.0;
	}
	else
	if(delta<(-180.0))
	{
		dbg_printf("  LESS\n");
		delta += 360.0;
		/*delta = 360.0 + delta;*/
	}

	dbg_printf("  add segment: start=%f, delta=%f\n", start, delta);

	csch_alien_append_poly_arc(&ctx->alien, arc, cx, cy, r, start, delta);

	return 0;
}

static csch_chdr_t* eeschema_render_arc(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	gsxl_node_t* const start, gsxl_node_t* const mid, gsxl_node_t* const end,
	const char* stroke, const char* fill)
{
	csch_chdr_t* arc;

	float sx, sy;
	float mx, my;
	float ex, ey;
	float cx, cy;
	float r;
	float ang_start;
	float ang_end;
	float ang_mid;
	float ang_delta;

	if(eechema_parse_xy(ctx, start, &sx, &sy)!=0 ||
		eechema_parse_xy(ctx, mid, &mx, &my)!=0 ||
		eechema_parse_xy(ctx, end, &ex, &ey)!=0)
	{
		return NULL;
	}

	dbg_printf("mkarc (%f;%f) (%f;%f) (%f;%f)\n", sx, sy, mx, my, ex, ey);

	if(fabs(sx-ex)<0.01 && fabs(sy-ey)<0.01)
	{
		dbg_printf("  degenerate case!\n");
		return csch_alien_mkarc(&ctx->alien, dst, sx, sy, 0.1, 0, 360, stroke);
	}

	if(eeschema_solve_circle_eq(ctx, sx, sy, mx, my, ex, ey, &cx, &cy, &r)!=0)
	{
		eechema_error(ctx, objroot, "could not solve circle equation");
		return NULL;
	}

	dbg_printf("  solution: c=(%f;%f), r=%f\n", cx, cy, r);

	ang_start = atan2(cy-sy, sx-cx) * 180.0 / M_PI;
	ang_mid   = atan2(cy-my, mx-cx) * 180.0 / M_PI;
	ang_end   = atan2(cy-ey, ex-cx) * 180.0 / M_PI;

	if(ctx->cur_libsym)
	{
		/* if rendering into a symbol, Y increases downwards, messing up */
		/* the computation above, so just hack it */
		ang_start = -ang_start;
		ang_mid   = -ang_mid;
		ang_end   = -ang_end;
	}

	if(ang_start<0) { ang_start += 360.0; }
	if(ang_mid<0) { ang_mid += 360.0; }
	if(ang_end<0) { ang_end += 360.0; }

	dbg_printf("  angles: start=%f, mid=%f, end=%f\n", ang_start, ang_mid,
		ang_end);

	if(!(arc=csch_alien_mkpoly(&ctx->alien, dst, stroke, fill)))
	{
		eechema_error(ctx, objroot, "could not allocate memory for arc");
		return NULL;
	}

	if(render_arc_segment(ctx, arc, cx, cy, r, ang_start, ang_mid)!=0)
	{
		return NULL;
	}

	if(render_arc_segment(ctx, arc, cx, cy, r, ang_mid, ang_end)!=0)
	{
		return NULL;
	}

	if(fill)
	{
		csch_alien_append_poly_line(&ctx->alien, arc, ex, ey, cx, cy);
		csch_alien_append_poly_line(&ctx->alien, arc, cx, cy, sx, sy);
	}

	return arc;
}

static int eeschema_render_bezier(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	gsxl_node_t* const pts, const char* stroke, const char* fill)
{
	float x[4], y[4];
	int xy_count = 0;

	gsxl_node_t* xys = pts;

	dbg_printf("mkbezier\n");

	for(;xys;xys=xys->next)
	{
		if(strcmp(xys->str, "xy")==0)
		{
			if(xy_count==4)
			{
				eechema_error(ctx, objroot, "too many control points");
				return -1;
			}

			if(eechema_parse_xy(ctx, xys->children, &x[xy_count],
				&y[xy_count])!=0)
			{
				return -1;
			}

			++xy_count;
		}
		else
		{
			unexpected_child(ctx, pts, xys);
			return -1;
		}
	}

	if(xy_count!=4)
	{
		eechema_error(ctx, objroot, "not enough control points");
		return -1;
	}

	csch_alien_mkbezier(&ctx->alien, dst, x[0], y[0], x[1], y[1], x[2], y[2],
		x[3], y[3], stroke);

	return 0;
}

static int eeschema_justify_text(read_ctx_t* const ctx,
	gsxl_node_t* const justify, csch_coord_t bbw, csch_coord_t bbh, int rot,
	csch_coord_t* const x, csch_coord_t* const y,
	int* const mirx, int* const miry)
{
	int jleft = 0;
	int jright = 0;
	int jtop = 0;
	int jbottom = 0;

	gsxl_node_t* j;

	if(justify!=NULL)
	{
		for(j=justify;j;j=j->next)
		{
			if(strcmp(j->str, "left")==0)   { jleft   = 1; continue; }
			if(strcmp(j->str, "right")==0)  { jright  = 1; continue; }
			if(strcmp(j->str, "top")==0)    { jtop    = 1; continue; }
			if(strcmp(j->str, "bottom")==0) { jbottom = 1; continue; }

			eechema_error(ctx, j, "unexpected justify option");
			return -1;
		}

		if((jleft && jright) || (jtop && jbottom))
		{
			eechema_error(ctx, justify, "conflicting justify options");
			return -1;
		}
	}

	if(jright)
	{
		/* 100% */

		if(rot==0)
		{
			*mirx = 1;
		}
		else
		{
			*miry = 1;
			*mirx = 1;
			*x -= bbh;
		}
	}
	else
	if(!jleft)
	{
		/* 50% */

		if(rot==0)
		{
			*x -= bbw/2;
		}
		else
		{
			*y -= bbw/2;
		}
	}

	if(jtop)
	{
		if(rot==0)
		{
			*y -= bbh;
		}
		else
		{
			*x += bbh;
		}
	}
	else
	if(!jbottom)
	{
		if(rot==0)
		{
			*y -= bbh/2;
		}
		else
		{
			*x += bbh/2;
		}
	}

	return 0;
}

static csch_chdr_t* eeschema_render_text(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot, gsxl_node_t* const at,
	gsxl_node_t* const justify, const char* const text,
	const char* const stroke)
{
	float x, y;
	int rot;
	csch_text_t* p;

	if(eechema_parse_at(ctx, at, &x, &y, &rot)!=0)
	{
		return NULL;
	}

	TODO("is this how it should be done?");
	rot %= 180;

	if(rot!=0 && rot!=90)
	{
		eechema_error(ctx, at, "text rotation is not 0 nor 90");
		return NULL;
	}

	dbg_printf("mktext (%f;%f), rot %d: %s\n", x, y, rot, text);

	p = (csch_text_t*)csch_alien_mktext(&ctx->alien, dst, x, y, stroke);

	if(!p)
	{
		return NULL;
	}

	p->text = rnd_strdup(text);

	csch_text_update(ctx->sheet, p, 1);

	{
		csch_coord_t bbw;
		csch_coord_t bbh;

		int mirx = 0;
		int miry = 0;

		bbw = p->hdr.bbox.x2 - p->hdr.bbox.x1;
		bbh = p->hdr.bbox.y2 - p->hdr.bbox.y1;

		if(eeschema_justify_text(ctx, justify, bbw, bbh, rot,
			&p->spec1.x, &p->spec1.y, &mirx, &miry)!=0)
		{
			return NULL;
		}

		p->spec_mirx = mirx;
		p->spec_miry = miry;
	}

	p->spec_rot = rot;

	return (csch_chdr_t*)p;
}

static csch_chdr_t* eeschema_render_image_placeholder(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	float x, float y, const char* stroke)
{
	float size = 5;

	float nx = x-size;
	float ny = y-size;
	float px = x+size;
	float py = y+size;

	csch_chdr_t* p = csch_alien_mkpoly(&ctx->alien, dst, stroke, NULL);

	dbg_printf("mk image_placeholder (%f;%f), size %f\n", x, y, size);

	/* \ */
	csch_alien_append_poly_line(&ctx->alien, p, nx, ny, px, py);

	/* / */
	csch_alien_append_poly_line(&ctx->alien, p, nx, py, px, ny);

	/* | */
	csch_alien_append_poly_line(&ctx->alien, p, nx, ny, nx, py);
	csch_alien_append_poly_line(&ctx->alien, p, px, ny, px, py);

	/* - */
	csch_alien_append_poly_line(&ctx->alien, p, nx, ny, px, ny);
	csch_alien_append_poly_line(&ctx->alien, p, nx, py, px, py);

	return p;
}

static csch_chdr_t* eeschema_render_noconnect(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	float x, float y, const char* stroke)
{
	float size = 1.27 * 0.5;

	float nx = x-size;
	float ny = y-size;
	float px = x+size;
	float py = y+size;

	csch_chdr_t* p = csch_alien_mkpoly(&ctx->alien, dst, stroke, NULL);

	dbg_printf("mk no_connect (%f;%f), size %f\n", x, y, size);

	/* \ */
	csch_alien_append_poly_line(&ctx->alien, p, nx, ny, px, py);

	/* / */
	csch_alien_append_poly_line(&ctx->alien, p, nx, py, px, ny);

	return p;
}

static void eeschema_render__append_polyline(csch_chdr_t* const poly_,
	csch_coord_t x1, csch_coord_t y1, csch_coord_t x2, csch_coord_t y2)
{
	csch_cpoly_t* poly = (csch_cpoly_t*)poly_;
	csch_coutline_t* dst = csch_vtcoutline_alloc_append(&poly->outline, 1);
	dst->hdr = poly->hdr;
	dst->hdr.type = CSCH_CTYPE_LINE;
	dst->line.spec.p1.x = x1;
	dst->line.spec.p1.y = y1;
	dst->line.spec.p2.x = x2;
	dst->line.spec.p2.y = y2;
}

static int eeschema_render_global_decor(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	csch_text_t* const text, const char* const shape,
	const float xf, const float yf, const int rot, const char* const stroke)
{
	csch_chdr_t* poly = csch_alien_mkpoly(&ctx->alien, dst, stroke, NULL);

	int left_pointed  = 0;
	int right_pointed = 0;
	int dir;

	const csch_coord_t x0 = csch_alien_coord_x(&ctx->alien, xf);
	const csch_coord_t y0 = csch_alien_coord_y(&ctx->alien, yf);

	csch_coord_t offset;
	csch_coord_t font_height;
	csch_coord_t text_width;
	csch_coord_t half;
	csch_coord_t delta_pt; /* pointed */
	csch_coord_t delta_np; /* non-pointed */

	dbg_printf("global_decor\n");

	if(strcmp(shape, "input")==0)
	{
		left_pointed = 1;
	}
	else
	if(strcmp(shape, "output")==0)
	{
		right_pointed = 1;
	}
	else
	if(strcmp(shape, "bidirectional")==0 || strcmp(shape, "tri_state")==0)
	{
		left_pointed  = 1;
		right_pointed = 1;
	}
	else
	if(strcmp(shape, "passive")==0)
	{
		/* no pointed sides */
	}
	else
	{
		eechema_error(ctx, objroot, "unexpected shape: '%s'", shape);
		return -1;
	}

	font_height = eeschema_get_font_height(ctx);
	dbg_printf("  font_height = %u\n", (unsigned int)font_height);

	/* update the object first to get access to bbox */

	csch_text_update(ctx->sheet, text, 1);

	if(rot==0 || rot==180)
	{
		text_width = text->hdr.bbox.x2 - text->hdr.bbox.x1;
	}
	else
	{
		text_width = text->hdr.bbox.y2 - text->hdr.bbox.y1;
	}

	dbg_printf("  text_width = %u\n", (unsigned int)text_width);

	switch(rot)
	{
	case 0:
	case 90:
		dir = +1;
		break;

	case 180:
	case 270:
		dir = -1;
		break;

	default:
		eechema_error(ctx, objroot, "unexpected rotation value");
		return -1;
	}

	half     = font_height / 2;
	delta_pt = font_height / 2;
	delta_np = font_height / 4;

	if(rot==0 || rot==180)
	{
		if(left_pointed)
		{
			text->spec1.x += dir*delta_pt;
			eeschema_render__append_polyline(poly, x0, y0, x0+dir*half, y0+half);
			eeschema_render__append_polyline(poly, x0, y0, x0+dir*half, y0-half);
			offset = dir*half;
		}
		else
		{
			offset = 0;
			text->spec1.x += dir*delta_np;
			text_width += delta_np;
			eeschema_render__append_polyline(poly, x0, y0-half, x0, y0+half);
		}

		if(right_pointed)
		{
			csch_coord_t xe;
			xe = x0 + offset + dir*text_width + dir*half;
			eeschema_render__append_polyline(poly, xe, y0, xe-dir*half, y0+half);
			eeschema_render__append_polyline(poly, xe, y0, xe-dir*half, y0-half);
		}
		else
		{
			csch_coord_t xe;
			text_width += delta_np;
			xe = x0 + offset + dir*text_width;
			eeschema_render__append_polyline(poly, xe, y0-half, xe, y0+half);
		}

		eeschema_render__append_polyline(poly, x0+offset, y0+half,
			x0+offset+dir*text_width, y0+half);
		eeschema_render__append_polyline(poly, x0+offset, y0-half,
			x0+offset+dir*text_width, y0-half);
	}
	else
	{
		if(left_pointed)
		{
			text->spec1.y += dir*delta_pt;
			eeschema_render__append_polyline(poly, x0, y0, x0+half, y0+dir*half);
			eeschema_render__append_polyline(poly, x0, y0, x0-half, y0+dir*half);
			offset = dir*half;
		}
		else
		{
			offset = 0;
			text->spec1.y += dir*delta_np;
			text_width += delta_np;
			eeschema_render__append_polyline(poly, x0-half, y0, x0+half, y0);
		}

		if(right_pointed)
		{
			csch_coord_t ye;
			ye = y0 + offset + dir*text_width + dir*half;
			eeschema_render__append_polyline(poly, x0, ye, x0+half, ye-dir*half);
			eeschema_render__append_polyline(poly, x0, ye, x0-half, ye-dir*half);
		}
		else
		{
			csch_coord_t ye;
			text_width += delta_np;
			ye = y0 + offset + text_width;
			eeschema_render__append_polyline(poly, x0-half, ye, x0+half, ye);
		}

		eeschema_render__append_polyline(poly, x0+half, y0+offset,
			x0+half, y0+offset+dir*text_width);
		eeschema_render__append_polyline(poly, x0-half, y0+offset,
			x0-half, y0+offset+dir*text_width);
	}

	return 0;
}

static int eeschema_render_hierarchical_decor(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	csch_text_t* const text, const char* const shape,
	const float xf, const float yf, const int rot, const char* const stroke)
{
	csch_chdr_t* poly = csch_alien_mkpoly(&ctx->alien, dst, stroke, NULL);

	const csch_coord_t x0 = csch_alien_coord_x(&ctx->alien, xf);
	const csch_coord_t y0 = csch_alien_coord_y(&ctx->alien, yf);

	int left_pointed  = 0;
	int right_pointed = 0;
	int need_vlines   = 1; /* vertical lines */
	int dir;

	csch_coord_t font_height;
	csch_coord_t text_spacing;
	csch_coord_t icon_size;
	csch_coord_t vline_start;
	csch_coord_t vline_end;
	csch_coord_t half;

	dbg_printf("hierarchical_decor\n");

	if(strcmp(shape, "input")==0)
	{
		left_pointed = 1;
	}
	else
	if(strcmp(shape, "output")==0)
	{
		right_pointed = 1;
	}
	else
	if(strcmp(shape, "bidirectional")==0 || strcmp(shape, "tri_state")==0)
	{
		left_pointed  = 1;
		right_pointed = 1;
		need_vlines   = 0;
	}
	else
	if(strcmp(shape, "passive")==0)
	{
		/* no pointed sides */
	}
	else
	{
		eechema_error(ctx, objroot, "unexpected shape: '%s'", shape);
		return -1;
	}

	dbg_printf("  left_pointed=%i, right_pointed=%i, need_vlines=%i\n",
		left_pointed, right_pointed, need_vlines);

	font_height = eeschema_get_font_height(ctx);
	dbg_printf("  font_height = %u\n", (unsigned int)font_height);

	text_spacing = font_height;
	icon_size = (font_height * 7) / 10;
	half = icon_size / 2;

	switch(rot)
	{
	case 0:
		dir = 1;
		text->spec1.x += text_spacing;
		break;

	case 180:
		dir = -1;
		text->spec1.x -= text_spacing;
		break;

	case 90:
		dir = 1;
		text->spec1.y += text_spacing;
		break;

	case 270:
		dir = -1;
		text->spec1.y -= text_spacing;
		break;

	default:
		eechema_error(ctx, objroot, "unexpected rotation value");
		return -1;
	}

	if(rot==0 || rot==180)
	{
		csch_coord_t xe = x0 + dir*icon_size;

		if(left_pointed)
		{
			eeschema_render__append_polyline(poly, x0, y0, x0+dir*half, y0+half);
			eeschema_render__append_polyline(poly, x0, y0, x0+dir*half, y0-half);
			vline_start = x0 + dir*half;
		}
		else
		{
			eeschema_render__append_polyline(poly, x0, y0-half, x0, y0+half);
			vline_start = x0;
		}

		if(right_pointed)
		{
			eeschema_render__append_polyline(poly, xe, y0, x0+dir*half, y0+half);
			eeschema_render__append_polyline(poly, xe, y0, x0+dir*half, y0-half);
			vline_end = x0 + dir*half;
		}
		else
		{
			eeschema_render__append_polyline(poly, xe, y0-half, xe, y0+half);
			vline_end = xe;
		}

		if(need_vlines)
		{
			eeschema_render__append_polyline(poly, vline_start, y0+half,
				vline_end, y0+half);
			eeschema_render__append_polyline(poly, vline_start, y0-half,
				vline_end, y0-half);
		}
	}
	else
	{
		csch_coord_t ye = y0 + dir*icon_size;

		if(left_pointed)
		{
			eeschema_render__append_polyline(poly, x0, y0, x0+half, y0+dir*half);
			eeschema_render__append_polyline(poly, x0, y0, x0-half, y0+dir*half);
			vline_start = y0 + dir*half;
		}
		else
		{
			eeschema_render__append_polyline(poly, x0-half, y0, x0+half, y0);
			vline_start = y0;
		}

		if(right_pointed)
		{
			eeschema_render__append_polyline(poly, x0, ye, x0+half, y0+dir*half);
			eeschema_render__append_polyline(poly, x0, ye, x0-half, y0+dir*half);
			vline_end = y0 + dir*half;
		}
		else
		{
			eeschema_render__append_polyline(poly, x0-half, ye, x0+half, ye);
			vline_end = ye;
		}

		if(need_vlines)
		{
			eeschema_render__append_polyline(poly, x0+half, vline_start,
				x0+half, vline_end);
			eeschema_render__append_polyline(poly, x0-half, vline_start,
				x0-half, vline_end);
		}
	}

	return 0;
}

static int eeschema_render_netclass_decor(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	const char* const shape, const float length,
	const float x0, const float y0, const int rot, const char* const stroke)
{
	const struct netclass_decor* const decor =
		eeschema_find_netclass_decor(shape);

	float x  = x0; /* decor shape X */
	float y  = y0; /* decor shape Y */
	float xe = x0; /* line end X */
	float ye = y0; /* line end Y */

	float delta;

	if(!decor)
	{
		eechema_error(ctx, objroot, "unknown netclass_flag shape: '%s'",
			shape);
		return -1;
	}

	delta = decor->height_norm * 0.5;

	switch(rot)
	{
	case 0:
		y -= length;
		ye = y + delta;
		break;

	case 180:
		y += length;
		ye = y - delta;
		break;

	case 90:
		x -= length;
		xe = x + delta;
		break;

	case 270:
		x += length;
		xe = x - delta;
		break;
	}

	if(!csch_alien_mkline(&ctx->alien, dst, x0, y0, xe, ye, stroke))
	{
		eechema_error(ctx, objroot, "could not make netclass_flag decor line");
		return -1;
	}

	if(decor->render_decor(ctx, dst, decor, x, y, rot, stroke)!=0)
	{
		eechema_error(ctx, objroot, "could not render netclass_flag decor");
		return -1;
	}

	return 0;
}

static int eeschema_render_busentry_decor(read_ctx_t* const ctx,
	csch_cgrp_t* const dst, gsxl_node_t* const objroot,
	const float x, const float y, const float size_x, const float size_y,
	const char* const stroke)
{
	if(!csch_alien_mkline(&ctx->alien, dst, x, y, x+size_x, y+size_y, stroke))
	{
		eechema_error(ctx, objroot, "could not make bus_entry decor line");
		return -1;
	}

	return 0;
}
