/**
	\file "net/netgraph.h"
	$Id: netgraph.h,v 1.2 2009/08/28 20:45:11 fang Exp $
 */

#ifndef	__HAC_NET_NETGRAPH_H__
#define	__HAC_NET_NETGRAPH_H__

#include <vector>
#include <string>
#include <map>
#include <set>
#include "net/common.h"
#include "Object/lang/cflat_context_visitor.h"
#include "Object/lang/PRS_footprint_expr.h"	// for precharge_ref_type

namespace HAC {
namespace NET {
using std::vector;
using std::string;
using std::map;
using std::set;
using std::pair;
using entity::cflat_context_visitor;
using entity::state_manager;
using entity::footprint;
using entity::global_entry;
using entity::bool_tag;
using entity::process_tag;
using entity::PRS::footprint_expr_node;

typedef	size_t		index_type;
typedef	double		real_type;
class netlist;
class local_netlist;
class netlist_generator;
struct netlist_options;

// 0-indexed nodes or 1-indexed?
// extern const index_type	base_index = 1;

//=============================================================================
/**
	Extension of (local) node information.  
	Corresponds to an electrical node in the netlist. 
	Bother with redundant connectivity list?
 */
struct node {
	struct __logical_node_tag { };
	struct __supply_node_tag { };
	struct __internal_node_tag { };
	struct __auxiliary_node_tag { };
	static const __supply_node_tag		supply_node_tag;
	static const __logical_node_tag		logical_node_tag;
	static const __internal_node_tag	internal_node_tag;
	static const __auxiliary_node_tag	auxiliary_node_tag;
	/**
		Index may correspond to index in local bool instance pool.
		For internal nodes, they correspond to the footprint's
		local internal index (pooled).
	 */
	index_type			index;
	/**
		Name of node.
		Named nodes inherit their names from canonical names
		in the original local definition, possibly mangled.  
		Need disambioguous ways of naming internal and 
		auxiliary nodes.
		Internal nodes just (optionally) copy this over
		from their footprint.  
	 */
	string				name;
	/**
		Logical nodes are those named in the original source.
		Internal nodes are special named nodes used to
		construct shared node networks.
		Supply nodes are implicit poewr supply nodes.
		Auxiliary nodes are those generated by subexpressions 
		(unnamed), and *cannot* cross subcircuit boundaries.  
	 */
	enum {
		NODE_TYPE_LOGICAL = 0,
		NODE_TYPE_INTERNAL = 1,
		NODE_TYPE_AUXILIARY = 2,
		NODE_TYPE_SUPPLY = 3
	};
	// is_named -- if this was a named node in original source, 
	//	otherwise is internal, auxiliary node.
	char				type;
	/**
		set to true if node participates in any device 
		(i.e. has any electrical connectivity whatsoever), 
		so unused nodes (e.g. in ports) can be skipped.  
	 */
	bool				used;
	// connectivity information needed? would be redundant with devices

	node(const string& s, const __supply_node_tag&) : 
		index(0), name(s), type(NODE_TYPE_SUPPLY), used(false) { }
	node(const index_type i, const __logical_node_tag&) : 
		index(i), name(), type(NODE_TYPE_LOGICAL), used(false) { }
	node(const index_type i, const __internal_node_tag&) : 
		index(i), name(), type(NODE_TYPE_INTERNAL), used(false) { }
	node(const __auxiliary_node_tag&) : 
		index(0), name(), type(NODE_TYPE_AUXILIARY), used(false) { }
	// only for VOID node
	node(const char* s, const __auxiliary_node_tag&) : 
		index(0), name(s), type(NODE_TYPE_AUXILIARY), used(false) { }
	node(const index_type a, const __auxiliary_node_tag&) : 
		index(a), name(), type(NODE_TYPE_AUXILIARY), used(false) { }


	bool
	is_logical_node(void) const { return type == NODE_TYPE_LOGICAL; }

	bool
	is_internal_node(void) const { return type == NODE_TYPE_INTERNAL; }

	// true if ndoe is automatically generated
	bool
	is_auxiliary_node(void) const { return type == NODE_TYPE_AUXILIARY; }

	bool
	is_supply_node(void) const { return type == NODE_TYPE_SUPPLY; }

	ostream&
	emit(ostream&, const footprint&) const;

	ostream&
	dump_raw(ostream&) const;

};	// end struct node

//-----------------------------------------------------------------------------
/**
	Standard 4-terminal device for transistor.
 */
struct transistor {
	enum fet_type {
		NFET_TYPE,
		PFET_TYPE
	};
	/**
		Devices are somehow named after the rules from which
		they are derived.  
		TODO: need auto-enumeration
	 */
	string				name;
	index_type			source;
	index_type			gate;
	index_type			drain;
	/**
		Substrate contact.
	 */
	index_type			body;
	/// device width parameter
	real_type			width;
	/// device length parameter
	real_type			length;
	/// device type: nfet, pfet
	char				type;
	// TODO: support overriding of device type
	// e.g. hvt, lvt, svt...

	// allow a conf/tech file to define/enumerate additional types
	// attributes:
	// is_standard_keeper
	// is_combination_feedback_keeper
	enum flags {
		DEFAULT_ATTRIBUTE = 0x0,
		IS_PRECHARGE = 0x01,
		IS_STANDARD_KEEPER = 0x02,
		IS_COMB_FEEDBACK = 0x04
	};
	char				attributes;

	template <class NP>
	void
	mark_used_nodes(NP&) const;

	template <class NP>
	ostream&
	emit(ostream&, const NP&, const footprint&, 
		const netlist_options&) const;

	ostream&
	emit_attribute_suffixes(ostream&) const;

	ostream&
	dump_raw(ostream&) const;

};	// end struct transistor

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/**
	Generic 2-terminal device.
	Capacitor, resistor, or inductor.
 */
struct passive_device {
	/**
		Points to the two terminals.  
	 */
	index_type			t[2];
	// enum type: (R) resistor, (L) inductor, (C) capacitor
	// (D) diode should be separate as it can be tied to a model
	char				type;
};	// end struct device

//-----------------------------------------------------------------------------
/**
	Instantiation of a particular substructure type.
	spice card: x
 */
struct instance {
	/**
		Reference to subcircuit type.
		Translated from looking up netlist_map_type in the generator
		class, below.
		Can point to either a local subcircuit or externally
		defined subcircuit (associated to defproc).
		Beware of storing insertion-invalidated pointers.
	 */
	const netlist*			type;
	/**
		Local process index.
	 */
	index_type			pid;
	/**
		Port connections.
	 */
	typedef	vector<index_type>	actuals_list_type;

	actuals_list_type		actuals;

	explicit
	instance(const netlist& t, const index_type p) :
		type(&t), pid(p), actuals() { }


	bool
	is_empty(void) const;

	template <class NP>
	ostream&
	emit(ostream&, const NP&, const footprint&) const;

	template <class NP>
	void
	mark_used_nodes(NP&) const;

	ostream&
	dump_raw(ostream&) const;

};	// end struct instance


//-----------------------------------------------------------------------------
/**
	Structures common to all netlists.  
 */
struct netlist_common {
	/**
		TODO: group transistors in a way that reflects
		original rule in PRS, if applicable.
		For now, flat list.
		TODO: print comment with origin rule before each
		group of transistors.
	 */
	typedef	vector<transistor>	transistor_pool_type;
	typedef	vector<passive_device>	passive_device_pool_type;
	transistor_pool_type		transistor_pool;
	passive_device_pool_type	passive_device_pool;

	bool
	is_empty(void) const;

	template <class NP>
	void
	mark_used_nodes(NP&) const;

	template <class NP>
	ostream&
	emit_devices(ostream&, const NP&, const footprint&, 
		const netlist_options&) const;

	ostream&
	dump_raw_devices(ostream&) const;

};	// end class netlist_common

//-----------------------------------------------------------------------------
/**
	Minimum requirements for any type of netlist.  
	Intended for use with subcircuits.
 */
struct local_netlist : public netlist_common {
	// does not have own list of internal nodes, defer to parent's
	// keep a local subset of nodes used, indexing into parent's collection
	// will use this
	typedef	set<index_type>		node_index_map_type;

	// name of local subcircuit, or just suffix, like :subs_0
	string				name;
	/**
		Mapping from parent node index to local node index.  
		Expected to be a sparse subset of parent's list.  
		Every node in this list will be a port of the 
		subcircuit definition.  (Maybe eventually optimize?)
		key= parent domain node index
	 */
	node_index_map_type			node_index_map;

	// does NOT have local subinstances, only devices
	template <class NP>
	void
	mark_used_nodes(NP&);

	// requires a parent netlist for reference
	ostream&
	emit_definition(ostream&, const netlist& n,
		const netlist_options&) const;
	ostream&
	emit_instance(ostream&, const netlist& n,
		const netlist_options&) const;

	ostream&
	dump_raw(ostream&, const netlist&) const;
};	// end class local_netlist

//-----------------------------------------------------------------------------
/**
	A graph.
	One netlist corresponds to a subcircuit, or in HAC a unique footprint.  
	TODO: support for special global nodes like Vdd, GND?
		can be automatic or overridden.
	Beware of using vectors and accidentally invalidating references
	and iterators after tail-insertion, which may cause reallocation.  
 */
class netlist : public netlist_common {
	typedef	entity::PRS::footprint	prs_footprint;
	/**
		Keep around footprint for node reference and printing.
	 */
	const footprint*		fp;
public:
// universal node indices to every subcircuit
	static const node void_node;	// not a real node
	static const node GND_node;
	static const node Vdd_node;

	// these should correspond with the order of insertion in netlist's ctor
	static const	index_type	void_index;
	static const	index_type	GND_index;
	static const	index_type	Vdd_index;

friend class local_netlist;
friend class netlist_generator;
	/**
		Collection of physical nodes, can be from logical nodes, 
		internal nodes, or auxiliary nodes, or supply nodes.  
	 */
	typedef	vector<node>		node_pool_type;
	/**
		first: local node index
		second: owner subcircuit index (1-indexed)
	 */
	typedef	pair<index_type, index_type> internal_node_entry_type;
	/**
		Maps the local footprint's internal node to 
		the netlist's generic node index.  
		index: footprint's internal node index
	 */
	typedef	vector<internal_node_entry_type>
					internal_node_map_type;
	/**
		Reverse-map for looking up by expression id.  
		key = prs footprint's local expression index
		value = footprint-index of internal node
	 */
	typedef	map<index_type, index_type>
					internal_expr_map_type;
	/**
		index= local footprint index of node
		value= netlist index of node
		Information is somewhat redundant, but provides
		a reverse map.  
		0-value means node has not been used yet.  
	 */
	typedef	vector<index_type>	named_node_map_type;
	/**
		For local subcircuits only.  
		Such local circuits are not shared outside of this definition
		and are only instantiated once.  
		Not using map because subcircuits may be anonymous. 
	 */
	typedef	vector<local_netlist>	local_subcircuit_list_type;
	typedef	vector<instance>	instance_pool_type;
	/**
		Indices refer to local nodes which need not be the 
		exact same set of local port aliases.
		May include Vdd, GND.
		Internal nodes cannot be ports.  
	 */
	typedef	vector<index_type>	port_list_type;
private:
	/**
		Name can be inferred from the hierarchical name 
		of the footprint.  Will have to be mangled 
		so we save the result.  
	 */
	string				name;
	named_node_map_type		named_node_map;
	/**
		All local nodes, including ports and internal nodes.
		This is only stored in the primary netlist, 
		whereas the local subcircuit netlists use indirect
		references to parents' nodes.  
	 */
	node_pool_type			node_pool;
	instance_pool_type		instance_pool;
	internal_node_map_type		internal_node_map;
	internal_expr_map_type		internal_expr_map;
	local_subcircuit_list_type	local_subcircuits;
	/**
		List of local node indices.  
		NOTE: there cannot be any aliases in the ports.  
		Does not include GND and Vdd, which are handled separately.
	 */
	port_list_type			port_list;
	/**
		If this definition is empty, don't bother emitting it
		or instances of it.  
		This is also empty if all instances are also empty.  
		This is set by summarize_ports(), which should be the
		last method called during netlist processing.  
	 */
	bool				empty;
	/**
		Running count of generated auxiliary nodes.
	 */
	index_type			aux_count;
	/**
		Running count of unnamed subcircuits.
		This is only used as a local variable, can toss it...
	 */
	index_type			subs_count;

public:
	netlist();
	~netlist();

	void
	bind_footprint(const footprint&, const netlist_options&);

	void
	bind_footprint(const footprint&, const string&);

	const string&
	get_name(void) const { return name; }

	bool
	is_empty(void) const { return empty; }

	index_type
	create_auxiliary_node(void);

	index_type
	create_internal_node(const index_type ni, const index_type ei);

	index_type
	lookup_internal_node(const index_type i) const;

	index_type
	register_named_node(const index_type);

	bool
	named_node_is_used(const index_type) const;

	void
	append_instance(const global_entry<process_tag>&, const netlist&, 
		const index_type);

	void
	summarize_ports(void);

	ostream&
	emit(ostream&, const bool s, const netlist_options&) const;

	ostream&
	dump_raw(ostream&) const;

private:
	void
	mark_used_nodes(void);

	void
	__bind_footprint(const footprint&);

};	// end class netlist

//=============================================================================

}	// end namespace NET
}	// end namespace HAC

#endif	// __HAC_NET_NETGRAPH_H__

