/*
 *                            COPYRIGHT
 *
 *  cschem - modular/flexible schematics editor - libcschem (core library)
 *  Copyright (C) 2018, 2022, 2024 Tibor 'Igor2' Palinkas
 *
 *  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_CONCRETE_H
#define CSCH_CONCRETE_H

#include <librnd/core/global_typedefs.h>
#include <librnd/core/hidlib.h>
#include <librnd/core/color.h>

#include <librnd/core/conf_multi.h>

#include "libcschem/common_types.h"
#include "libcschem/rtree.h"
#include "libcschem/attrib.h"
#include "libcschem/oidpath.h"
#include <libuundo/uundo.h>
#include <libminuid/libminuid.h>
#include <genht/htsp.h>
#include <genht/htip.h>
#include <genht/htpi.h>
#include <genvector/vtl0.h>
#include <genlist/gendlist.h>
#include GENGEO2D_TYPECFG
#include <gengeo2d/prim.h>

/* the two top level sheet groups have hardwired OIDs by the spec */
#define CSCH_TOP_OID_INDIRECT 1
#define CSCH_TOP_OID_DIRECT 2

typedef enum csch_displayer_e {
	CSCH_DSPLY_BACKGROUND,        /* bottom: decoration */
	CSCH_DSPLY_WIRE,              /* wiring: wires, hubs, anything non-bus */
	CSCH_DSPLY_BUS,               /* buses only */
	CSCH_DSPLY_SYMBOL,            /* symbol graphics that are not in HUBTERM */
	CSCH_DSPLY_HUBTERM,           /* non-in-symbol hubs, terminals (overrides symbol so symbol terminals are included) */
	CSCH_DSPLY_DECORATION,        /* text, annotations, titleblocks */
	CSCH_DSPLY_SYMBOL_GRP,        /* symbol groups and group refs */
	CSCH_DSPLY_CONN,              /* connections or missing connections */
	CSCH_DSPLY_SYMBOL_META,       /* symbol bbox and loclib info */
	CSCH_DSPLY_TEXT_META,         /* text bbox and orientation */
	CSCH_DSPLY_max,
	CSCH_DSPLY_invalid = -1
} csch_displayer_t;

extern int csch_layer_vis[CSCH_DSPLY_max];
extern int csch_export_layer_vis[CSCH_DSPLY_max];

typedef enum csch_ctype_s {
	CSCH_CTYPE_invalid = 0,
	CSCH_CTYPE_LINE,
	CSCH_CTYPE_ARC,
	CSCH_CTYPE_POLY,
	CSCH_CTYPE_TEXT,
	CSCH_CTYPE_BITMAP,
	CSCH_CTYPE_CONN,
	CSCH_CTYPE_GRP,
	CSCH_CTYPE_GRP_REF,
	CSCH_CTYPE_PEN,
	CSCH_CTYPE_max
} csch_ctype_t;

typedef enum csch_cmask_s {
	CSCH_CMASK_LINE      = (1 << CSCH_CTYPE_LINE),
	CSCH_CMASK_ARC       = (1 << CSCH_CTYPE_ARC),
	CSCH_CMASK_POLY      = (1 << CSCH_CTYPE_POLY),
	CSCH_CMASK_TEXT      = (1 << CSCH_CTYPE_TEXT),
	CSCH_CMASK_BITMAP    = (1 << CSCH_CTYPE_BITMAP),
	CSCH_CMASK_CONN      = (1 << CSCH_CTYPE_CONN),
	CSCH_CMASK_GRP       = (1 << CSCH_CTYPE_GRP),
	CSCH_CMASK_GRP_REF   = (1 << CSCH_CTYPE_GRP_REF),
	CSCH_CMASK_PEN       = (1 << CSCH_CTYPE_PEN),

	/* composites */
	CSCH_CMASK_ANY_GRP   = CSCH_CMASK_GRP | CSCH_CMASK_GRP_REF,
	CSCH_CMASK_ANY_ATOM  = CSCH_CMASK_LINE | CSCH_CMASK_ARC | CSCH_CMASK_POLY | CSCH_CMASK_TEXT | CSCH_CMASK_BITMAP,
	CSCH_CMASK_ANY       = 65535
} csch_cmask_t;

typedef enum csch_role_s {
	CSCH_ROLE_invalid,
	CSCH_ROLE_empty,
	CSCH_ROLE_BUS_NET,
	CSCH_ROLE_BUS_TERMINAL,
	CSCH_ROLE_HUB_POINT,
	CSCH_ROLE_SYMBOL,
	CSCH_ROLE_TERMINAL,
	CSCH_ROLE_WIRE_NET,
	CSCH_ROLE_JUNCTION,

	/* extensions: not in the data model */
	CSCH_ROLE_EXTOBJ_GFX
} csch_role_t;

typedef struct csch_comm_str_s {
	const char *str; /* sheet comm_str hash key */
} csch_comm_str_t;

struct csch_chdr_s {
	csch_rtree_box_t bbox; /* CACHE: first field to easy rtree handling */

	csch_oid_t oid;
	csch_ctype_t type;
	csch_cgrp_t *parent;
	csch_comm_str_t stroke_name, fill_name;  /* used to resolve pen */
	gdl_elem_t link; /* sheet's active or removed list */

	/* common properties (as per {des3:81}) */
	unsigned int lock:1;     /* deduced from the lock attribute */
	unsigned int floater:1;  /* deduced from the floater attribute */

	/*** cached ***/
	csch_cpen_t *stroke, *fill;
	csch_sheet_t *sheet;
	csch_displayer_t dsply;
	vtp0_t conn;       /* in which connections the current object participates */
	struct {
		csch_cgrp_t *g;  /* grp_ref that hosts transformations for this object */
		long idx;        /* transformation index within grpref's child_xform array */
	} grp_ref_xform;

	unsigned int indirect:1;

	/*** gui states (not saved) ***/
	unsigned int selected:1;
	unsigned int hilight:1;

	/** for integrity check and other sync plugins ***/
	unsigned mark:1;
	unsigned visit:1;
	void *mark_ptr;
};

typedef struct csch_extobj_impl_s csch_extobj_impl_t;

struct csch_cgrp_s { /* type=CSCH_CTYPE_GRP || CSCH_CTYPE_GRP_REF */
	csch_chdr_t hdr;
	minuid_bin_t uuid;      /* instance (changes on dup/copy) */
	htsp_t attr;
	htip_t id2obj;          /* members (memb): id -> object pointer (primary storage); also acts as a list of active children objects */
	htsp_t name2pen;        /* all pen directly under this group; key: pen->name; value: pen */
	char *loclib_name;      /* name used in the local library only */

	union {
		struct { /* type=CSCH_CTYPE_GRP */
			csch_oid_t next_id;
			minuid_bin_t src_uuid;  /* initial/source uuid (filled in on initial allocation, preserved on dup/copy) */
		} grp;
		struct { /* type=CSCH_CTYPE_GRP_REF */
			char *ref_str;           /* xorcache: will be NULL once ->grp is resolved */
			csch_cgrp_t *grp;        /* xorcache: NULL only when ref_str is not NULL */
			vtp0_t child_xform;      /* an array of child transformations (csch_child_xform_t) */
		} ref;
	} data;

	double spec_rot;
	csch_coord_t x, y;
	unsigned mirx:1, miry:1;

	/* cache */
	const char *srole; /* string version of the role: points into the attributes */
	csch_role_t role;  /* symbolic version, resolved once when the string is changed */
	struct {           /* accumulated transformations: parent's + local offset/rot/mirror */
		g2d_xform_t mx;  /* resulting matrix for quick coord transformations */
		double rot;
		csch_coord_t x, y;
		unsigned mirx:1, miry:1;
	} xform;
	unsigned wirenet_recalc_lock:1;
	unsigned wirenet_split_lock:1;
	unsigned sym_prefer_loclib:1;     /* this group is a symbol that should be put in the local lib and converted to a group ref if global config permits */
	vtl0_t aid;        /* link to the abstract objects compiled from this group; zero or one in flat design but can be more in hierarchic */
	char *file_name;   /* hint: trace file name from which the group was loaded, if available */
	csch_rtree_box_t bbox_flt; /* bounding box with floaters included (e.g. for sheet size) */
	const csch_extobj_impl_t *extobj; /* handler; not NULL if extobj attrib is set, but if implementation no found, all fields are NULL */
	long extobj_inhibit;        /* if >0 do not call extobj hooks */
};

typedef enum csch_sheet_type_e {
	CSCH_SHTY_unknown,
	CSCH_SHTY_UNLISTED, /* not explicitly listen in the project file, just happens to be in the same dir */
	CSCH_SHTY_ROOT,     /* explicitly listed in project file as a root sheet */
	CSCH_SHTY_AUX,      /* explicitly listed in project file as an aux sheet */
	CSCH_SHTY_EXTERNAL  /* not explicitly listen in the project file, loaded during compilation, for hierarchy */
} csch_sheet_type_t;

struct csch_sheet_s {
	rnd_design_t hidlib;
	long uid;

	csch_rtree_t dsply[CSCH_DSPLY_max];      /* object strokes to draw per display layer */
	csch_rtree_t dsply_fill[CSCH_DSPLY_max]; /* fills to draw per display layer */
	csch_cgrp_t direct, indirect; /* a sheet is really made of groups to keep the code simple */
	/* (sheet attributes are in the direct, the indirect group is for the "local library") */

	char *newname;  /* special case: if a new sheet is created, loadname will be NULL but we need to remember what we loaded for new sheet for revert to work; for non-new-sheets this is NULL but ->hidlib.loadname is non-NULL */
	char *loadfmt;  /* format request used at original load */
	csch_rtree_box_t bbox;
	unsigned bbox_changed:1; /* set when bbox changed (bumped) by object editing; used and reset by the GUI */

	unsigned non_graphical:1; /* sheet specified by data, not drawing */

	void *non_graphical_data; /* filled in and used by the plugin dealing with the non-graphical format when ->non_graphical==1 */
	csch_non_graphical_impl_t *non_graphical_impl;

	/* list of all objects ever allocated for the sheet */
	gdl_list_t active, deleted;

	csch_comm_str_t junction_pen_name; /* the caller may change this; used whenever junction needs to be placed in wirenet recalc */

	/* caches and temporary storage */
	uundo_list_t undo;
	htsp_t comm_str; /* common strings, like pen names, stored only once; key=strdup(str), val=NULL; referenceed as csch_comm_str_t */
	csch_oid_t auto_oid;  /* negative oids for objects that have automatically assigned oid; decrease first, use after */
	vtp0_t libs;          /* indexed by csch_lib_master_t->uid, resolves to (csch_lib_root_t *); each sheet needs an own set because of local configuration may differ */
	vtp0_t local_libs;    /* same as libs but operates on sheet->indirect */
	csch_chdr_t *currobj; /* for propset @ */

	csch_rtree_box_t invbox; /* an application may keep track on redraw bbox here - maintained by libcschem*/
	long redraw_inhibit;     /* when >0 do not draw but collect invalidated box - maintained by libcschem */
	unsigned invbox_valid:1; /* whether invbox is a valid box that could be drawn */
	unsigned saving:1;       /* set to 1 while the sheet is being saved (helps inhibiting some change indications on the UI) */
	unsigned warned_too_large:1; /* whether a sheet-too-large warning has already been generated for this sheet */

	struct {
		int recalc_inhibit;
		htpi_t recalc_wn;
	} util_wirenet;

	unsigned load_pending:1;     /* when set, the sheet has not yet been loaded (but got referenced from the project file) */
	unsigned loose_sym:1;        /* allow editing symbol part objects, bypassing the symbol lock */
	unsigned is_symbol:1;        /* if 1: we are in symbol editing mode */
	unsigned changed:1;          /* whether data has been edited since last save */
	csch_sheet_type_t stype;     /* sheet type/role within the project */

	char *design_dir;            /* for rc.paths.desgin */

	/* Autocomp timer */
	long acp_remain;             /* remaining time in milisec; 0 means not started */
	long acp_starting;           /* starting time in milisec; used to update the progress bar */

	/* plugins */
	double infobar_last_date;
};

/* Standard low level object operations; when int, return 0 for success */
typedef struct csch_cop_s {
	csch_chdr_t *(*remove)(csch_sheet_t *dst, csch_chdr_t *obj);
	csch_chdr_t *(*dup)(csch_chdr_t *obj);
	csch_chdr_t *(*move)(csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy);
	csch_chdr_t *(*move_to)(csch_sheet_t *dst, csch_chdr_t *obj);
	csch_chdr_t *(*rotate90)(csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy, int steps);
	csch_chdr_t *(*rotate)(csch_chdr_t *obj, csch_coord_t dx, csch_coord_t dy, double ang, double cosx, double sinx);
	csch_chdr_t *(*mirrorx)(csch_chdr_t *obj, csch_coord_t ox); /* mirror x coordinates (around the y axis) */
	csch_chdr_t *(*mirrory)(csch_chdr_t *obj, csch_coord_t oy); /* mirror y coordinates (around the x axis) */
} csch_cop_t;

typedef enum csch_hash_ignore_u {        /* bitfield: which properties to ignore in objects hashing */
	CSCH_HIGN_FLOATER_GEO   = 1,           /* recursively ignore floater geometry (position, transformations) */
	CSCH_HIGN_GRP_ATTRIB    = 2,           /* ignore root group's attributes */
	CSCH_HIGN_GRP_PLACEMENT = 4            /* ignore root group's placement */
} csch_hash_ignore_t;

csch_sheet_t *csch_sheet_init(csch_sheet_t *sheet, csch_project_t *parent);
void csch_sheet_uninit(csch_sheet_t *sheet);

csch_sheet_t *csch_sheet_alloc(csch_project_t *parent);
void csch_sheet_free(csch_sheet_t *);
RND_INLINE const csch_rtree_box_t *csch_sheet_bbox(csch_sheet_t *sheet);
void csch_sheet_bbox_update(csch_sheet_t *sheet);

csch_oid_t csch_oid_new(csch_sheet_t *sheet, csch_cgrp_t *grp);

/* Return the human readable name of a display layer, or "<invalid>" */
const char *csch_dsply_name(csch_displayer_t dsply);

/* Return the sheet's rtree for a name; the name may be suffixed with
   -fill or -stroke for selecting the appropriate rtree. If not suffixed,
   the stroke rtree is returned. */
csch_rtree_t *csch_rtree_lookup_by_name(csch_sheet_t *sheet, const char *name);

/* Return the human readable name of concrete object type, or "<invalid>" */
const char *csch_ctype_name(csch_ctype_t typ);

/* Copy header metadata (like display layer and pen OID) while dupping
   an object */
void csch_chdr_copy_meta4dup(csch_chdr_t *dst, const csch_chdr_t *src);

/* Return the full oidpath of chdr renderd in text; caller needs to free
   the returned string */
char *csch_chdr_to_oidpath_str(const csch_chdr_t *chdr);

/* An object is selected if it is ->selected or if any of its parents is ->selected */
RND_INLINE int csch_chdr_is_selected(csch_chdr_t *obj);

/* An object is selected if it is ->selected or if any of its parents is ->selected */
RND_INLINE int csch_chdr_any_parent_selected(csch_chdr_t *obj);

/* Return common string for str; if not already allocated and alloc != 0,
   strup and insert str to common strings. If str is NULL or not found/allocated,
   returns a comm_str with NULL str */
csch_comm_str_t csch_comm_str(csch_sheet_t *sheet, const char *str, int alloc);


/*** implementation ***/
RND_INLINE const csch_rtree_box_t *csch_sheet_bbox(csch_sheet_t *sheet)
{
	if (csch_bbox_is_invalid(&sheet->bbox))
		csch_sheet_bbox_update(sheet);
	return &sheet->bbox;
}

RND_INLINE int csch_chdr_is_selected(csch_chdr_t *obj)
{
	for(;obj != NULL; obj = &obj->parent->hdr)
		if (obj->selected)
			return 1;

	return 0;
}

RND_INLINE int csch_chdr_any_parent_selected(csch_chdr_t *obj)
{
	return csch_chdr_is_selected(&obj->parent->hdr);
}


/* Returns 1 if obj is a group or group ref, 0 for other objects */
RND_INLINE int csch_obj_is_grp(const csch_chdr_t *obj)
{
	return ((obj != NULL) && ((obj->type == CSCH_CTYPE_GRP) || (obj->type == CSCH_CTYPE_GRP_REF)));
}

/* Returns 1 if obj is deleted (not part of the active objects of the sheet) */
RND_INLINE int csch_obj_is_deleted(const csch_chdr_t *obj)
{
	return (obj->link.parent == &obj->sheet->deleted);
}

#define csch_ctype_in_cmask(type, mask)  (!!((1 << (type)) & (mask)))

#define CSCH_ACT_SHEET ((csch_sheet_t *)argv[0].val.argv0.user_call_ctx)

/* Call with new_val=1 after any sheet edit */
void csch_sheet_set_changed(csch_sheet_t *sheet, int new_val);


#endif
