/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2019 Tibor 'Igor2' Palinkas
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.*
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; 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 "config.h"

#include <assert.h>
#include <genht/htsp.h>
#include <librnd/core/misc_util.h>
#include <librnd/core/rnd_printf.h>

#include "cnc_any_obj.h"

#include "event.h"
#include "concrete.h"
#include "cnc_arc.h"
#include "cnc_bitmap.h"
#include "cnc_conn.h"
#include "cnc_grp.h"
#include "cnc_line.h"
#include "cnc_text.h"
#include "cnc_obj.h"
#include "cnc_pen.h"
#include "cnc_poly.h"
#include "undo.h"

void csch_cobj_free(csch_chdr_t *obj)
{
	switch(obj->type) {
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:  break;
		case CSCH_CTYPE_LINE:     csch_line_free((csch_line_t *)obj); break;
		case CSCH_CTYPE_ARC:      csch_arc_free((csch_arc_t *)obj); break;
		case CSCH_CTYPE_POLY:     csch_cpoly_free((csch_cpoly_t *)obj); break;
		case CSCH_CTYPE_TEXT:     csch_text_free((csch_text_t *)obj); break;
		case CSCH_CTYPE_BITMAP:   csch_cbitmap_free((csch_cbitmap_t *)obj); break;
		case CSCH_CTYPE_CONN:     csch_conn_free((csch_conn_t *)obj); break;
		case CSCH_CTYPE_GRP:      csch_cgrp_free((csch_cgrp_t *)obj); break;
		case CSCH_CTYPE_GRP_REF:  csch_cgrp_ref_free((csch_cgrp_t *)obj); break;
		case CSCH_CTYPE_PEN:      csch_pen_free((csch_cpen_t *)obj); break;
	}
}


csch_chdr_t *csch_cobj_dup(csch_sheet_t *sheet, csch_cgrp_t *grp, const csch_chdr_t *src, int keep_id, int inst2spec)
{
	void *dst;

	switch(src->type) {
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:  return NULL;
		case CSCH_CTYPE_LINE:     dst = csch_line_dup(sheet, grp, (const csch_line_t *)src, keep_id, inst2spec); break;
		case CSCH_CTYPE_ARC:      dst = csch_arc_dup(sheet, grp, (const csch_arc_t *)src, keep_id); break;
		case CSCH_CTYPE_POLY:     dst = csch_cpoly_dup(sheet, grp, (const csch_cpoly_t *)src, keep_id); break;
		case CSCH_CTYPE_TEXT:     dst = csch_text_dup(sheet, grp, (const csch_text_t *)src, keep_id, inst2spec); break;
		case CSCH_CTYPE_BITMAP:   dst = csch_cbitmap_dup(sheet, grp, (const csch_cbitmap_t *)src, keep_id); break;
		case CSCH_CTYPE_CONN:     abort();
		case CSCH_CTYPE_GRP:
		case CSCH_CTYPE_GRP_REF:  dst = csch_cgrp_dup(sheet, grp, (const csch_cgrp_t *)src, keep_id); break;
		case CSCH_CTYPE_PEN:      dst = csch_pen_dup(sheet, grp, (const csch_cpen_t *)src); break; /* id is name and is always kept */
	}
	return dst;
}

void csch_cobj_update(csch_sheet_t *sheet, csch_chdr_t *src, int do_xform)
{
	switch(src->type) {
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:  abort();
		case CSCH_CTYPE_LINE:     csch_line_update(sheet, (csch_line_t *)src, do_xform); break;
		case CSCH_CTYPE_ARC:      csch_arc_update(sheet, (csch_arc_t *)src, do_xform); break;
		case CSCH_CTYPE_POLY:     csch_poly_update(sheet, (csch_cpoly_t *)src, do_xform); break;
		case CSCH_CTYPE_TEXT:     csch_text_update(sheet, (csch_text_t *)src, do_xform); break;
		case CSCH_CTYPE_BITMAP:   abort();
		case CSCH_CTYPE_CONN:     csch_conn_update(sheet, (csch_conn_t *)src, do_xform); break;
		case CSCH_CTYPE_GRP:      csch_cgrp_update(sheet, (csch_cgrp_t *)src, do_xform); break;
		case CSCH_CTYPE_GRP_REF:  csch_cgrp_ref_update(sheet, (csch_cgrp_t *)src, do_xform); break;
		case CSCH_CTYPE_PEN:      /* nop */ break;
	}
}

int csch_cobj_attrib_set(csch_sheet_t *sheet, csch_cgrp_t *obj, int prio, const char *key, const char *val, csch_source_arg_t *source)
{
	csch_attrib_t *a;
	int res;

	assert(csch_obj_is_grp(&obj->hdr));

	res = csch_attrib_set(&obj->attr, prio, key, val, source, &a);
	if (res == 0) /* attribute value changed */
		csch_cgrp_attrib_update(sheet, obj, prio, a->key, a->val);
	return res;
}

int csch_cobj_attrib_seti(csch_sheet_t *sheet, csch_cgrp_t *obj, int prio, const char *key, long idx, const char *val, csch_source_arg_t *source)
{
	TODO("call csch_cobj_attrib_update() with idx");
	assert(csch_obj_is_grp(&obj->hdr));
	return csch_attrib_seti(&obj->attr, prio, key, idx, val, source, NULL);
}


void csch_cobj_attrib_copy_all(csch_sheet_t *sheet, csch_cgrp_t *dst, const csch_cgrp_t *src)
{
	csch_attrib_t *a, *b;
	htsp_entry_t *e;

	assert(csch_obj_is_grp(&dst->hdr) && csch_obj_is_grp(&src->hdr));

	for(e = htsp_first(&src->attr); e; e = htsp_next(&src->attr, e)) {
		a = e->value;
		b = csch_attrib_dup(a);
		htsp_set(&dst->attr, b->key, b);
		csch_cgrp_attrib_update(sheet, dst, b->prio, b->key, b->val);
	}
}

unsigned csch_cobj_hash_(csch_chdr_t *obj, csch_hash_ignore_t ignore)
{
	switch(obj->type) {
		case CSCH_CTYPE_LINE:     return csch_line_hash((csch_line_t *)obj, ignore);
		case CSCH_CTYPE_ARC:      return csch_arc_hash((csch_arc_t *)obj, ignore);
		case CSCH_CTYPE_POLY:     return csch_cpoly_hash((csch_cpoly_t *)obj, ignore);
		case CSCH_CTYPE_TEXT:     return csch_text_hash((csch_text_t *)obj, ignore);
		case CSCH_CTYPE_BITMAP:   /*return csch_bitmap_hash((csch_bitmap_t *)obj, ignore);*/
		case CSCH_CTYPE_CONN:     /*return csch_conn_hash((csch_conn_t *)obj, ignore);*/
		case CSCH_CTYPE_GRP:
		case CSCH_CTYPE_GRP_REF:  return csch_cgrp_hash_((csch_cgrp_t *)obj, ignore);
		case CSCH_CTYPE_PEN:      return csch_pen_hash((csch_cpen_t *)obj, ignore);
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:
			break;
	}
	return 0;
}

unsigned csch_cobj_hash(csch_chdr_t *obj)
{
	return csch_cobj_hash_(obj, 0);
}


int csch_cobj_keyeq_(csch_chdr_t *obj1, csch_chdr_t *obj2, csch_hash_ignore_t ignore)
{
	if (obj1->type != obj2->type) return 0;
	switch(obj1->type) {
		case CSCH_CTYPE_LINE:     return csch_line_keyeq((csch_line_t *)obj1, (csch_line_t *)obj2, ignore);
		case CSCH_CTYPE_ARC:      return csch_arc_keyeq((csch_arc_t *)obj1, (csch_arc_t *)obj2, ignore);
		case CSCH_CTYPE_POLY:     return csch_cpoly_keyeq((csch_cpoly_t *)obj1, (csch_cpoly_t *)obj2, ignore);
		case CSCH_CTYPE_TEXT:     return csch_text_keyeq((csch_text_t *)obj1, (csch_text_t *)obj2, ignore);
		case CSCH_CTYPE_BITMAP:   /*return csch_bitmap_keyeq((csch_bitmap_t *)obj1, (csch_bitmap_t *)obj2, ignore);*/
		case CSCH_CTYPE_CONN:     /*return csch_conn_keyeq((csch_conn_t *)obj1, (csch_conn_t *)obj2, ignore);*/
		case CSCH_CTYPE_GRP:
		case CSCH_CTYPE_GRP_REF:  return csch_cgrp_keyeq_((csch_cgrp_t *)obj1, (csch_cgrp_t *)obj2, ignore);
		case CSCH_CTYPE_PEN:      return csch_pen_keyeq((csch_cpen_t *)obj1, (csch_cpen_t *)obj2, ignore);
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:
			break;
	}
	return 0;
}

int csch_cobj_keyeq(csch_chdr_t *obj1, csch_chdr_t *obj2)
{
	return csch_cobj_keyeq_(obj1, obj2, 0);
}


int csch_cobj_get_endxy(const csch_chdr_t *src, int side, csch_coord_t *x, csch_coord_t *y)
{
	switch(src->type) {
		case CSCH_CTYPE_LINE:     return csch_line_get_endxy((csch_line_t *)src, side, x, y);
		case CSCH_CTYPE_ARC:      return csch_arc_get_endxy((csch_arc_t *)src, side, x, y);
		case CSCH_CTYPE_POLY:
		case CSCH_CTYPE_TEXT:
		case CSCH_CTYPE_BITMAP:
		case CSCH_CTYPE_CONN:
		case CSCH_CTYPE_GRP:
		case CSCH_CTYPE_GRP_REF:
		case CSCH_CTYPE_PEN:
		case CSCH_CTYPE_max:
		case CSCH_CTYPE_invalid:
			break;
	}
	return -1;
}


static const char core_any_obj_cookie[] = "libcschem/core/cnc_any_obj.c";

/*** undoable pen name mod ***/
typedef struct {
	csch_chdr_t *obj;
	csch_comm_str_t stroke_name;
	csch_comm_str_t fill_name;
} undo_pen_name_modify_t;

static int undo_pen_name_modify_swap(void *udata)
{
	undo_pen_name_modify_t *u = udata;

	rnd_swap(csch_comm_str_t, u->stroke_name, u->obj->stroke_name);
	rnd_swap(csch_comm_str_t, u->fill_name, u->obj->fill_name);
	u->obj->stroke = NULL;
	u->obj->fill = NULL;
	csch_cobj_update_pen(u->obj);

	return 0;
}

static void undo_pen_name_modify_print(void *udata, char *dst, size_t dst_len)
{
	undo_pen_name_modify_t *u = udata;
	rnd_snprintf(dst, dst_len, "obj pen name change: stroke: %s <--> %s  fill: %s <--> %s", u->obj->stroke_name, u->stroke_name, u->obj->fill_name, u->fill_name);
}

static const uundo_oper_t undo_pen_name_modify = {
	core_any_obj_cookie,
	NULL,
	undo_pen_name_modify_swap,
	undo_pen_name_modify_swap,
	undo_pen_name_modify_print
};



void csch_chdr_pen_name_modify(csch_sheet_t *sheet, csch_chdr_t *obj, csch_comm_str_t *stroke_name, csch_comm_str_t *fill_name, int undoable)
{
	undo_pen_name_modify_t utmp, *u = &utmp;

	if (undoable) u = uundo_append(&sheet->undo, &undo_pen_name_modify, sizeof(undo_pen_name_modify_t));

	u->obj = obj;
	u->stroke_name = stroke_name == NULL ? obj->stroke_name : *stroke_name;
	u->fill_name = fill_name == NULL ? obj->fill_name : *fill_name;

	undo_pen_name_modify_swap(u);
	if (undoable) csch_undo_inc_serial(sheet);
}


RND_INLINE void select_side_effects(csch_sheet_t *sheet, csch_chdr_t *obj, int root)
{
	if (obj->type == CSCH_CTYPE_TEXT)
		csch_text_invalidate_font((csch_text_t *)obj); /* may need to re-render for indicate selection */
	if (csch_obj_is_grp(obj)) {
		csch_cgrp_t *grp = (csch_cgrp_t *)obj;
		htip_entry_t *e;
		for(e = htip_first(&grp->id2obj); e != NULL; e = htip_next(&grp->id2obj, e))
			select_side_effects(sheet, e->value, 0);
	}
	if (root)
		csch_cobj_redraw(obj);
}

void csch_cobj_select(csch_sheet_t *sheet, csch_chdr_t *obj)
{
	if (!obj->selected) {
		obj->selected = 1;
		select_side_effects(sheet, obj, 1);
	}
}


void csch_cobj_unselect(csch_sheet_t *sheet, csch_chdr_t *obj)
{
	if (obj->selected) {
		obj->selected = 0;
		select_side_effects(sheet, obj, 1);
	}
}

int csch_cobj_is_direct(const csch_chdr_t *obj)
{
	csch_cgrp_t *g, *direct = &obj->sheet->direct;
	
	for(g = obj->parent; g != NULL; g = g->hdr.parent)
		if (g == direct)
			return 1;

	return 0;
}

int csch_cobj_is_indirect(const csch_chdr_t *obj)
{
	csch_cgrp_t *g, *indirect = &obj->sheet->indirect;
	
	for(g = obj->parent; g != NULL; g = g->hdr.parent)
		if (g == indirect)
			return 1;

	return 0;
}

