/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2018,2019,2022 Tibor 'Igor2' Palinkas
 *
 *  (Supported by NLnet NGI0 PET Fund in 2022)
 *
 *  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-lib for attrib.c */

struct csch_source_arg_s {
	char type[8];
	char src_desc[1]; /* "source::desc"; dynamic length, allocated together with csch_source_arg_s */
};

void csch_attr_src_free(csch_source_arg_t *src)
{
	if (src == NULL)
		return;
	src->type[0] = '\0';
	free(src);
}

static void append_src(csch_attrib_t *a, int prio, csch_source_arg_t *source, int fail)
{
	char tmp[32];
	gds_t s;
	gds_init(&s);
	sprintf(tmp, "%d::%s", prio, source->type);
	gds_append_str(&s, tmp);
	if (fail)
		gds_append(&s, '-');
	gds_append_str(&s, "::");
	if (*source->src_desc == '\0')
		gds_append(&s, '-');
	else
		gds_append_str(&s, source->src_desc);
	vts0_append(&a->source, s.array);
}

void csch_attrib_append_src(csch_attrib_t *a, int prio, csch_source_arg_t *source, int fail)
{
	append_src(a, prio, source, fail);
}

static csch_sheet_t *csch_attrib_src_lookup_sheet(csch_project_t *prj, const char *name, long name_len)
{
	long n;
	for(n = 0; n < prj->hdr.designs.used; n++) {
		csch_sheet_t *sheet = prj->hdr.designs.array[n];
		if (sheet->hidlib.loadname != NULL) {
			long slen = strlen(sheet->hidlib.loadname);
			if ((slen == name_len) && (memcmp(sheet->hidlib.loadname, name, slen) == 0))
				return sheet;
		}
	}
	return NULL;
}

/* Return the first occurance of cc in str (cheap assuming str is short) */
static char *strdchr(const char *str, char c)
{
	for(;;) {
		if ((str[0] == c) && (str[1] == c))
			return (char *)str;
		if (str[0] == '\0')
			return NULL;
		str++;
	}
}

static int csch_attrib_src_parse_c(csch_sheet_t *ssheet, const char *source, csch_chdr_t **cobj, char **attr, const char **desc)
{
	char *sep, *end, *attr_start, *attr_end;
	csch_sheet_t *sheet;
	char *tmp;


	if (*source == '-') source++;
	if ((source[0] != ':') || (source[1] != ':')) return -1;
	source += 2;

	sep = strchr(source, ',');
	if (sep == NULL) return -1;

	sheet = csch_attrib_src_lookup_sheet((csch_project_t *)ssheet->hidlib.project, source, sep-source);
	if (sheet == NULL)
		return -1;


	/* split off desc, strdup "oidpath,..." into tmp */
	sep++;
	end = strdchr(sep, ':');
	if (end != NULL) {
		tmp = rnd_strndup(sep, end - sep);
		if (desc != NULL) *desc = end+2;
	}
	else
		tmp = rnd_strdup(end);

	/* find the comma after oidpath */
	attr_start = strchr(tmp, ',');
	if (attr_start == NULL) {
		free(tmp);
		return -1;
	}
	*attr_start = '\0';
	attr_start++;

	if (cobj != NULL) {
		csch_oidpath_t oidp = {0};
		csch_oidpath_parse(&oidp, tmp);
		*cobj = csch_oidpath_resolve(sheet, &oidp);
		csch_oidpath_free(&oidp);
	}

	if (attr != NULL) {
		/* figure end of attributes, move it in front of tmp */
		attr_end = strchr(attr_start, ',');
		if (attr_end != NULL) {
			*attr_end = '\0';
			attr_end++;
			memmove(tmp, attr_start, attr_end-attr_start);
		}
		else {
			long l = strlen(attr_start);
			memmove(tmp, attr_start, l);
			tmp[l] = '\0';
		}

		*attr = tmp;
	}
	else
		free(tmp);

	return 0;
}

static int csch_attrib_src_parse_a(csch_sheet_t *sheet, const char *source, csch_ahdr_t **aobj, char **attr, const char **desc)
{
	csch_project_t *prj = (csch_project_t *)sheet->hidlib.project;
	long aid;
	char *end, *tmp, *d;

	if (*source == '-') source++;
	if ((source[0] != ':') || (source[1] != ':')) return -1;
	source += 2;

	aid = strtol(source, &end, 10);
	if (*end != ',')
		return -1;
	if (aobj != NULL)
		*aobj = htip_get(&prj->abst->aid2obj, aid);

	end++;
	d = strdchr(end, ':');
	if (d != NULL) {
		if (attr != NULL)
			tmp = rnd_strndup(end, d - end);
		if (desc != NULL)
			*desc = d+2;
	}
	else if (attr != NULL)
		tmp = rnd_strdup(end);

	if (attr != NULL) {
		/* cut off ,plugin from attr name */
		end = strchr(tmp, ',');
		if (end != NULL)
			*end = '\0';

		*attr = tmp;
	}

	return 0;
}

#define CMP1(c0) \
	((end[0] == c0) && ((end[1] == ':') || (end[1] == '-')))
#define CMP2(c0, c1) \
	((end[0] == c0) && (end[1] == c1) && ((end[2] == ':') || (end[2] == '-')))

int csch_attrib_src_parse(csch_sheet_t *sheet, const char *source, int *prio, csch_attrib_src_type_t *type, csch_chdr_t **cobj, csch_ahdr_t **aobj, char **attr, const char **desc)
{
	char *end, *s;
	long priol;

	if (prio != NULL) *prio = -1;
	if (cobj != NULL) *cobj = NULL;
	if (aobj != NULL) *aobj = NULL;
	if (attr != NULL) *attr = NULL;
	if (desc != NULL) *desc = NULL;

	priol = strtol(source, &end, 10);
	if ((end[0] != ':') || (end[1] != ':') || (priol < 0) || (priol > 65535))
		return -1;

	if (prio != NULL) *prio = priol;

	end+=2;

	if (type != NULL) {
		*type = 0;
		for(s = end; (*s != ':') && (*s != '\0'); s++) {
			switch(*s) {
				case 'p': *type |= CSCH_ASRCT_P; break;
				case 'c': *type |= CSCH_ASRCT_C; break;
				case 'a': *type |= CSCH_ASRCT_A; break;
				case 'e': *type |= CSCH_ASRCT_E; break;
				case '-': *type |= CSCH_ASRCT_FAIL; break;
			}
		}
	}

	if (CMP1('c')) return 0; /* concrete model address */
	if (CMP1('p')) return 0; /* no attribute address */
	if (CMP2('a', 'c')) return csch_attrib_src_parse_c(sheet, end+2, cobj, attr, desc);
	if (CMP2('p', 'c')) return csch_attrib_src_parse_c(sheet, end+2, cobj, attr, desc);
	if (CMP2('p', 'a')) return csch_attrib_src_parse_a(sheet, end+2, aobj, attr, desc);
	return -1;
}

#undef CMP1
#undef CMP2

#define ATTRIB_SRC_INIT \
	gds_t tmp = {0}; \
	csch_source_arg_t *dst; \
	gds_enlarge(&tmp, sizeof(csch_source_arg_t)+32); \
	tmp.used = offsetof(csch_source_arg_t, src_desc); \
	dst = (csch_source_arg_t *)tmp.array

csch_source_arg_t *csch_attrib_src_c(const char *filename, long line, long col, const char *desc)
{
	ATTRIB_SRC_INIT;

	dst->type[0] = 'c';
	dst->type[1] = '\0';

	if (filename == NULL) filename = "-";

	if ((line > 0) && (col > 0))
		rnd_append_printf(&tmp, "%s,%ld,%ld::", filename, line, col);
	else if (line > 0)
		rnd_append_printf(&tmp, "%s,%ld::", filename, line);
	else {
		gds_append_str(&tmp, filename);
		rnd_append_printf(&tmp, "%s::", filename);
	}

	if (desc != NULL)
		gds_append_str(&tmp, desc);

	return (csch_source_arg_t *)tmp.array;
}

csch_source_arg_t *csch_attrib_src_ac(csch_chdr_t *cobj, const char *attr_name, const char *desc)
{
	csch_oidpath_t oidp = {0};
	ATTRIB_SRC_INIT;

	dst->type[0] = 'a';
	dst->type[1] = 'c';
	dst->type[2] = '\0';

	if (attr_name == NULL) attr_name = "";
	if (desc == NULL) desc = "";

	if (cobj->sheet->hidlib.loadname != NULL)
		gds_append_str(&tmp, cobj->sheet->hidlib.loadname);
	else
		gds_append_str(&tmp, "<unsaved>");
	gds_append(&tmp, ',');
	csch_oidpath_from_obj(&oidp, cobj);
	csch_oidpath_to_str_append(&tmp, &oidp);
	csch_oidpath_free(&oidp);
	gds_append(&tmp, ',');
	gds_append_str(&tmp, attr_name);
	gds_append_str(&tmp, "::");

	if (desc != NULL)
		gds_append_str(&tmp, desc);

	return (csch_source_arg_t *)tmp.array;
}


csch_source_arg_t *csch_attrib_src_pa(csch_ahdr_t *aobj, const char *attr_name, const char *plugin_name, const char *desc)
{
	ATTRIB_SRC_INIT;

	dst->type[0] = 'p';
	dst->type[1] = 'a';
	dst->type[2] = '\0';

	if (attr_name == NULL) attr_name = "";
	if (plugin_name == NULL) plugin_name = "";

	rnd_append_printf(&tmp, "%ld,%s,%s::", aobj->aid, attr_name, plugin_name);
	if (desc != NULL)
		gds_append_str(&tmp, desc);

	return (csch_source_arg_t *)tmp.array;
}

csch_source_arg_t *csch_attrib_src_p(const char *plugin_name, const char *desc)
{
	ATTRIB_SRC_INIT;

	dst->type[0] = 'p';
	dst->type[1] = '\0';

	if (plugin_name == NULL) plugin_name = "";
	if (desc == NULL) desc = "";

	gds_append_str(&tmp, plugin_name);
	gds_append_str(&tmp, "::");

	if (desc != NULL)
		gds_append_str(&tmp, desc);

	return (csch_source_arg_t *)tmp.array;
}

