#include <list>
#include <iostream>
#include <map>
#include <set>
#include <algorithm>


#ifndef _GATE_
#define _GATE_

typedef enum Type_of_gate {	G_AND, G_OR, G_NAND, G_NOR, G_XOR, G_XNOR,
							G_BUF, G_INV};

typedef std::map<int, int> Row;			//obsahuje pouze vstupy na kterych zavisi
typedef std::list<Row*> True_table;		//seznam radku pravdivostni tabulky LUTu

class Gate;

class Lut {
public:
	
	int id;
	std::string name;
	True_table* true_table;
	std::list<int> inputs;
	std::list<Gate*> names_inputs;	
	std::set<Lut*> input_luts;
	unsigned int depth;
	~Lut();
	unsigned int spoctiHloubku ();
};

class Gate {
private:	
	static unsigned int c_id;					// staticka promenna pro id jednotlivych hradel
	
	unsigned int id;							// identifikator hradla
	unsigned int depth;							// hloubka hradla v obvodu
	unsigned int depth_of_lut;					// hloubka LUTu ve vyslednem reseni
	bool k_bounded;								// priznak, zda li uz byl ohranicen
	std::string name;							// nazev hradla (pokud se vytvari nove, tak obsahuje "L+id uzlu"
	Type_of_gate type;							// typ hradla
	
	std::list <Gate*> inputs;					// vstupni hradla
	std::list <Gate*> outputs;					// vystupni hradla
	std::list <std::string> name_of_inputs;		// seznam nazev hradel vstupnich hradel...pri nacitani obvodu ze souboru dojde nejdrive k nacteni techto retezcu a teprve potom k provazani hradel
	
	void merge_tables(True_table* t, True_table* t1, True_table* t2);
	bool merge_rows (Row& r, Row* r1, Row* r2);

public:
	static unsigned int odstraneno;
	Gate (char* _name, bool _k_bounded);								// pro vytvareni noveho hradla pri nacitani obvodu
	Gate (bool _k_bounded);												// pro vytvareni novych hradel pri dekompozici do K-vhodne site, jako nazev je pouzito "Lid", kde id je id hradla


	
	void add_input			(Gate* gat);								// prida predchudce hradla do seznamu vstupu
	void add_output			(Gate* gat);								// prida naslednika hradla do seznamu vystupu
	void add_name_of_input	(char* name);								// prida dalsi nazev vstupu
	void set_depth			(unsigned int D);			 				// nastavi hloubku hradla na D
	void set_k_bounded		(void) {k_bounded = true;};					// nastavi priznak, za hradlo je K-ohraniene
	void set_type			(Type_of_gate _type);						// nastavi typ hradla

	unsigned int get_depth			(void) const;						// vrati hloubku hradla v obvodu
	unsigned int get_depth_of_lut	(void) const;						// vrati hloubku LUTu ve vyslednem reseni
	unsigned int get_id				(void) const;						// !!!!!!!!!!!!! toto by melo zmizet

	bool is_k_bounded(void) const {return k_bounded;};					// jestli uz bylo prevedeno na K-ohranicenu sit
	
	Type_of_gate get_type (void) const;									// vrati typ hradla
	True_table* compute_true_table(const std::set<Gate*>& c, int hodn);				// vrati pravdivostni tabulku LUTu pomoci rezu (mnoziny) c, hodn je pozadovana hodnota (0 nebo 1)
	std::string get_name (void) const {return name;};					// vrati nazev hradla				
	const std::list<std::string>& get_names_of_inputs(void) {return name_of_inputs;};  // vrati list retezcu vstupu hradla

	std::list<Gate*>& get_inputs(void) {return inputs;};				//vrati list ukazatelu na predchudce hradla
	std::list<Gate*>& get_outputs(void) {return outputs;};				//vrati list ukazatelu na nasledniky hradla		

	void add_inputs (std::set<Gate*>& vstupy, unsigned int K);
};
#endif
