/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2018,2023 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 <stdlib.h>
#include <ctype.h>
#include <genht/hash.h>
#include <genht/ht_utils.h>
#include <librnd/core/compat_misc.h>
#include <assert.h>
#include "concrete.h"
#include "event.h"
#include "cnc_obj.h"
#include "cnc_grp.h"
#include "non_graphical.h"
#include "project.h"
#include "plug_library.h"

static const char *dsply_names[CSCH_DSPLY_max] = {
	"background",
	"wire",
	"bus",
	"symbol decoration",
	"hub & terminal",
	"sheet decoration",
	"symbol",
	"connections",
	"symbol meta",
	"text meta"
};

int csch_layer_vis[CSCH_DSPLY_max] = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0};
int csch_export_layer_vis[CSCH_DSPLY_max] = {1, 1, 1, 1, 1, 1, 1, 0, 0, 0};

const char *csch_dsply_name(csch_displayer_t dsply)
{
	if ((dsply < 0) || (dsply >= CSCH_DSPLY_max))
		return "<invalid>";
	return dsply_names[dsply];
}

csch_rtree_t *csch_rtree_lookup_by_name(csch_sheet_t *sheet, const char *name)
{
	int n;

	for(n = 0; n < CSCH_DSPLY_max; n++) {
		int len;

		len = strlen(dsply_names[n]);
		if (strncmp(name, dsply_names[n], len) == 0) {
			name += len;
			goto found;
		}
	}
	return NULL;

	found:;
	while(isspace(*name) || (*name == '-') || (*name == '_') || (*name == '/')) name++;
	if (*name == '\0') return &sheet->dsply[n];
	if (strcmp(name, "fill") == 0) return &sheet->dsply_fill[n];
	if (strcmp(name, "stroke") == 0) return &sheet->dsply[n];
	return NULL;
}


static const char *ctype_names[CSCH_CTYPE_max] = { "invalid", "line", "arc", "polygon", "text", "bitmap", "connection", "grp", "grp_ref", "pen" };
const char *csch_ctype_name(csch_ctype_t typ)
{
	if ((typ < 0) || (typ >= CSCH_CTYPE_max))
		return "<invalid>";
	return ctype_names[typ];
}

static long next_uid;

csch_sheet_t *csch_sheet_init(csch_sheet_t *sheet, csch_project_t *parent)
{
	int n;

	if (sheet == NULL)
		return NULL;

	if (parent != NULL)
		rnd_project_append_design(&parent->hdr, &sheet->hidlib);

	sheet->uid = next_uid++;
	htsp_init(&sheet->comm_str, strhash, strkeyeq);

	sheet->util_wirenet.recalc_inhibit = 0;
	htpi_init(&sheet->util_wirenet.recalc_wn, ptrhash, ptrkeyeq);

	for(n = 0; n < CSCH_DSPLY_max; n++) {
		csch_rtree_init(&sheet->dsply[n]);
		csch_rtree_init(&sheet->dsply_fill[n]);
	}

	csch_cgrp_init(sheet, &sheet->direct, NULL, CSCH_TOP_OID_DIRECT);
	csch_cgrp_init(sheet, &sheet->indirect, NULL, CSCH_TOP_OID_INDIRECT);

	csch_bbox_invalidate(&sheet->bbox);
	sheet->junction_pen_name = csch_comm_str(sheet, "junction", 1);

	uundo_list_init(&sheet->undo);

	return sheet;
}

csch_sheet_t *csch_sheet_alloc(csch_project_t *parent)
{

	return csch_sheet_init(calloc(sizeof(csch_sheet_t), 1), parent);
}

void csch_sheet_uninit(csch_sheet_t *sheet)
{
	int n;

	if (sheet->non_graphical && (sheet->non_graphical_impl != NULL) && (sheet->non_graphical_impl->free_sheet != NULL)) {
		sheet->non_graphical_impl->free_sheet(sheet);
		sheet->non_graphical_data = NULL;
		sheet->non_graphical_impl = NULL;
	}

	htpi_uninit(&sheet->util_wirenet.recalc_wn);

	csch_lib_free_sheet_local_libs(sheet);
	csch_lib_free_sheet_libs(sheet);

	uundo_list_uninit(&sheet->undo);

	csch_cgrp_uninit(&sheet->direct);
	csch_cgrp_uninit(&sheet->indirect);

	TODO("free all objects in the hash");
	for(n = 0; n < CSCH_DSPLY_max; n++) {
		csch_rtree_uninit(&sheet->dsply[n]);
		csch_rtree_uninit(&sheet->dsply_fill[n]);
	}

	genht_uninit_deep(htsp, &sheet->comm_str, {
		free(htent->key);
	});

	free(sheet->newname);
	sheet->newname = NULL;
	free(sheet->hidlib.loadname);
	sheet->hidlib.loadname = NULL;
	free(sheet->hidlib.fullpath);
	sheet->hidlib.fullpath = NULL;
	free(sheet->loadfmt);
	sheet->loadfmt = NULL;
	free(sheet->design_dir);
	sheet->design_dir = NULL;

}

void csch_sheet_free(csch_sheet_t *sheet)
{
	rnd_event(&sheet->hidlib, CSCH_EVENT_SHEET_PREUNLOAD, NULL);
	if (sheet->hidlib.project != NULL)
		rnd_project_remove_design(sheet->hidlib.project, &sheet->hidlib);
	if (sheet->hidlib.link.parent != NULL) /* NULL for buffers */
		rnd_conf_state_del_design(&sheet->hidlib); /* also removes curr from rnd_designs */
	csch_sheet_uninit(sheet);
	free(sheet);
}

void csch_sheet_bbox_update(csch_sheet_t *sheet)
{
	csch_cgrp_bbox_update(sheet, &sheet->direct);
	sheet->bbox = sheet->direct.bbox_flt;
}

csch_oid_t csch_oid_new(csch_sheet_t *sheet, csch_cgrp_t *grp)
{
	if (grp->data.grp.next_id == 0)
		grp->data.grp.next_id = 1;

	assert(grp->hdr.type == CSCH_CTYPE_GRP);
	assert(grp->data.grp.next_id < CSCH_MAX_OID);
	return grp->data.grp.next_id++;
}

void csch_chdr_copy_meta4dup(csch_chdr_t *dst, const csch_chdr_t *src)
{
	if (dst->sheet != src->sheet) {
		/* cross-sheet copy: need to use dst sheet's pen name hash */
		dst->stroke_name = csch_comm_str(dst->sheet, src->stroke_name.str, 1);
		dst->fill_name = csch_comm_str(dst->sheet, src->fill_name.str, 1);
	}
	else {
		/* cheap: copy within a sheet means names are from the same hash */
		dst->stroke_name = src->stroke_name;
		dst->fill_name = src->fill_name;
	}

	dst->dsply = src->dsply;
	dst->lock = src->lock;
	dst->floater = src->floater;
}

char *csch_chdr_to_oidpath_str(const csch_chdr_t *chdr)
{
	csch_oidpath_t oidp;
	char *res;

	memset(&oidp, 0, sizeof(oidp));
	csch_oidpath_from_obj(&oidp, chdr);
	res = csch_oidpath_to_str(&oidp);
	csch_oidpath_free(&oidp);
	return res;
}

csch_comm_str_t csch_comm_str(csch_sheet_t *sheet, const char *str, int alloc)
{
	csch_comm_str_t res;

	if (str != NULL) {
		htsp_entry_t *e = htsp_getentry(&sheet->comm_str, str);
		if (e != NULL) {
			res.str = e->key;
			return res;
		}

		if (alloc) {
			res.str = rnd_strdup(str);
			htsp_set(&sheet->comm_str, (char *)res.str, NULL);
			return res;
		}
	}

	/* allocate the standard NULL string for "not found but not allocated" */
	res.str = NULL;
	return res;
}

void csch_sheet_set_changed(csch_sheet_t *sheet, int new_val)
{
	int old = sheet->changed;

	sheet->changed = new_val;

	if (old != new_val)
		rnd_event(&sheet->hidlib, RND_EVENT_DESIGN_META_CHANGED, NULL);

	if (new_val)
		rnd_event(&sheet->hidlib, CSCH_EVENT_SHEET_EDITED, NULL);
}
