/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - sch-rnd (executable)
 *  Copyright (C) 2022 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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
 */

#include <librnd/hid/hid.h>
#include <librnd/hid/hid_inlines.h>
#include <librnd/core/color.h>
#include <librnd/core/vtc0.h>

#include <libcschem/concrete.h>
#include <libcschem/cnc_line.h>
#include <libcschem/cnc_poly.h>
#include <libcschem/cnc_text.h>
#include <libcschem/cnc_arc.h>
#include <libcschem/cnc_pen.h>

#include "draw.h"
#include "draw_xor.h"
#include "font.h"

typedef struct {
	csch_sheet_t *sheet;
	rnd_hid_gc_t gc;
	csch_coord_t dx, dy;
	unsigned thin:1;   /* centerline */
	unsigned wire:1;   /* wireframe */
	unsigned no_cap:1; /* omit end caps in wireframe */
	unsigned backref:1;/* floaters draw a line back to their original position */
} xor_ctx_t;

RND_INLINE void draw_xor_line(xor_ctx_t *ctx, csch_line_t *l, csch_cpen_t *stroke)
{
	if (ctx->thin || ctx->wire)
		rnd_render->draw_line(ctx->gc, C2P(l->inst.c.p1.x + ctx->dx), C2P(l->inst.c.p1.y + ctx->dy), C2P(l->inst.c.p2.x + ctx->dx), C2P(l->inst.c.p2.y + ctx->dy));
}

RND_INLINE void draw_xor_arc(xor_ctx_t *ctx, csch_arc_t *a, csch_cpen_t *stroke)
{
	if (ctx->thin || ctx->wire)
		rnd_render->draw_arc(ctx->gc, C2P(a->inst.c.c.x + ctx->dx), C2P(a->inst.c.c.y + ctx->dy), C2P(a->inst.c.r), C2P(a->inst.c.r), (180.0 - a->inst.c.start * RND_RAD_TO_DEG), -(a->inst.c.delta * RND_RAD_TO_DEG));
}

RND_INLINE void draw_xor_text(xor_ctx_t *ctx, csch_text_t *t, csch_cpen_t *stroke)
{
	if (ctx->thin || ctx->wire) {
		/* if there's no spec2 calculated for a non-bbox-specified text, need to update it to get real dimensions */
		if (!t->has_bbox && (t->spec2.x == 0) && (t->spec2.y == 0))
			sch_rnd_font_text_update(t->hdr.sheet, t, csch_stroke_(&t->hdr));

		rnd_render->draw_line(ctx->gc, C2P(t->inst1.x + ctx->dx), C2P(t->inst1.y + ctx->dy), C2P(t->inst2.x + ctx->dx), C2P(t->inst2.y + ctx->dy));
		rnd_render->draw_line(ctx->gc, C2P(t->inst15.x + ctx->dx), C2P(t->inst15.y + ctx->dy), C2P(t->inst25.x + ctx->dx), C2P(t->inst25.y + ctx->dy));
		rnd_render->draw_line(ctx->gc, C2P(t->inst1.x + ctx->dx), C2P(t->inst1.y + ctx->dy), C2P(t->inst15.x + ctx->dx), C2P(t->inst15.y + ctx->dy));
		rnd_render->draw_line(ctx->gc, C2P(t->inst1.x + ctx->dx), C2P(t->inst1.y + ctx->dy), C2P(t->inst25.x + ctx->dx), C2P(t->inst25.y + ctx->dy));
		rnd_render->draw_line(ctx->gc, C2P(t->inst2.x + ctx->dx), C2P(t->inst2.y + ctx->dy), C2P(t->inst15.x + ctx->dx), C2P(t->inst15.y + ctx->dy));
		rnd_render->draw_line(ctx->gc, C2P(t->inst2.x + ctx->dx), C2P(t->inst2.y + ctx->dy), C2P(t->inst25.x + ctx->dx), C2P(t->inst25.y + ctx->dy));
	}
}

RND_INLINE void draw_xor_poly(xor_ctx_t *ctx, csch_cpoly_t *p, csch_cpen_t *stroke)
{
	int save;
	long n;
	csch_coutline_t *o;

	save = ctx->no_cap;
	ctx->no_cap = 1;
	for(n = 0, o = p->outline.array; n < p->outline.used; n++, o++) {
		switch(o->hdr.type) {
			case CSCH_CTYPE_LINE: draw_xor_line(ctx, &o->line, stroke); break;
			case CSCH_CTYPE_ARC:  draw_xor_arc(ctx, &o->arc, stroke); break;
			default: /* invalid, do not draw */ break;
		}
	}
	ctx->no_cap = save;
}

static void sch_rnd_xor_draw_obj_(xor_ctx_t *ctx, csch_chdr_t *obj);

RND_INLINE void draw_xor_grp(xor_ctx_t *ctx, csch_cgrp_t *grp)
{
	htip_entry_t *e;
	for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e))
		sch_rnd_xor_draw_obj_(ctx, (csch_chdr_t *)e->value);
}

/* Draw a line from the floater object back to its parent group */
static void sch_rnd_xor_draw_obj_backref(xor_ctx_t *ctx, csch_chdr_t *obj)
{
	csch_cgrp_t *grp = obj->parent;
	csch_coord_t ox, oy, px, py;

	ox = (obj->bbox.x1 + obj->bbox.x2) / 2 + ctx->dx;
	oy = (obj->bbox.y1 + obj->bbox.y2) / 2 + ctx->dy;
	px = (grp->hdr.bbox.x1 + grp->hdr.bbox.x2) / 2;
	py = (grp->hdr.bbox.y1 + grp->hdr.bbox.y2) / 2;

	rnd_render->draw_line(ctx->gc, C2P(ox), C2P(oy), C2P(px), C2P(py));
}

static void sch_rnd_xor_draw_obj_(xor_ctx_t *ctx, csch_chdr_t *obj)
{
	switch(obj->type) {
		case CSCH_CTYPE_LINE: draw_xor_line(ctx, (csch_line_t *)obj, csch_stroke_(obj)); break;
		case CSCH_CTYPE_POLY: draw_xor_poly(ctx, (csch_cpoly_t *)obj, csch_stroke_(obj)); break;
		case CSCH_CTYPE_ARC:  draw_xor_arc(ctx, (csch_arc_t *)obj, csch_stroke_(obj)); break;
		case CSCH_CTYPE_TEXT: draw_xor_text(ctx, (csch_text_t *)obj, csch_stroke_(obj)); break;

		case CSCH_CTYPE_GRP:
		case CSCH_CTYPE_GRP_REF:
			draw_xor_grp(ctx, (csch_cgrp_t *)obj); break;

		case CSCH_CTYPE_BITMAP:
			TODO("bitmap: implement xor draw for this object");
			break;
		case CSCH_CTYPE_CONN:
		case CSCH_CTYPE_PEN:
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:
			/* no need to draw these */
			break;
	}

	if (ctx->backref && obj->floater && (obj->parent != NULL) && (obj->parent != &obj->sheet->direct))
		sch_rnd_xor_draw_obj_backref(ctx, obj);
}


void sch_rnd_xor_draw_obj(csch_sheet_t *sheet, csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy, rnd_hid_gc_t gc, int thin, int wireframe)
{
	xor_ctx_t ctx;

	ctx.sheet = sheet;
	ctx.gc = gc;
	ctx.dx = dx;
	ctx.dy = dy;
	ctx.thin = thin;
	ctx.wire = wireframe;
	ctx.no_cap = 0;
	ctx.backref = 1;

	sch_rnd_xor_draw_obj_(&ctx, obj);
}


void sch_rnd_xor_draw_buffer(csch_sheet_t *sheet, csch_sheet_t *buffer, csch_coord_t dx, csch_coord_t dy, rnd_hid_gc_t gc, int thin, int wireframe)
{
	xor_ctx_t ctx;

	ctx.sheet = sheet;
	ctx.gc = gc;
	ctx.dx = dx;
	ctx.dy = dy;
	ctx.thin = thin;
	ctx.wire = wireframe;
	ctx.no_cap = 0;
	ctx.backref = 0;

	draw_xor_grp(&ctx, &buffer->direct);
}

