/*
 *                            COPYRIGHT
 *
 *  pcb-rnd, interactive printed circuit board design
 *  Copyright (C) 2020 Tibor 'Igor2' Palinkas
 *
 *  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/pcb-rnd
 *    lead developer: http://repo.hu/projects/pcb-rnd/contact.html
 *    mailing list: pcb-rnd (at) list.repo.hu (send "subscribe")
 */

/* Query language - glue functions to other infra */

#include <libcschem/cnc_text.h>

static int fnc_action(pcb_qry_exec_t *ectx, int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res)
{
	int n, retv = 0;
	fgw_arg_t tmp, resa, arga[PCB_QRY_MAX_FUNC_ARGS], *arg;

	if (argv[0].type != PCBQ_VT_STRING) {
		rnd_message(RND_MSG_ERROR, "query: action() first argument must be a string\n");
		return -1;
	}

	/* convert query arguments to action arguments */
	for(n = 1; n < argc; n++) {
		switch(argv[n].type) {
			case PCBQ_VT_VOID:
				arga[n].type = FGW_PTR;
				arga[n].val.ptr_void = 0;
				break;
			case PCBQ_VT_LST:
				{
					long i;
					csch_oidpath_list_t *list = calloc(sizeof(csch_oidpath_list_t), 1);
					for(i = 0; i < argv[n].data.lst.used; i++) {
						csch_oidpath_t *idp = calloc(sizeof(csch_oidpath_t), 1);
						csch_oidpath_from_obj(idp, argv[n].data.lst.array[i]);
						fgw_ptr_reg(&rnd_fgw, &tmp, RND_PTR_DOMAIN_IDPATH, FGW_PTR | FGW_STRUCT, idp);
						csch_oidpath_list_append(list, fgw_idpath(&tmp));
					}
					fgw_ptr_reg(&rnd_fgw, &(arga[n]), RND_PTR_DOMAIN_IDPATH_LIST, FGW_PTR | FGW_STRUCT, list);
				}
				break;
			case PCBQ_VT_OBJ:
				{
					csch_oidpath_t *idp = calloc(sizeof(csch_oidpath_t), 1);
					csch_oidpath_from_obj(idp, argv[n].data.obj);
					fgw_ptr_reg(&rnd_fgw, &(arga[n]), RND_PTR_DOMAIN_IDPATH, FGW_PTR | FGW_STRUCT, idp);
				}
				break;
			case PCBQ_VT_COORD:
				arga[n].type = FGW_COORD;
				fgw_coord(&arga[n]) = argv[n].data.crd;
				break;
			case PCBQ_VT_LONG:
				arga[n].type = FGW_LONG;
				arga[n].val.nat_long = argv[n].data.lng;
				break;
			case PCBQ_VT_DOUBLE:
				arga[n].type = FGW_DOUBLE;
				arga[n].val.nat_double = argv[n].data.dbl;
				break;
			case PCBQ_VT_STRING:
				arga[n].type = FGW_STR;
				arga[n].val.cstr = argv[n].data.str;
				break;
		}
	}

	if (rnd_actionv_bin(&ectx->sheet->hidlib, argv[0].data.str, &resa, argc, arga) != 0) {
		retv = -1;
		goto fin;
	}

	/* convert action result to query result */

#	define FGW_TO_QRY_NUM(lst, val)   res->source = NULL; res->type = PCBQ_VT_LONG; res->data.lng = val; goto fin;
#	define FGW_TO_QRY_STR(lst, val)   res->source = NULL; res->type = PCBQ_VT_STRING; res->data.str = rnd_strdup(val == NULL ? "" : val); goto fin;
#	define FGW_TO_QRY_NIL(lst, val)   res->source = NULL; res->type = PCBQ_VT_VOID; goto fin;

/* has to free idpath and idpathlist return, noone else will have the chance */
#	define FGW_TO_QRY_PTR(lst, val) \
	if (val == NULL) { \
		res->source = NULL; \
		res->type = PCBQ_VT_VOID; \
		goto fin; \
	} \
	else if (fgw_ptr_in_domain(&rnd_fgw, val, RND_PTR_DOMAIN_IDPATH)) { \
		csch_oidpath_t *idp = val; \
		res->source = NULL; \
		res->type = PCBQ_VT_OBJ; \
		res->data.obj = csch_oidpath_resolve(ectx->sheet, idp); \
		csch_oidpath_list_remove(idp); \
		fgw_ptr_unreg(&rnd_fgw, &resa, RND_PTR_DOMAIN_IDPATH); \
		free(idp); \
		goto fin; \
	} \
	else if (fgw_ptr_in_domain(&rnd_fgw, val, RND_PTR_DOMAIN_IDPATH_LIST)) { \
		rnd_message(RND_MSG_ERROR, "query action(): can not convert object list yet\n"); \
		res->source = NULL; \
		res->type = PCBQ_VT_VOID; \
		goto fin; \
	} \
	else { \
		rnd_message(RND_MSG_ERROR, "query action(): can not convert unknown pointer\n"); \
		res->source = NULL; \
		res->type = PCBQ_VT_VOID; \
		goto fin; \
	}

	arg = &resa;
	if (FGW_IS_TYPE_CUSTOM(resa.type))
		fgw_arg_conv(&rnd_fgw, &resa, FGW_AUTO);

	switch(FGW_BASE_TYPE(resa.type)) {
		ARG_CONV_CASE_LONG(lst, FGW_TO_QRY_NUM);
		ARG_CONV_CASE_LLONG(lst, FGW_TO_QRY_NUM);
		ARG_CONV_CASE_DOUBLE(lst, FGW_TO_QRY_NUM);
		ARG_CONV_CASE_LDOUBLE(lst, FGW_TO_QRY_NUM);
		ARG_CONV_CASE_PTR(lst, FGW_TO_QRY_PTR);
		ARG_CONV_CASE_STR(lst, FGW_TO_QRY_STR);
		ARG_CONV_CASE_CLASS(lst, FGW_TO_QRY_NIL);
		ARG_CONV_CASE_INVALID(lst, FGW_TO_QRY_NIL);
	}
	if (arg->type & FGW_PTR) {
		FGW_TO_QRY_PTR(lst, arg->val.ptr_void);
	}
	else {
		FGW_TO_QRY_NIL(lst, 0);
	}

	fin:;
	for(n = 1; n < argc; n++) {
		switch(argv[n].type) {
			case PCBQ_VT_LST:
				{
					csch_oidpath_list_t *list = arga[n].val.ptr_void;
					csch_oidpath_list_clear(list);
					free(list);
					arga[n].val.ptr_void = NULL;
				}
				break;
			default: break;
		}
	}

	fgw_arg_free(&rnd_fgw, &resa);
	return retv;
}

int pcb_qry_fnc_getconf(pcb_qry_exec_t *ectx, int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res)
{
	rnd_conf_native_t *nat;

	if (argc != 1)
		return -1;
	if (argv[0].type != PCBQ_VT_STRING)
		return -1;

	nat = rnd_conf_get_field(argv[0].data.str);
	if (nat == NULL)
		PCB_QRY_RET_INV(res);

	switch(nat->type) {
		case RND_CFN_STRING:   PCB_QRY_RET_STR(res, nat->val.string[0]);
		case RND_CFN_BOOLEAN:  PCB_QRY_RET_INT(res, nat->val.boolean[0]);
		case RND_CFN_INTEGER:  PCB_QRY_RET_INT(res, nat->val.integer[0]);
		case RND_CFN_REAL:     PCB_QRY_RET_DBL(res, nat->val.real[0]);
		case RND_CFN_COORD:    PCB_QRY_RET_COORD(res, nat->val.coord[0]);
		
		case RND_CFN_COLOR:  PCB_QRY_RET_STR(res, nat->val.color[0].str);

		case RND_CFN_UNIT:
			if (nat->val.unit[0] == NULL)
				PCB_QRY_RET_INV(res);
			else
				PCB_QRY_RET_STR(res, nat->val.unit[0]->suffix);
			break;

		case RND_CFN_LIST:
		case RND_CFN_HLIST:
		case RND_CFN_max:
			PCB_QRY_RET_INV(res);
	}
	return 0;
}

static int fnc_obj_by_idpath(pcb_qry_exec_t *ectx, int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res)
{
	csch_sheet_t *sheet = ectx->sheet;
	csch_oidpath_t path;
	csch_chdr_t *obj;

	if ((argc != 1) || (argv[0].type != PCBQ_VT_STRING))
		return -1;

	if (csch_oidpath_parse(&path, argv[0].data.str) != 0) /* slash separated list of ids */
		PCB_QRY_RET_INV(res);

	obj = csch_oidpath_resolve(sheet, &path);
	csch_oidpath_free(&path);

	if (obj == NULL)
		PCB_QRY_RET_INV(res);

	PCB_QRY_RET_OBJ(res, obj);
}

static int fnc_text_invalid_chars(pcb_qry_exec_t *ectx, int argc, pcb_qry_val_t *argv, pcb_qry_val_t *res)
{
	csch_chdr_t *obj;

	if ((argc != 1) || (argv[0].type != PCBQ_VT_OBJ))
		return -1;

	obj = argv[0].data.obj;
	if (obj->type != CSCH_CTYPE_TEXT)
		return -1;

	PCB_QRY_RET_INT(res, csch_text_invalid_chars(((csch_text_t *)obj)));
}
