/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2019,2020,2022,2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust in 2023)
 *
 *  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., 31 Milk Street, # 960789 Boston, MA 02196 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
 */

/* #included from draw.c */

vtc0_t draw_poly_x, draw_poly_y;

static void draw_poly_reset(void)
{
	draw_poly_x.used = draw_poly_y.used = 0;
}

static void draw_poly_append(rnd_coord_t x, rnd_coord_t y)
{
	/* do not add duplicate points */
	if (draw_poly_x.used > 0) {
		if ((draw_poly_x.array[draw_poly_x.used-1] == x) && (draw_poly_y.array[draw_poly_x.used-1] == y))
			return;
	}

	vtc0_append(&draw_poly_x, x);
	vtc0_append(&draw_poly_y, y);
}

static void draw_poly_uninit(void)
{
	vtc0_uninit(&draw_poly_x);
	vtc0_uninit(&draw_poly_y);
}

RND_INLINE void draw_poly_arc(csch_arc_t *arc)
{
	double sa = arc->inst.c.start, da = arc->inst.c.delta;
	long int n, steps;
	double a, step;

	if (rnd_render != NULL) {
		double cpp = rnd_render->coord_per_pix;

		if (cpp <= 0)
			cpp = RND_MIL_TO_COORD(1000.0)/600.0; /* assume 600 DPI, for the exporters */

		steps = (double)C2P(arc->inst.c.r) * (double)da / cpp / 10.0;
		if (steps < 0)
			steps= - steps;
		if (steps < 3)
			steps = 3;
		else if (steps > 1000)
			steps = 1000;
	}
	else
		steps = 3;


	step = da/steps;
	for(a = sa, n = 0; n < steps; a += step, n++) {
		csch_coord_t x, y;
		x = rnd_round(arc->inst.c.c.x + arc->inst.c.r * cos(a));
		y = rnd_round(arc->inst.c.c.y + arc->inst.c.r * sin(a));
		draw_poly_append(C2P(x), C2P(y));
	}
}

RND_INLINE void draw_layer_poly_fill(draw_ctx_t *ctx, csch_cpoly_t *p, int selected)
{
	long n;
	csch_coutline_t *o;

	if (!p->has_fill)
		return;

	rnd_render->set_color(ctx->gc, sch_rnd_fill_color(ctx, &p->hdr, csch_fill_fallback(p->hdr.sheet, p, ctx->xform == NULL ? NULL : ctx->xform->fallback_pen), selected));
	draw_poly_reset();
	for(n = 0, o = p->outline.array; n < p->outline.used; n++, o++) {
		switch(o->hdr.type) {
			case CSCH_CTYPE_LINE:
				draw_poly_append(C2P(o->line.inst.c.p1.x), C2P(o->line.inst.c.p1.y));
				draw_poly_append(C2P(o->line.inst.c.p2.x), C2P(o->line.inst.c.p2.y));
				break;
			case CSCH_CTYPE_ARC:
				draw_poly_arc(&o->arc);
				break;
			default:
				/* invalid object in contour, already warned */
				break;
		}
	}
	rnd_render->fill_polygon(ctx->gc, draw_poly_x.used, draw_poly_x.array, draw_poly_y.array);
}

RND_INLINE void draw_layer_poly_stroke(draw_ctx_t *ctx, csch_cpoly_t *p, csch_cpen_t *stroke, int selected)
{
	long n;
	csch_coutline_t *o;

	if (!p->has_stroke)
		return;

	rnd_render->set_color(ctx->gc, sch_rnd_stroke_color(ctx, &p->hdr, stroke, selected));
	rnd_hid_set_line_cap(ctx->gc, pen_cap(stroke));
	rnd_hid_set_line_width(ctx->gc, C2P(stroke->size));

	for(n = 0, o = p->outline.array; n < p->outline.used; n++, o++) {
		switch(o->hdr.type) {
			case CSCH_CTYPE_LINE:
				rnd_render->draw_line(ctx->gc, C2P(o->line.inst.c.p1.x), C2P(o->line.inst.c.p1.y), C2P(o->line.inst.c.p2.x), C2P(o->line.inst.c.p2.y));
				break;
			case CSCH_CTYPE_ARC:
				rnd_render->draw_arc(ctx->gc, C2P(o->arc.inst.c.c.x), C2P(o->arc.inst.c.c.y), C2P(o->arc.inst.c.r), C2P(o->arc.inst.c.r), (180.0 - o->arc.inst.c.start * RND_RAD_TO_DEG), -(o->arc.inst.c.delta * RND_RAD_TO_DEG));
				break;
			default:
				/* invalid, do not draw */
				break;
		}
	}
}

#define poly_area_next(x, y) \
do { \
	if (first) { \
		x1 = (x); \
		y1 = (y); \
		first = 0; \
	} \
	else \
		area += ((lx) - (x)) * ((ly) + (y));\
	lx = (x); \
	ly = (y);\
} while(0)

static void draw_poly_calc_area(draw_ctx_t *ctx, csch_cpoly_t *poly)
{
	long n;
	int first = 1, i, steps = 4;
	double a, sa, da, step, area = 0, r, x1, y1, lx, ly;
	csch_coutline_t *o;

	for(n = 0, o = poly->outline.array; n < poly->outline.used; n++, o++) {
		switch(o->hdr.type) {
			case CSCH_CTYPE_LINE:
				poly_area_next((double)o->line.inst.c.p1.x, (double)o->line.inst.c.p1.y);
				poly_area_next((double)o->line.inst.c.p2.x, (double)o->line.inst.c.p2.y);
				break;
			case CSCH_CTYPE_ARC:
				sa = o->arc.inst.c.start;
				da = o->arc.inst.c.delta;
				r = o->arc.inst.c.r;
				step = da/steps;
				for(a = sa, i = 0; i < steps; a += step, i++) {
					double x, y;
					x = (double)o->arc.inst.c.c.x + r * cos(a);
					y = (double)o->arc.inst.c.c.y + r * sin(a);
					poly_area_next(x, y);
				}
				break;
			default: break;
		}
	}
	if (!first)
		poly_area_next(x1, y1);

	/* Unlikely: polygon specified in the wrong direction */
	if (area < 0)
		area = -area;

	poly->area = area;
}
