/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - standard cschem attributes
 *  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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <libfungw/fungw.h>
#include <libcschem/config.h>
#include <libcschem/abstract.h>
#include <libcschem/concrete.h>
#include <libcschem/engine.h>
#include <libcschem/actions_csch.h>
#include <libcschem/attrib.h>
#include <libcschem/libcschem.h>
#include <libcschem/util_compile.h>
#include <libcschem/hierarchy.h>
#include <librnd/core/actions.h>
#include <librnd/core/compat_misc.h>
#include <librnd/core/rnd_printf.h>
#include <librnd/core/plugins.h>
#include <librnd/core/conf_multi.h>

#include <plugins/sch_dialogs/quick_attr_util.h>

#include "std_cschem_conf.h"
#include "conf_internal.c"

static conf_std_cschem_t std_cschem_conf;

#if 0
typedef struct {
} std_cschem_ctx_t; /* per view data */
#endif

static const char std_cschem_cookie[] = "std_cschem";

/*** hooks ***/

RND_INLINE fgw_error_t render_hier_netname(fgw_arg_t *res, gds_t *prefix, const char *sname)
{
	gds_t tmp;

	gds_init(&tmp);
	gds_append_len(&tmp, prefix->array, prefix->used);
	gds_append_str(&tmp, sname);
	res->type = FGW_STR | FGW_DYN;
	res->val.str = tmp.array;

	return 0;
}

fgw_error_t std_cschem_wirenet_name_to_net_name(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *sname;
	csch_hier_path_t *hpath;
	csch_chdr_t *wnet;

	CSCH_HOOK_CONVARG(1, FGW_STR,  std_cschem_wirenet_name_to_net_name, sname = argv[1].val.cstr);
	CSCH_HOOK_CONVARG(2, FGW_COBJ, std_cschem_wirenet_name_to_net_name, wnet = fgw_cobj(&argv[2]));
	CSCH_HOOK_CONVARG(3, FGW_HPATH, std_cschem_wirenet_name_to_net_name, hpath = fgw_hpath(&argv[3]));

	if (sname == NULL)
		return 0;

	return render_hier_netname(res, &hpath->prefix, sname);
}

fgw_error_t std_cschem_attrib_net_name_to_net_name(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *sname;
	csch_hier_path_t *hpath;
	csch_acomp_t *acomp;
	gds_t prefix = {0};
	fgw_error_t retval;

	CSCH_HOOK_CONVARG(1, FGW_STR,  std_cschem_attrib_net_name_to_net_name, sname = argv[1].val.cstr);
	CSCH_HOOK_CONVARG(2, FGW_AOBJ, std_cschem_attrib_net_name_to_net_name, acomp = fgw_aobj(&argv[2]));

	if (sname == NULL)
		return 0;

	csch_hier_build_path(&prefix, acomp->hlev);
	retval = render_hier_netname(res, &prefix, sname);
	gds_uninit(&prefix);

	return retval;
}

fgw_error_t std_cschem_symbol_name_to_component_name(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	const char *sname, *end;
	int preflen;
	gds_t tmp;
	csch_chdr_t *sym, *o;
	csch_hier_path_t *hpath;

/*	fgw_obj_t *obj = argv[0].val.argv0.func->obj;*/
/*	std_cschem_ctx_t *ctx = obj->script_data; */

	CSCH_HOOK_CONVARG(1, FGW_STR,  std_cschem_symbol_name_to_component_name, sname = argv[1].val.cstr);
	CSCH_HOOK_CONVARG(2, FGW_COBJ, std_cschem_symbol_name_to_component_name, sym = fgw_cobj(&argv[2]));
	CSCH_HOOK_CONVARG(3, FGW_HPATH, std_cschem_symbol_name_to_component_name, hpath = fgw_hpath(&argv[3]));

	if (sname == NULL)
		return 0;


	if (!std_cschem_conf.plugins.std_cschem.fix_unnamed_syms) {
		normal_hprefix:;
		if (hpath->prefix.used > 0) { /* apply hierarchic prefix */ 
			gds_init(&tmp);
			gds_append_len(&tmp, hpath->prefix.array, hpath->prefix.used);
			gds_append_str(&tmp, sname);
			res->type = FGW_STR | FGW_DYN;
			res->val.str = tmp.array;
		}
		return 0;
	}

	end = sname + strlen(sname) - 1;
	if (*end != '?')
		goto normal_hprefix;

	while((*end == '?') && (end >= sname)) end--;
	preflen = end - sname + 1;

	gds_init(&tmp);

	if (hpath->prefix.used > 0)
		gds_append_len(&tmp, hpath->prefix.array, hpath->prefix.used);

	if (preflen > 0)
		gds_append_len(&tmp, sname, preflen);
	else
		gds_append_str(&tmp, "anon");

	gds_append(&tmp, '_');

	for(o = sym; o->parent != NULL; o = &o->parent->hdr)
		rnd_append_printf(&tmp, "_%ld", o->oid);

	rnd_message(RND_MSG_INFO, "Compile: renaming component '%s' to '%s'; please name your symbols!\n", sname, tmp.array);

	res->type = FGW_STR | FGW_DYN;
	res->val.str = tmp.array;
	return 0;
}


fgw_error_t std_cschem_compile_comp1(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
/*	fgw_obj_t *obj = argv[0].val.argv0.func->obj;*/
/*	std_cschem_ctx_t *ctx = obj->script_data; */
	csch_hook_call_ctx_t *cctx = argv[0].val.argv0.user_call_ctx;
	csch_acomp_t *comp;
	const csch_attrib_t *conn;
	long n;

	CSCH_HOOK_CONVARG(1, FGW_AOBJ, std_cschem_comp_update, comp = fgw_aobj(&argv[1]));
	assert(comp->hdr.type == CSCH_ATYPE_COMP);

	if (comp->hdr.omit)
		return 0;

	conn = csch_attrib_get(&comp->hdr.attr, "connect");
	if (conn == NULL)
		return 0;

	if (conn->val != NULL) {
		rnd_message(RND_MSG_ERROR, "Abstract component's 'connect' attribute is not an array in '%s'\n", comp->name);
		return -1;
	}

	for(n = 0; n < conn->arr.used; n++) {
		char tmp[1024];
		const char *sep;
		char *portname, *netname;
		int len;

		sep = strchr(conn->arr.array[n], ':');
		if (sep == NULL) {
			rnd_message(RND_MSG_ERROR, "Abstract component ('%s') 'connect' attribute item doesn't have a ':' separator: '%s' (ignoring item)\n", comp->name, conn->arr.array[n]);
			continue;
		}
		
		len = strlen(conn->arr.array[n]);
		if (len >= sizeof(tmp)) {
			rnd_message(RND_MSG_ERROR, "Abstract component ('%s') 'connect' attribute too long: '%s' (ignoring item)\n", comp->name, conn->arr.array[n]);
			continue;
		}
		len = sep - conn->arr.array[n];
		strcpy(tmp, conn->arr.array[n]);
		portname = tmp;
		netname = tmp+len;
		*netname = '\0';
		netname++;

		if (csch_cmp_nameconn_port_net(cctx->project, comp, portname, netname, 1, cctx->view_id, cctx->hpath) < 0) {
			csch_aport_t *port = csch_aport_get(comp->hdr.abst, comp, portname, 1);
			rnd_message(RND_MSG_ERROR, "Abstract component ('%s') 'connect' attribute failed: '%s'\n", comp->name, conn->arr.array[n]);
			if (port->conn.net == NULL) {
				rnd_message(RND_MSG_ERROR, "  Reason: symbol's connect attribute on an explicitly drawn terminal that's not connected to a wire\n");
			}


			return -1;
		}
	}

	return 0;
}

#include "quick_attr_connect.c"


static int on_load(fgw_obj_t *obj, const char *filename, const char *opts)
{
	fgw_func_reg(obj, "wirenet_name_to_net_name", std_cschem_wirenet_name_to_net_name);
	fgw_func_reg(obj, "attrib_net_name_to_net_name", std_cschem_attrib_net_name_to_net_name);
	fgw_func_reg(obj, "symbol_name_to_component_name", std_cschem_symbol_name_to_component_name);
	fgw_func_reg(obj, "compile_component1", std_cschem_compile_comp1);

	/* initialize view-local cache */
	obj->script_data = /*calloc(sizeof(std_cschem_ctx_t), 1)*/ NULL;

	return 0;
}

static int on_unload(fgw_obj_t *obj)
{
/*	std_cschem_ctx_t *ctx = obj->script_data;
	free(ctx);*/
	return 0;
}


static const fgw_eng_t fgw_std_cschem_eng = {
	"std_cschem",
	csch_c_call_script,
	NULL,
	on_load,
	on_unload
};

static rnd_action_t std_cschem_action_list[] = {
	{"quick_attr_connect", csch_act_quick_attr_connect, csch_acth_quick_attr_connect, csch_acts_quick_attr_connect}
};

int pplg_check_ver_std_cschem(int ver_needed) { return 0; }

void pplg_uninit_std_cschem(void)
{
	rnd_remove_actions_by_cookie(std_cschem_cookie);
	rnd_conf_plug_unreg("plugins/std_cschem/", std_cschem_conf_internal, std_cschem_cookie);
}

int pplg_init_std_cschem(void)
{
	RND_API_CHK_VER;

	fgw_eng_reg(&fgw_std_cschem_eng);

	RND_REGISTER_ACTIONS(std_cschem_action_list, std_cschem_cookie);

	rnd_conf_plug_reg(std_cschem_conf, std_cschem_conf_internal, std_cschem_cookie);
#define conf_reg(field,isarray,type_name,cpath,cname,desc,flags) \
	rnd_conf_reg_field(std_cschem_conf, field,isarray,type_name,cpath,cname,desc,flags);
#include "std_cschem_conf_fields.h"

	return 0;
}

