/*
 *                            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., 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
 */

/* Sheet related app operations */

#include <libcschem/config.h>

#include <libminuid/libminuid.h>

#include <libcschem/concrete.h>
#include <libcschem/project.h>
#include <libcschem/plug_library.h>
#include <libcschem/event.h>
#include <libcschem/libcschem.h>
#include <libcschem/plug_io.h>

#include <librnd/core/conf.h>
#include <librnd/hid/hid_menu.h>
#include <librnd/core/rnd_conf.h>
#include <librnd/core/compat_fs.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/safe_fs.h>
#include <librnd/core/paths.h>

#include "conf_core.h"
#include "util_sheet.h"
#include "draw.h"

static const char sheet_cookie[] = "sch-rnd/sheet.c";

extern const char *default_sheet_internal;

RND_INLINE rnd_coord_t sheet_attr_crd(csch_sheet_t *sheet, const char *key, rnd_coord_t defval)
{
	const char *s = csch_attrib_get_str(&sheet->direct.attr, key);
	double val;
	rnd_bool succ;

	if (s == NULL)
		return defval;

	val = rnd_get_value_ex(s, NULL, NULL, NULL, "nm", &succ);
	if (!succ)
		return defval;

	return val;
}

rnd_coord_t sch_rnd_sheet_attr_crd(csch_sheet_t *sheet, const char *key, rnd_coord_t defval)
{
	return sheet_attr_crd(sheet, key, defval);
}

static void warn_size(csch_sheet_t *sheet, csch_coord_t c)
{
	if (sheet->warned_too_large)
		return;

	if (c > P2C(RND_COORD_MAX)/2) {
		rnd_message(RND_MSG_ERROR, "Sheet is too large. Please consider recompiling librnd with 64 bit coords.\n");
		sheet->warned_too_large = 1;
	}
}

void sch_rnd_sheet_warn_size(csch_sheet_t *sheet, csch_coord_t x1, csch_coord_t y1, csch_coord_t x2, csch_coord_t y2)
{
	if (sheet->warned_too_large)
		return;
	warn_size(sheet, x1); warn_size(sheet, y1);
	warn_size(sheet, x2); warn_size(sheet, y2);
}

void sch_rnd_sheet_recalc_bbox(csch_sheet_t *sheet)
{
		rnd_coord_t min_x = sheet_attr_crd(sheet, "drawing_min_width", 0);
		rnd_coord_t min_y = sheet_attr_crd(sheet, "drawing_min_height", 0);

	csch_sheet_bbox_update(sheet);

	sheet->hidlib.dwg.X1 = C2P(sheet->bbox.x1);
	sheet->hidlib.dwg.Y1 = C2P(sheet->bbox.y1);
	sheet->hidlib.dwg.X2 = C2P(RND_MAX(min_x, sheet->bbox.x2));
	sheet->hidlib.dwg.Y2 = C2P(RND_MAX(min_y, sheet->bbox.y2));

	sch_rnd_sheet_warn_size(sheet, sheet->bbox.x1, sheet->bbox.y1, RND_MAX(min_x, sheet->bbox.x2), RND_MAX(min_y, sheet->bbox.y2));
}

void sch_rnd_sheet_postproc(csch_sheet_t *sheet)
{
	sheet->hidlib.grid = rnd_conf.editor.grid;

	if (!sheet->non_graphical)
		sch_rnd_sheet_recalc_bbox(sheet);

	if (sheet->hidlib.fullpath != NULL) {
		sheet->design_dir = rnd_dirname(sheet->hidlib.fullpath);
		rnd_conf_force_set_str(conf_core.rc.path.design, sheet->design_dir);
	}
	else {
		sheet->design_dir = NULL;
		rnd_conf_force_set_str(conf_core.rc.path.design, "");
	}
	rnd_conf_ro("rc/path/design");

	if (!sheet->non_graphical) {
		/* This works from load only because the conf state is already set up for
		   the new sheet, even tho librnd's current sheet is not yet set */
		csch_lib_add_all(sheet, csch_lib_get_master("symbol", 1), &conf_core.rc.library_search_paths, 0);
		csch_lib_add_all(sheet, csch_lib_get_master("hlibrary", 1), &conf_core.rc.hlibrary_search_paths, 0);
	}

	if (sheet->is_symbol)
		sch_rnd_reconf_for_symbol_as_sheet();
}

static csch_sheet_t *sch_rnd_sheet_new_(csch_project_t *prj, csch_sheet_t *dst_sheet, int emit_ev)
{
	csch_sheet_t *sheet = NULL;
	int dsch = -1;

	rnd_hid_menu_merge_inhibit_inc();
	if (dst_sheet == NULL)
		rnd_conf_list_foreach_path_first(NULL, dsch, &conf_core.rc.default_sheet_file, csch_project_load_sheet(prj, __path__, NULL, &sheet, 1, NULL));
	else
		rnd_conf_list_foreach_path_first(NULL, dsch, &conf_core.rc.default_sheet_file, ((sheet = csch_load_sheet_io(NULL, dst_sheet, __path__, __path__, NULL, 0, NULL)) == NULL));
	rnd_hid_menu_merge_inhibit_dec();

	if (dsch != 0) { /* no default sheet file found, use embedded version */
		FILE *f;
		char *efn;
		const char *tmp_fn = ".sch-rnd.default-sheet.lht";

		/* We can parse from file only, make a temp file */
		f = rnd_fopen_fn(NULL, tmp_fn, "wb", &efn);
		if (f != NULL) {
			fwrite(default_sheet_internal, strlen(default_sheet_internal), 1, f);
			fclose(f);
			if (dst_sheet != NULL) {
				sheet = csch_load_sheet_io(NULL, dst_sheet, efn, efn, NULL, 0, NULL);
				if (sheet != NULL)
					dsch = 0;
				else
					dsch = 1;
			}
			else
				dsch = csch_project_load_sheet(prj, efn, NULL, &sheet, rnd_conf.rc.quiet, NULL);

			if (dsch == 0)
				rnd_message(RND_MSG_WARNING, "Couldn't find default sheet - using the embedded fallback\n");
			else
				rnd_message(RND_MSG_ERROR, "Couldn't find default sheet and failed to load the embedded fallback\n");
			rnd_remove(NULL, efn);
			free(efn);
		}
	}

	/* final fallback: empty sheet */
	if (sheet == NULL) {
		if (dst_sheet != NULL)
			sheet = dst_sheet;
		else
			sheet = csch_sheet_alloc(prj);
	}

	/* do not remember default sheet's file name so that a 'save' won't overwrite it */
	free(sheet->hidlib.fullpath);
	sheet->hidlib.fullpath = NULL;
	sheet->newname = sheet->hidlib.loadname;
	sheet->hidlib.loadname = NULL;
	minuid_gen(&csch_minuid, sheet->direct.uuid);
	minuid_clr(sheet->direct.data.grp.src_uuid);

	if (emit_ev)
		rnd_event(&sheet->hidlib, CSCH_EVENT_SHEET_POSTLOAD, NULL);

	return sheet;
}

csch_sheet_t *sch_rnd_sheet_new(csch_project_t *prj, csch_sheet_t *sheet)
{
	/* we may be called prior to configuration is set up so make sure
	   conf_core.rc.path.* are filled in, so loading the default sheet
	   from $(paths) referred would work */
	if (conf_core.rc.path.prefix == NULL)
		sch_rnd_conf_core_postproc();

	return sch_rnd_sheet_new_(prj, NULL, 0);
}

csch_sheet_t *sch_rnd_sheet_new4revert(csch_sheet_t *sheet)
{
	csch_sheet_t *newsh = sch_rnd_sheet_new_(NULL, sheet, 1);
	if (newsh->hidlib.fullpath == NULL)
		newsh->hidlib.fullpath = rnd_strdup("<none>");
	return newsh;
}


static void sch_rnd_sheet_postload_ev(rnd_design_t *hidlib, void *user_data, int argc, rnd_event_arg_t argv[])
{
	csch_sheet_t *sheet = (csch_sheet_t *)hidlib;

	sch_rnd_sheet_postproc(sheet);
}


void sch_rnd_sheet_change_loadname(rnd_design_t *hidlib, const char *name)
{
	free(hidlib->loadname);
	hidlib->loadname = rnd_strdup(name);
	rnd_event(hidlib, RND_EVENT_DESIGN_FN_CHANGED, NULL);
}

void sch_rnd_sheet_change_fullpath(rnd_design_t *hidlib, const char *path)
{
	free(hidlib->fullpath);
	hidlib->fullpath = rnd_strdup(path);
}


void sch_rnd_sheet_init2(void)
{
	rnd_event_bind(CSCH_EVENT_SHEET_POSTLOAD, sch_rnd_sheet_postload_ev, NULL, sheet_cookie);
}

void sch_rnd_sheet_uninit(void)
{
	rnd_event_unbind_allcookie(sheet_cookie);
}

