/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2018,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., 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
 */


#ifndef CSCH_ATTRIB_H
#define CSCH_ATTRIB_H
#include "libcschem/common_types.h"
#include <genht/htsp.h>
#include <genvector/vts0.h>
#include <genvector/vtp0.h>

typedef enum {
	CSCH_ATP_HARDWIRED = 0,
	CSCH_ATP_USER_DEFAULT = 250
} csch_attrib_prio_t;

typedef struct csch_attrib_s {
	char *key;
	int prio;

	unsigned int deleted:1;

	char *val;  /* scalar; if NULL, use arr */
	vts0_t arr;
	vts0_t source;

	char *export_name; /* abstract model: if not NULL, export this attribute using this name (always strdup'd) */
} csch_attrib_t;

/* Opaque struct for temporarily holding attribute source information */
typedef struct csch_source_arg_s csch_source_arg_t;

/* Create source info in dst for different source addressing schemes, as
   specified in {des4:19} */
csch_source_arg_t *csch_attrib_src_c(const char *filename, long line, long col, const char *desc);
csch_source_arg_t *csch_attrib_src_ac(csch_chdr_t *cobj, const char *attr_name, const char *desc);
csch_source_arg_t *csch_attrib_src_pa(csch_ahdr_t *aobj, const char *attr_name, const char *plugin_name, const char *desc);
csch_source_arg_t *csch_attrib_src_p(const char *plugin_name, const char *desc);

typedef enum csch_attrib_src_type_e { /* bitfield */
	CSCH_ASRCT_P = 1,
	CSCH_ASRCT_C = 2,
	CSCH_ASRCT_A = 4,
	CSCH_ASRCT_E = 8,
	CSCH_ASRCT_FAIL = 16
} csch_attrib_src_type_t;

/* Parse a source string found in attribute history; return 0 on success; out:
    - prio: if not NULL: integer priority
    - type: if not NULL: bitfield of type letters
    - cobj: if not NULL: loaded with concrete object if addr is concrete
    - aobj: if not NULL: loaded with abstract object if addr is abstract
    - attr: if not NULL: loaded with strdup()'d attribute name if available
    - desc: if not NULL: loaded with pointer into the original source to description
   Returned values are valid only until any change to the data model in
   the project or recompilation. *attr needs to be free()'d be the caller.
   Any output may be NULL if field is not available.
*/
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);


typedef htsp_t csch_attribs_t;

void csch_attrib_init(csch_attribs_t *attribs);
void csch_attrib_uninit(csch_attribs_t *attribs);

#define csch_attrib_get(attribs, key) htsp_get((csch_attribs_t *)attribs, key)

/* Returns the string value (or arr value) of an attribute or NULL if
   the attribute doesn't exist or is not a string (or arr) */
RND_INLINE const char *csch_attrib_get_str(const csch_attribs_t *attribs, const char *key);
RND_INLINE const vts0_t *csch_attrib_get_arr(const csch_attribs_t *attribs, const char *key);


/* Set a scalar attribute; the ptr version takes malloc()'d val and source;
   return 0 on write, -1 on failure or if the attrib didn't change because of prios
   (ptr means "use the ptr provided, don't strdup"). src is always free'd. */
int csch_attrib_set(csch_attribs_t *attribs, int prio, const char *key, const char *val, csch_source_arg_t *source, csch_attrib_t **attr_out);
int csch_attrib_setptr(csch_attribs_t *attribs, int prio, char *key, const char *val, csch_source_arg_t *source, csch_attrib_t **attr_out);

/* Overwrite an array; src is always free'd. Items are strdup'd */
int csch_attrib_set_arr(csch_attribs_t *attribs, int prio, const char *key, const vts0_t *val, csch_source_arg_t *source, csch_attrib_t **attr_out);
int csch_attrib_set_arr_c(csch_attribs_t *attribs, int prio, const char *key, const char **val, csch_source_arg_t *source, csch_attrib_t **attr_out);

/* Same as above, but for arrays: overwrite an existing item; src is always free'd. */
int csch_attrib_seti(csch_attribs_t *attribs, int prio, const char *key, long idx, const char *val, csch_source_arg_t *source, csch_attrib_t **attr_out);
int csch_attrib_setiptr(csch_attribs_t *attribs, int prio, const char *key, long idx, char *val, csch_source_arg_t *source, csch_attrib_t **attr_out);

/* Prepend or append to an array; src is always free'd.*/
int csch_attrib_prepend(csch_attribs_t *attribs, int prio, const char *key, const char *val, csch_source_arg_t *source);
int csch_attrib_prependptr(csch_attribs_t *attribs, int prio, const char *key, char *val, csch_source_arg_t *source);
int csch_attrib_append(csch_attribs_t *attribs, int prio, const char *key, const char *val, csch_source_arg_t *source);
int csch_attrib_appendptr(csch_attribs_t *attribs, int prio, const char *key, char *val, csch_source_arg_t *source);

/* Append the value of srca; if it's an array, append all elements in order */
int csch_attrib_append_val(csch_attribs_t *attribs, int prio, const char *key, const csch_attrib_t *srca, csch_source_arg_t *source);


/* Del a key from an attribute hash; if history is not NULL, the key is kept
   and the value is emptied so source history is preserved.
   src is always free'd. */
int csch_attrib_del(csch_attribs_t *attribs, int prio, const char *key, csch_source_arg_t *source);


/* Build an ordered list of attributes; vector items point to
   (const csch_attrib_t *) */
void csch_attrib_sort(vtp0_t *dst, const csch_attribs_t *attribs);

/* Overwrites the export name from expname */
void csch_attrib_set_export_name(csch_attrib_t *dst, const char *expname);


/* Allocate a new attribute, duplicate src with all sources (deep copy); does
   not copy export name. */
csch_attrib_t *csch_attrib_dup(csch_attrib_t *src);
csch_attrib_t *csch_attrib_dup_rename(csch_attrib_t *src, const char *new_key);


/* copy each src attrib to dst */
void csch_attrib_copy_all(csch_attribs_t *dst, const csch_attribs_t *src);
void csch_attrib_copy_all_undoable(csch_sheet_t *sheet, csch_cgrp_t *dst_obj, csch_attribs_t *dst, const csch_attribs_t *src, int undoable);

/* copy each src attrib to dst using a forced source; returns 0 on success;
   src is always free'd. If force_prio is not NULL, use that as the new
   attributes prio - plugins shall do this normally. Does not apply '-' or '+'
   prefix; rather use csch_compile_attribute() unless low level copy is needed */
int csch_attrib_apply(csch_attribs_t *dst, const csch_attribs_t *src, csch_source_arg_t *source, int *force_prio);

unsigned csch_attrib_hash(const csch_attribs_t *attr);
int csch_attrib_eq(const csch_attribs_t *a1, const csch_attribs_t *a2);
int csch_attrib_eq_(const csch_attrib_t *a1, const csch_attrib_t *a2);
int csch_attrib_val_eq_(const csch_attrib_t *a1, const csch_attrib_t *a2);


/* Standard undoable mod functions; negative prio means: keep original if
   possible, use positive version of the prio if original is not available;
   src is always free'd. */
void csch_attr_modify_str(csch_sheet_t *sheet, csch_cgrp_t *obj, int prio, const char *key, const char *val, csch_source_arg_t *source, int undoable);
void csch_attr_modify_del(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, int undoable);

/* Undoable modification of attribute priority */
void csch_attr_modify_prio(csch_sheet_t *sheet, csch_cgrp_t *obj, csch_attrib_t *a, int prio, csch_source_arg_t *source, int undoable);


/* Rename an attribute (by creating a new one and removing the old one);
   new_name must not be the same as a->key */
void csch_attr_modify_rename(csch_sheet_t *sheet, csch_cgrp_t *grp, csch_attrib_t *a, const char *new_name, csch_source_arg_t *src, int undoable);

void csch_attr_arr_modify_ins_before(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, long idx, const char *newval, int undoable);
void csch_attr_arr_modify_str(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, long idx, const char *newval, int undoable);
void csch_attr_arr_modify_del(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, long idx, int undoable);
void csch_attr_arr_modify_move(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, long idx, long delta, int undoable);
void csch_attr_modify_conv_to_arr(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, int undoable);
void csch_attr_modify_conv_to_str(csch_sheet_t *sheet, csch_cgrp_t *obj, const char *key, int undoable);

/* Free fields of src and src itself */
void csch_attr_src_free(csch_source_arg_t *src);

/* allocate new csch_source_arg_t and copy src */
csch_source_arg_t *csch_attr_src_dup(csch_source_arg_t *src);

/* Append src to history of abstract attribute a; useful to log failed
   attribute sets */
void csch_attrib_append_src(csch_attrib_t *a, int prio, csch_source_arg_t *source, int fail);

void csch_attr_side_effects(csch_cgrp_t *grp, const char *key);

/* Some alien I/O code need to remove attributes after creating them on load */
void csch_attr_free(csch_attrib_t *a);

/*** implementation ***/
RND_INLINE const char *csch_attrib_get_str(const csch_attribs_t *attribs, const char *key)
{
	csch_attrib_t *a = csch_attrib_get(attribs, key);
	if ((a == NULL) || (a->deleted)) return NULL;
	return a->val;
}

RND_INLINE const vts0_t *csch_attrib_get_arr(const csch_attribs_t *attribs, const char *key)
{
	csch_attrib_t *a = csch_attrib_get(attribs, key);
	if ((a == NULL) || (a->deleted) || (a->val != NULL)) return NULL;
	return &a->arr;
}

#endif
