/*
 *                            COPYRIGHT
 *
 *  sch-rnd - modular/flexible schematics editor - fawk non-graphical sheet support
 *  Copyright (C) 2023 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 Entrust in 2023)
 *
 *  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
 */

/* High level fawk parser and glue to core and comp.c */

#include <libcschem/config.h>
#include <ctype.h>

#include <librnd/core/error.h>
#include <librnd/core/safe_fs.h>
#include <libfungw/fungw.h>

#include <libcschem/concrete.h>
#include <libcschem/non_graphical.h>

#include <plugins/lib_ngrp/lib_ngrp.h>

#include "read.h"

pup_context_t io_ngrp_fawk_pup;


typedef struct ngrp_fawk_s {
	sch_ngrp_t ngrp; /* must be the first field */
	fgw_ctx_t fgw;
	fgw_obj_t *obj;
	pup_plugin_t *pup;
} ngrp_fawk_t;

static void io_ngrp_fawk_free(csch_sheet_t *sheet)
{
	ngrp_fawk_t *ctx = sheet->non_graphical_data;

	if (ctx->obj != NULL)
		fgw_obj_unreg(&ctx->fgw, ctx->obj);
#ifdef RND_HAVE_SYS_FUNGW
	if (ctx->pup != NULL)
		pup_unload(&io_ngrp_fawk_pup, ctx->pup, NULL);
#endif
	fgw_uninit(&ctx->fgw);


	sch_ngrp_free(&ctx->ngrp); /* also frees ctx */
}

static int io_ngrp_fawk_load_file(csch_sheet_t *sheet, const char *fn)
{
	ngrp_fawk_t *ctx = sheet->non_graphical_data;
	return sch_ngrp_load_file(sheet, &ctx->ngrp, fn);
}

static char *io_ngrp_fawk_draw_getline(csch_sheet_t *sheet, long *cnt, void **cookie)
{
	ngrp_fawk_t *ctx = sheet->non_graphical_data;
	return sch_ngrp_draw_getline(&ctx->ngrp, cnt, cookie);
}

static int io_ngrp_fawk_compile_sheet(csch_abstract_t *dst, int viewid, csch_hier_path_t *hpath, const csch_sheet_t *src)
{
	ngrp_fawk_t *ctx = src->non_graphical_data;
	return sch_ngrp_compile_sheet(dst, viewid, hpath, src, &ctx->ngrp);
}

static void io_ngrp_fawk_async_error(fgw_obj_t *obj, const char *msg)
{
	rnd_message(RND_MSG_ERROR, "%s", msg);
}

#define CONV_ISTR_(arg) \
do { \
	if (arg->type == FGW_DOUBLE) { \
		if (arg->val.nat_double == floor(arg->val.nat_double)) \
			FGW_ARG_CONV(arg, FGW_LONG); \
	} \
	else if (arg->type == FGW_FLOAT) { \
		if (arg->val.nat_float == floor(arg->val.nat_float)) \
			FGW_ARG_CONV(arg, FGW_LONG); \
	} \
	FGW_ARG_CONV(arg, FGW_STR); \
} while(0)

#define CONV_ISTR(arg) CONV_ISTR_((arg))

static fgw_error_t ngrp_fawk_acomp_attr(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	FGW_DECL_CTX;
	ngrp_fawk_t *nctx = (ngrp_fawk_t *)argv[0].val.argv0.user_call_ctx;
	sch_ngrp_obj_t *comp;
	int n;

	CONV_ISTR(&argv[1]);

	comp = sch_ngrp_add_obj(&nctx->ngrp.comps, argv[1].val.str, 0);
	for(n = 2; n+1 < argc; n += 2) {
		CONV_ISTR(&argv[n]);
		CONV_ISTR(&argv[n+1]);
		sch_ngrp_add_attr(&comp->attr_head, argv[n].val.str, argv[n+1].val.str, -1, 0);
	}

	return 0;
}

static fgw_error_t ngrp_fawk_anet_attr(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	FGW_DECL_CTX;
	ngrp_fawk_t *nctx = (ngrp_fawk_t *)argv[0].val.argv0.user_call_ctx;
	sch_ngrp_obj_t *net;
	int n;

	CONV_ISTR(&argv[1]);

	net = sch_ngrp_add_obj(&nctx->ngrp.nets, argv[1].val.str, 0);
	for(n = 2; n+1 < argc; n += 2) {
		CONV_ISTR(&argv[n]);
		CONV_ISTR(&argv[n+1]);
		sch_ngrp_add_attr(&net->attr_head, argv[n].val.str, argv[n+1].val.str, -1, 0);
	}

	return 0;
}

static fgw_error_t ngrp_fawk_aconn(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	FGW_DECL_CTX;
	ngrp_fawk_t *nctx = (ngrp_fawk_t *)argv[0].val.argv0.user_call_ctx;
	int n;

	CONV_ISTR(&argv[1]);

	sch_ngrp_add_obj(&nctx->ngrp.nets, argv[1].val.str, 0);
	for(n = 2; n+1 < argc; n += 2) {
		CONV_ISTR(&argv[n]);
		CONV_ISTR(&argv[n+1]);
		sch_ngrp_add_conn(&nctx->ngrp.conns, argv[1].val.str, argv[n].val.str, argv[n+1].val.str, 0);
	}

	return 0;
}

static int on_load(fgw_obj_t *obj, const char *filename, const char *opts)
{
	fgw_func_reg(obj, "acomp_attr",   ngrp_fawk_acomp_attr);
	fgw_func_reg(obj, "anet_attr",    ngrp_fawk_anet_attr);
	fgw_func_reg(obj, "aconn",        ngrp_fawk_aconn);
	return 0;
}

static fgw_error_t c_call_script(fgw_arg_t *res, int argc, fgw_arg_t *argv)
{
	fgw_error_t rv;
	fgw_func_t *fnc = argv[0].val.func;
	rv = fnc->func(res, argc, argv);
	fgw_argv_free(fnc->obj->parent, argc, argv);
	return rv;
}

const fgw_eng_t fgw_ngrp_fawk_eng = {
	"io_ngrp_fawk",
	c_call_script,
	NULL,
	on_load
};


int io_ngrp_fawk_parse(ngrp_fawk_t *ctx, const char *fn)
{
	csch_sheet_t *sheet = ctx->ngrp.sheet;

	rnd_trace("FAWK: parse fawk non-graphical sheet?\n");

	fgw_init(&ctx->fgw, "sch-rnd non-graphical sheet: fawk");

	ctx->fgw.async_error = io_ngrp_fawk_async_error;

#ifdef RND_HAVE_SYS_FUNGW
	{ /* need to load the fawk engine */
		static const char *pupname = "fungw_fawk";
		const char *paths[2];
		int st;

		paths[0] = FGW_CFG_PUPDIR;
		paths[1] = NULL;
		ctx->pup = pup_load(&io_ngrp_fawk_pup, paths, pupname, 0, &st);

		if (ctx->pup == NULL) {
			rnd_message(RND_MSG_ERROR, "non-graphical fawk sheet: can not load script engine %s\n", pupname);
			io_ngrp_fawk_free(sheet);
			return -1;
		}
	}
#endif

	fgw_obj_new(&ctx->fgw, "env", "io_ngrp_fawk", NULL, NULL);

	ctx->obj = fgw_obj_new2(&ctx->fgw, "script", "fawk", fn, NULL, ctx);
	if (ctx->obj == NULL) {
		rnd_message(RND_MSG_ERROR, "Failed to parse/execute script from file %s\n", fn);
		io_ngrp_fawk_free(sheet);
		return -1;
	}


	rnd_trace("FAWK: parsed fawk non-graphical sheet\n");

	return 0;
}


csch_non_graphical_impl_t io_ngrp_fawk_impl;

int io_ngrp_fawk_load_sheet(FILE *f, const char *fn, const char *fmt, csch_sheet_t *dst)
{
	ngrp_fawk_t *ctx = calloc(sizeof(ngrp_fawk_t), 1);

	if (ctx == NULL)
		return -1;

	ctx->ngrp.sheet = dst;
	dst->non_graphical = 1;
	dst->non_graphical_data = ctx;
	dst->non_graphical_impl = &io_ngrp_fawk_impl;

	io_ngrp_fawk_impl.compile_sheet = io_ngrp_fawk_compile_sheet;
	io_ngrp_fawk_impl.free_sheet = io_ngrp_fawk_free;
	io_ngrp_fawk_impl.draw_getline = io_ngrp_fawk_draw_getline;

	if (io_ngrp_fawk_load_file(dst, fn) != 0) {
		rnd_message(RND_MSG_ERROR, "Failed to load content of file '%s'\n", fn);
		goto error;
	}

	return io_ngrp_fawk_parse(ctx, fn);

	error:;
	io_ngrp_fawk_free(dst);
	return -1;
}

int io_ngrp_fawk_test_parse(FILE *f, const char *fn, const char *fmt, csch_plug_io_type_t type)
{
	char line[256], *s;

	if (fgets(line, sizeof(line), f) == NULL)
		return -1;

	if ((line[0] != '#') || (line[1] != '#'))
		return -1;

	for(s = line+2; isspace(*s); s++) ;

	if (strncmp(s, "cschem", 6) != 0)
		return -1;

	for(s += 6; isspace(*s); s++) ;

	if (strncmp(s, "fawk", 4) != 0)
		return -1;

	for(s += 4; isspace(*s); s++) ;

	if (strncmp(s, "sheet", 5) != 0)
		return -1;

	s += 5;

	if ((*s == '\0') || isspace(*s))
		return 0;

	return -1;
}


