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

#include <stdio.h>

#include <genht/htip.h>
#include <genht/htsp.h>
#include <genht/htpp.h>
#include <genlist/gendlist.h>
#include <genvector/vtp0.h>

#include "libcschem/common_types.h"
#include "libcschem/attrib.h"
#include "TODO.h"


typedef struct csch_hlevel_s csch_hlevel_t;
typedef struct csch_hier_path_s csch_hier_path_t;

/* A single level of hierarchy storing local objects with short (local) names;
   objects are not allocated, just referenced from the global (flat) abstract
   model (csch_abstract_t) */
struct csch_hlevel_s {
	htsp_t nets;         /* local netname -> csch_anet_t */
	htsp_t comps;        /* local name -> csch_acomp_t */

	char *name;
	int hdepth;

	csch_cgrp_t *sym;      /* the sheetref symbol that created this level (NULL in root) */
	csch_hlevel_t *parent; /* NULL in root */
	gdl_elem_t link;     /* doubly linked list of siblings; not used in root */
	gdl_list_t children; /* of (csch_hlevel_t *) */
};

struct csch_abstract_s {
	htip_t aid2obj;    /* primary storage: global aid -> (csch_ahdr_t *) */
	htul_t uid2id;     /* UID -> object id (cache) */
	htsp_t nets;       /* global netname -> csch_anet_t */
	htsp_t comps;      /* global name -> csch_acomp_t */
	vtp0_t orphaned_ports; /* ports without a component parent; typical for sheet ports in a hierarchy */
	long next_aid;

	/* counters for unique names */
	struct {
		long wirenet, comp, port;
		long target_tmp; /* next id for temporary object created by the target plugin(s) - post-increment */
	} ucnt;

	/* compilation state */
	unsigned new_comps:1; /* set to 1 whe a new component is created */
	unsigned new_nets:1;  /* set to 1 whe a new net is created */
	unsigned new_ports:1; /* set to 1 whe a new port is created */
	csch_project_t *prj;  /* only during compilation */
	long view_id;         /* which view the abstract model is compiled for; only during compilation */
	htpp_t eng_transient; /* private data used during compilation by engines; typically set in the project_before hook and removed/freed in the project_after hook; key is a const char *cookie */
	long comp_cnt;        /* each time a compilation start this counter is increased by one; useful to track if a cache is out of sync with an abstract model */

	/* hierarchy */
	csch_hlevel_t *hroot; /* the common hierarchy level of all root sheets */
};

typedef enum csch_atype_e {
	CSCH_ATYPE_INVALID = 0,
	CSCH_ATYPE_NET,
	CSCH_ATYPE_BUSNET,
	CSCH_ATYPE_BUSCHAN,
	CSCH_ATYPE_PORT,
	CSCH_ATYPE_BUSPORT,
	CSCH_ATYPE_COMP,
	CSCH_ATYPE_HUB
} csch_atype_t;
const char *csch_atype_name(csch_atype_t type);

typedef enum csch_ascope_e {   /* implements {des6:8} */
	CSCH_ASCOPE_unknown = -1,
	CSCH_ASCOPE_GLOBAL=1,        /* syntax: /name */
	CSCH_ASCOPE_SUBTREE_LOCAL,   /* syntax: v/name */
	CSCH_ASCOPE_SHEET_LOCAL,     /* syntax: ./name */
	CSCH_ASCOPE_AUTO,            /* syntax: name */
	CSCH_ASCOPE_SUBTREE_AUTO     /* syntax: ^/name */
} csch_ascope_t;


struct csch_ahdr_s {
	csch_atype_t type;
	csch_attribs_t attr;
	long aid;
	vtp0_t srcs;              /* list of pointers to concrete objects that were source to this abstract object */
	csch_abstract_t *abst;
	const char *ghost;        /* if not NULL, the given object is only an empty shell (got used once during the compilation, but was emptied later, probably merged into something else) */
	unsigned compiled:1;      /* already compiled, compiler should skip it */

	/* cached from attributes to speed up render */
	unsigned dnp:1;           /* attribute display/dnp: do not populate */
	unsigned omit:1;          /* attribute display/dnp: omit from export */
};

typedef struct csch_acomp_s csch_acomp_t;


/* type = CSCH_ATYPE_NET */
typedef struct csch_anet_s {
	csch_ahdr_t hdr;
	vtp0_t conns; /* a list of (csch_ahdr_t *) connections, ports and bus-ports */
	char *name;   /* global unique name for the hash (flat model); includes hierarchic path of some sort */
	char *name_loc; /* local name within the hierarchic level (doesn't include hierarchic path) */

	/* hierarchy */
	csch_ascope_t scope;  /* coming from name prefix */
	csch_hlevel_t *hlev;  /* subtree the net is in */


	unsigned no_uname:1; /* 1 if there is no user assigned net name */
	unsigned term_term:1; /* the net was created for a "hidden" terminal-terminal connection */
	long hdepth;          /* how deep the net is in the hierarchy; 0 means it's on a root sheet */

	/* cached from attributes: */
	const char *chan;
} csch_anet_t;


/* type = CSCH_ATYPE_BUSCHAN */
typedef struct csch_anet_s csch_abuschan_t;

/* type = CSCH_ATYPE_BUSNET */
typedef struct csch_abusnet_s {
	csch_ahdr_t hdr;
	htsp_t *chans; /* of (csch_abuschan_t *), key is ->chan */

	/* cached from attributes: */
	const char *name;
} csch_abus_t;

/* type = CSCH_ATYPE_PORT */
typedef struct csch_aport_s csch_aport_t;
struct csch_aport_s {
	csch_ahdr_t hdr;
	csch_acomp_t *parent;
	char *name;

	struct {
		csch_anet_t *net; /* net the port is connected to */
		/* TODO: bus */
	} conn;

	/* cached from attributes: */
	const char *chan;
	unsigned sheet_port:1;       /* set to 1 if this port is directly on the sheet (hierarchy) */
	csch_acomp_t *referer;       /* if sheet_port is 1, this is the referer component on the parent sheet; this component is a subsheet component */
	csch_aport_t *subsheet_port; /* port is on a subsheet symbol; this field shows which port it is connected to within the subsheet */
};

/* type = CSCH_ATYPE_BUSPORT */
typedef struct csch_abusport_s {
	csch_ahdr_t hdr;
	csch_acomp_t *parent;

	/* cached from attributes: */
	const char *name;
	void *chan_rewrite;
} csch_abusport_t;

/* type = CSCH_ATYPE_COMP */
struct csch_acomp_s {
	csch_ahdr_t hdr;
	char *name;      /* global unique name for the hash (flat model); includes hierarchic path of some sort */
	char *name_loc;  /* local name within the hierarchic level (doesn't include hierarchic path) */

	/* hierarchy */
	csch_ascope_t scope;  /* coming from name prefix */
	csch_hlevel_t *hlev;  /* subtree the component is in */
	long hdepth;          /* how deep the comp is in the hierarchy; 0 means it's on a root sheet */

	htsp_t ports;    /* of csch_aport_t, key is ->name */
	htsp_t busports; /* of csch_abusport_t, key is ->name */

	csch_sheet_t *child_sheet; /* set when symbol is referencing a hierarchic sheet down */

	/* cache/temporary for compilation */
	unsigned postprocessed:1;
};

/* type = CSCH_ATYPE_HUB */
typedef struct csch_ahub_s {
	csch_ahdr_t hdr;
} csch_ahub_t;


/*** abs ***/
void csch_abstract_init(csch_abstract_t *abs);
void csch_abstract_uninit(csch_abstract_t *abs);
void csch_abstract_dump(const csch_abstract_t *abs, FILE *f, const char *prefix);

/*** generic ***/
/* Modify name ptr to remove prefix and return the scope specified in the prefix */
csch_ascope_t csch_split_name_scope(const char **name);


/*** net ***/
csch_anet_t *csch_anet_get(csch_abstract_t *abs, const char *netname); /* does NOT handle hierarchy (prefixed netnames) */
csch_anet_t *csch_anet_get_at(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *netname);
csch_anet_t *csch_anet_new(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *name_glob, const char *name_loc, int set_no_uname);

/*** component and port ***/
csch_acomp_t *csch_acomp_get(csch_abstract_t *abs, const char *name);  /* does NOT handle hierarchy (prefixed netnames) */
csch_acomp_t *csch_acomp_get_at(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *name);
csch_acomp_t *csch_acomp_new(csch_abstract_t *abs, csch_hlevel_t *hlev, csch_ascope_t scope, const char *name_glob, const char *name_loc);

csch_aport_t *csch_aport_get(csch_abstract_t *abs, csch_acomp_t *comp, const char *name, int alloc);

/* return aport's parent name or <sheet> if it's global */
#define APORT_PARENT_NAME(ap) (((ap)->parent == NULL) ? "<sheet>" : (ap)->parent->name)

#endif
