 /*
  *                            COPYRIGHT
  *
  *  pcb-rnd, interactive printed circuit board design - chart block graphic
  *  Copyright (C) 2024 Tibor 'Igor2' Palinkas
  *
  *  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/pcb-rnd
  *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
  *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
  */

#include <gengeo2d/xform.h>
#include <libcschem/operation.h>
#include <libcschem/cnc_text.h>
#include <libcschem/cnc_any_obj.h>
#include <librnd/hid/hid_dad.h>

static void poly_new_line(csch_cpoly_t *poly, rnd_coord_t x1, rnd_coord_t y1, rnd_coord_t x2, rnd_coord_t y2)
{
	csch_coutline_t *dst = csch_vtcoutline_alloc_append(&poly->outline, 1);
	csch_line_t *line = &dst->line;

	line->hdr.type = CSCH_CTYPE_LINE;
	line->spec.p1.x = x1; line->spec.p1.y = y1;
	line->spec.p2.x = x2; line->spec.p2.y = y2;
}

/* edit object: a single text object (find the first one) */
static csch_text_t *get_edit(csch_cgrp_t *grp)
{
	htip_entry_t *e;

	for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e)) {
		csch_text_t *txt = (csch_text_t *)e->value;
		if (txt->hdr.type == CSCH_CTYPE_TEXT)
			return txt;
	}

	return NULL;
}

static const char *shapes[] = {"rectangle",   "diamond",    "oval",    NULL};
typedef enum shape_e          {SHP_RECTANGLE, SHP_DIAMOND,  SHP_OVAL} shape_t;

/* The group's coordinate is the center of the block. Graphics are drawn in
   relative coordinate system and text is moved so text bbox center matches
   block center. */
static void chart_block_update(csch_cgrp_t *grp)
{
	csch_sheet_t *sheet = grp->hdr.sheet;
	csch_cgrp_t *gfx = csch_extobj_gfx_clear(sheet, grp);
	csch_text_t *txt = get_edit(grp);
	csch_cpoly_t *poly;
	rnd_coord_t tcx, tcy, dx, dy, mx, my;
	rnd_coord_t w, h, rx, ry; /* rounded up to n*1000 grid */
	const char *sshape;
	shape_t shape = SHP_RECTANGLE;

	if (txt == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't update chart-block extended object without a text child\n");
		return;
	}

	csch_text_update(sheet, txt, 1);
	poly = csch_cpoly_alloc(sheet, gfx, csch_oid_new(sheet, gfx));
	poly->hdr.stroke_name = csch_comm_str(sheet, "sheet-decor", 1);
	poly->hdr.fill_name = csch_comm_str(sheet, "sheet-decor-fill", 1);
	poly->has_stroke = poly->has_fill = 1;

	w = ((txt->hdr.bbox.x2 - txt->hdr.bbox.x1) + 1500) / 1000 * 1000;
	h = ((txt->hdr.bbox.y2 - txt->hdr.bbox.y1) + 500) / 1000 * 1000;
	rx = w >> 1;
	ry = h >> 1;

	/* figure shape */
	sshape = csch_attrib_get_str(&grp->attr, "chart/shape");
	if (sshape != NULL) {
		int n;
		for(n = 0; shapes[n] != NULL; n++) {
			if (strcmp(shapes[n], sshape) == 0) {
				shape = n;
				break;
			}
		}
	}

	/* draw shape */
	switch(shape) {
		case SHP_RECTANGLE:
			poly_new_line(poly, -rx, -ry, +rx, -ry);
			poly_new_line(poly, +rx, -ry, +rx, +ry);
			poly_new_line(poly, +rx, +ry, -rx, +ry);
			poly_new_line(poly, -rx, +ry, -rx, -ry);
			break;

		case SHP_DIAMOND:
			rx *= 2; ry *= 2;
			poly_new_line(poly, 0, +ry, +rx, 0);
			poly_new_line(poly, +rx, 0, 0, -ry);
			poly_new_line(poly, 0, -ry, -rx, 0);
			poly_new_line(poly, -rx, 0, 0, +ry);
			break;

		case SHP_OVAL:
			{
				double a;
				rnd_coord_t x, y, lx, ly, fx, fy;

				rx *= 1.5; ry *= 1.5;
				for(a = 0; a < 2*M_PI; a += 0.2) {
					x = cos(a) * rx;
					y = sin(a) * ry;
					if (a == 0) {
						fx = x;
						fy = y;
					}
					else
						poly_new_line(poly, lx, ly, x, y);
					lx = x;
					ly = y;
				}
				poly_new_line(poly, lx, ly, fx, fy);
			}
			break;
	}

	/* center text */
	tcx = (txt->hdr.bbox.x2 + txt->hdr.bbox.x1)/2;
	tcy = (txt->hdr.bbox.y2 + txt->hdr.bbox.y1)/2;
	mx = tcx - grp->x; my = tcy - grp->y;
	if ((mx != 0) || (my != 0))
		csch_move(sheet, &txt->hdr, -mx, -my, 1);

	csch_cgrp_update(sheet, grp, 1);
}

static void chart_block_attr_edit_post(csch_cgrp_t *grp, const char *key)
{
	int changed = 0;
	rnd_trace("chart block post '%s'\n", key);

	if (key == NULL)
		return;

	switch(*key) {
		case 's': changed = (strcmp(key, "shape") == 0); break;
	}

	if (changed)
		chart_block_update(grp);
}

static void chart_block_conv_from(vtp0_t *objs)
{
	long n;
	csch_text_t *txt = NULL, *newt;
	csch_coord_t cx, cy;
	csch_cgrp_t *exto;
	csch_sheet_t *sheet;
	csch_source_arg_t *src;

	for(n = objs->used-1; n >= 0; n--) {
		csch_chdr_t *cobj = objs->array[n];
		if (cobj->type == CSCH_CTYPE_TEXT) {
			txt = (csch_text_t *)cobj;
			vtp0_remove(objs, n, 1);
			break;
		}
	}

	if (txt == NULL)
		return;

	sheet = txt->hdr.sheet;
	uundo_freeze_serial(&sheet->undo);

	exto = (csch_cgrp_t *)csch_op_create(sheet, &sheet->direct, CSCH_CTYPE_GRP);

	newt = (csch_text_t *)csch_cobj_dup(sheet, exto, &txt->hdr, 0, 0);
	newt->spec1.x = newt->spec1.y = 0;
	csch_text_dyntext_render(newt);
	newt->bbox_calced = 0; /* allow update() to recompute text bbox */

	cx = ((txt->hdr.bbox.x2 + txt->hdr.bbox.x1)/2000 * 1000);
	cy = ((txt->hdr.bbox.y2 + txt->hdr.bbox.y1)/2000 * 1000);

	exto->x = cx;
	exto->y = cy;

	src = csch_attrib_src_c(NULL, 0, 0, NULL);
	csch_attrib_set(&exto->attr, 0, "extobj", "chart-block", src, NULL);
	exto->extobj = csch_extobj_lookup("chart-block");

	csch_cgrp_update(sheet, exto, 1); /* get txt coords updated for the new group origin */

	chart_block_update(exto);
	csch_op_remove(sheet, &txt->hdr);

	uundo_unfreeze_serial(&sheet->undo);
	uundo_inc_serial(&sheet->undo);

}


static void chart_block_gui_edit_dlg(csch_cgrp_t *grp)
{
	csch_sheet_t *sheet = grp->hdr.sheet;
	csch_text_t *editobj = get_edit(grp);
	int n, resi, wstr, wshape, oldshape = -1;

	const char *oss, **sn;
	rnd_hid_dad_buttons_t clbtn[] = {{"Cancel", -1}, {"Ok", 0}, {NULL, 0}};
	RND_DAD_DECL(dlg);

	if (editobj == NULL) {
		rnd_message(RND_MSG_ERROR, "Can't edi chart block extended object: there is no text object in this block\n");
		return;
	}

	oss = csch_attrib_get_str(&grp->attr, "chart/shape");
	if (oss != NULL) {
		for(sn = shapes, n = 0; *sn != NULL; sn++,n++) {
			if (strcmp(*sn, oss) == 0) {
				oldshape = n;
				break;
			}
		}
	}
	if (oldshape < 0)
		oldshape = 0; /* default: rectangle */

	RND_DAD_BEGIN_VBOX(dlg);
		RND_DAD_COMPFLAG(dlg, RND_HATF_EXPFILL);
		RND_DAD_BEGIN_HBOX(dlg);
			RND_DAD_LABEL(dlg, "Text string:");
			RND_DAD_STRING(dlg);
				wstr = RND_DAD_CURRENT(dlg);
				RND_DAD_DEFAULT_PTR(dlg, rnd_strdup(editobj->text));
		RND_DAD_END(dlg);
		RND_DAD_BEGIN_HBOX(dlg);
			RND_DAD_LABEL(dlg, "Shape:");
			RND_DAD_ENUM(dlg, shapes);
				wshape = RND_DAD_CURRENT(dlg);
				RND_DAD_DEFAULT_NUM(dlg, oldshape);
		RND_DAD_END(dlg);
		RND_DAD_BUTTON_CLOSES(dlg, clbtn);
	RND_DAD_END(dlg);

	RND_DAD_NEW("extobj_chart_block", dlg, "Edit chart-block extended object", NULL, rnd_true, NULL);
	resi = RND_DAD_RUN(dlg);
	if (resi == 0) {
		const char *newstr = dlg[wstr].val.str, *newshape = shapes[dlg[wshape].val.lng];
		int need_update = 0;

		/* make object/attrib modifications */
		uundo_freeze_serial(&sheet->undo);
		if (strcmp(newstr, editobj->text) != 0) {
			csch_text_modify(sheet, editobj, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &newstr, NULL, NULL, 1, 0);
			csch_text_dyntext_render(editobj);
			editobj->bbox_calced = 0; /* allow update() to recompute text bbox */
			need_update = 1;
		}
		if ((oss == NULL) || (strcmp(oss, newshape) != 0)) {
			csch_source_arg_t *src = csch_attrib_src_c(NULL, 0, 0, NULL);
			csch_attr_modify_str(sheet, grp, CSCH_ATP_USER_DEFAULT, "chart/shape", newshape, src, 1);
			need_update = 1;
		}

		/* update the extended object */
		if (need_update) {
			chart_block_update(grp);
			uundo_unfreeze_serial(&sheet->undo);
			uundo_inc_serial(&sheet->undo);
		}
		else
			uundo_unfreeze_serial(&sheet->undo);
	}
	RND_DAD_FREE(dlg);
}

