#ifndef _BOOLGRAPH_GRAPH
#define _BOOLGRAPH_GRAPH
#define SPACES " \t\r\n|"

#include <vector>
#include <list>
#include <string>
using namespace std;

typedef enum {undefined,f,fd,fr,fdr} graphtype;

class GVariable;

class GTerm //term - uzel
{
private:
	string inputs; //vstupy
	string outputs; //vystupy
	int count_on, count_off, count_dc; //pocty promennych v ruznych polaritach
	list<GVariable*> vars_on; //seznam promennych v pozitivni polarite
	list<GVariable*> vars_off; //v negativni polarite
	list<GVariable*> vars_dc; //neobsazenych v termu
public:
	GTerm::GTerm(string &_inputs, string &_outputs, vector<GVariable*> &variables); 
	friend class Graph;
	//konstruktor vytvari term a rika, jaky ma vztah k jednotlivym variables (to je ve stringu)
	string GetIn() const; //vraci string se vstupy
	char GetIn(const int numinput) const; //vraci vstup [output]
	string GetOut() const; //vraci string s vystupy
	char GetOut(const int numoutput) const; //vraci vystup cislo [output] tohoto termu
	void SetOut(const int numoutput, char set); //nastavuje vystup cislo [output] tohoto termu
	int GetCount_dc() const; //vraci pocet neobsazenych promennych v termu
	list<GVariable*> GetVars(const char set) const; //vraci nektery seznam promennych
	bool Satisfy(const string &_variables) const; //urcuje, zda term splnuje konfiguraci promennych podle variables
	bool operator>(const GTerm &druhy); //term1>term2, pokud term1 pokryva vetsi cast stavoveho prostoru
	bool operator<(const GTerm &druhy); //term1<term2, pokud term1 pokryva mensi cast stavoveho prostoru
	bool operator>=(const GTerm &druhy); 
	bool operator<=(const GTerm &druhy); 
	friend ostream& operator<< (ostream &os, const GTerm &t); //operator pro vystup do proudu generuje radek do PLA
};

class GVariable //promenna - hrana
{
private:
	const int number; //poradove cislo promenne
	struct Outputs //struktura pro vicevystupovou logiku
	{
		struct ONset //onset termy
		{
			list<GTerm*> term_on; //seznam termu, ktere maji tuto promennou obsazenu v prime forme
			list<GTerm*> term_off; //seznam termu, ktere maji tuto promennou obsazenu negovanou
			list<GTerm*> term_dc; //seznam termu, ktere ji nemaji obsazenou
		} onset;
		struct OFFset //offset termy
		{
			list<GTerm*> term_on; //seznam termu, ktere maji tuto promennou obsazenu v prime forme
			list<GTerm*> term_off; //seznam termu, ktere maji tuto promennou obsazenu negovanou
			list<GTerm*> term_dc; //seznam termu, ktere ji nemaji obsazenou
		} offset;
		struct DCset //dont care
		{
			list<GTerm*> term_on; //seznam termu, ktere maji tuto promennou obsazenu v prime forme
			list<GTerm*> term_off; //seznam termu, ktere maji tuto promennou obsazenu negovanou
			list<GTerm*> term_dc; //seznam termu, ktere ji nemaji obsazenou
		} dcset;
		int count_on, count_off, count_dc; //pocty vystupu ruznych termu
	}* outputs;
public:
	GVariable::GVariable(const int numoutputs, const int counter);
	GVariable::~GVariable();
	friend class Graph;
	int GetNum() const; //vraci poradove cislo promenne
	void AddTermFromPLALine(GTerm *term, const char &relation); //prirazuje promenne ukazatele na term a vztah, v jakem je promenna k termu
	void RemoveTerm(GTerm *term, const char &relation); //odstranuje zaznam vazby promenne na tento term
	void RemoveTermOutput(GTerm *term, const char &relation, const int output, const char old_output); //odstranuje zaznam vazby promenne na tento term ve vystupu output
	void AddTermOutput(GTerm *term, const char &relation, const int num_output, const char new_output); //navazuje nove vazby na tento term ve vystupu output
	list<GTerm*> GetTerms(const int output, const char set, const char list) const; //navraci jeden ze seznamu termu
};

class Graph //trida obsahujici celou definici grafu
{
	list<GTerm*> terms; //jednotlive termy (odpovidaji uzlum v grafu)
	struct Outputs //struktura pro vice vystupovou logiku
	{
		list<GTerm*> terms_on;		 //onset
		list<GTerm*> terms_off;	 //offset
		list<GTerm*> terms_dc;		 //dcset
		list<GTerm*> terms_nm; //no meaning
	}* outputs;

	vector<GVariable*> variables; //jednotlive promenne (odpovidaji hranam v grafu)
	int count_inputs; //pocet promennych (vstupu)
	int count_outputs; //pocet vystupu
	int count_terms; //pocet termu
	graphtype type; //typ PLA
	string input_names, output_names; //uloziste pro jmena vstupnich a vystupnich promennych (pouziva se pri generovani PLA)
	void SetParams(const graphtype &_type, const int &numinputs, const int &numoutputs, const string &_input_names, const string &_output_names); //nastaveni parametru
	GTerm* ConstructFromPLALine(string &_inputs, string &_outputs); //parsuje jednu radku s termem (z PLA souboru) a postupne konstruuje graf
	void RemoveTerm(GTerm *term); // odstrani z grafu uzel s termem a veskere odkazy na nej
	void RemoveTermOutput(GTerm *term, const int output); //odstrani u termu jeden vystup
	GTerm* AddTermOutput(string &t_input, const int &num_output, char output); //pokud term s temito vstupy existuje a na vystupu num_output ma no-meaning, zada tam novy vstup a vrati ukazatel na nej, pokud existuje se stejnym stavem na vystupu, vrati null, pokud term jeste neexistuje, vytvori ho a vrati ukazatel na novy term
	void AddTermOutput(GTerm *target, const int &num_output, char output); 
	void SetTermOutput(GTerm *term, const int &num_output, char output); 
	list<string> GenerateMatrix(); //nageneruje radky se vstupy termu

	void Simplification(const int num_output);
	void SimplificationWithDontCares(const int num_output);
	void SimplificationWithDontCares(const int num_output, list<GTerm*> &essentials);
	void Absorbtion_OrthRespect(); //zjednodusuje termy absorbci, ale jen nektere - tak, ze zustavaji ortogonalni
	void Absorbtion_OrthRespect(const int num_output);
	list<GTerm*> AddTermsRemoveHazardsWithDontCares(const int output, const bool find_essential); // vytvari nove termy, ktere spojuji termy stavajici (redundantni termy)
	void SimplificationWithDontCares(); //totez co simplific, ale vyuziva DC

	string BruteForceTautology(const int output) const; //resi problem tautologie hrubou silou z hlediska jednoho vystupu
	string BranchAndBoundTautology(const int output) const; //resi problem tautologie metodou vetvi a hranic z hlediska jednoho vystupu
	list<string> Complement(const int output); //vytvari komplement
	void Minimization(const int num_output, const bool deep); //minimalizuje funkci v grafu z hlediska jednoho vystupu
	void Orthogonalization(const int num_output, const bool deep); //ortogonalizuje z hlediska nejakeho vystupu

public:
	Graph::Graph();
	Graph::~Graph();
	void ConstructFromPLALines(list<string> &buffer); //parsuje cely vstup PLA souboru a konstruuje graf
	void ConstructFromPLAFile(char *filename);
	void ConstructFromBoomPLA(PLA *_pla);
	string GenerateStreamForPLA(); //vygeneruje nove PLA podle aktualni konfigurace grafu
	PLA* GetPLA(); //create new empty PLA and set everything from Graph
	PLA* GetPLA(PLA *pla); //do a copy from PLA (without data) and from Graph only append terms

	void Simplification(); //absorbce, absorbce negace, indepotence
	string BruteForceTautology() const; //resi problem tautologie hrubou silou
	string BranchAndBoundTautology() const; //resi problem tautologie metodou vetvi a hranic
	Graph* Complement(); //vytvari komplement funkce
	void Minimization(const bool deep); //kompletne minimalizuje funkci v grafu
	void Orthogonalization(const bool deep); //provadi ortogonalizaci grafu
};

void TrimLeft (string &s, const string &sp = SPACES);
void TrimRight (string &s, const string &sp = SPACES);
void Trim (string &s, const string &sp = SPACES);

string Separation(string &s, const string &sp = SPACES);
bool FindMatch(string &a, string &b);
void MergeMatch(string &a, string &b);

void IncStr(string &str);

#endif