#include "gate.h"



True_table* Gate::compute_true_table(const std::set<Gate*>& c, int hodn) {
	True_table* t_table = new True_table();
	
	std::set<Gate*>::const_iterator result = c.find(this);
	
	if (result == c.end()) {
	if (hodn ==1)
	switch (type) {
		case G_AND: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			
			delete (t1);
			delete (t2);
			break;						
					}
		case G_NAND: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;
					 }
		case G_OR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;
				   }
		case G_NOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,0);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XNOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,1);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					 }
		case G_INV: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
			
					}
		case G_BUF: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
					}		
	}

	if (hodn ==0) {
		switch (type) {
		case G_AND: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			delete (t2);
			break;					
					}
		case G_NAND: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;		
					 }
		case G_OR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1,t2);
			
			delete (t1);
			delete (t2);
			break;
				   }
		case G_NOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,1);
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			for (True_table::iterator it = t2->begin(); it != t2->end(); ++it ) {
				t_table->push_back(*it);
			}
			break;	
					}
		case G_XOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,1);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					}
		case G_XNOR: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			True_table* t2 = inputs.back()->compute_true_table(c,0);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);

			t1 = inputs.front()->compute_true_table(c,0);
			t2 = inputs.back()->compute_true_table(c,1);
			merge_tables(t_table, t1, t2);
			delete (t1);
			delete (t2);
			break;	
					 }
		case G_INV: {
			True_table* t1 = inputs.front()->compute_true_table(c,1);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
			
					}
		case G_BUF: {
			True_table* t1 = inputs.front()->compute_true_table(c,0);
			
			for (True_table::iterator it = t1->begin(); it != t1->end(); ++it ) {
				t_table->push_back(*it);
			}
			delete (t1);
			break;
					}		
	}
	}
	} else { // navraceni prvdivostni tabulky
		Row* r= new Row();
		(*r)[id] = hodn;
		t_table->push_back(r);
		}
	return t_table;

}

bool Gate::merge_rows (Row& r, Row* r1, Row* r2) {
	r.clear();
	r = (*r1);
	for (Row::iterator it = r2->begin(); it != r2->end(); ++it) {
		Row::iterator result = r.find((*it).first);
		if ((result != r.end()) && ((*result).second != (*it).second)) {
			return false;
		}
		else {
			r[(*it).first] = (*it).second;
		}
	}
	return true;
}

void Gate::merge_tables(True_table *t, True_table *t1, True_table *t2) {
	Row* r = new Row();
	for (True_table::iterator it1 = t1->begin(); it1 != t1->end(); ++it1) {
		for (True_table::iterator it2 = t2->begin(); it2 != t2->end(); ++it2) {
			Row* r = new Row();
			if (merge_rows(*r, *it1, *it2) ) { //sli sloucit
				t->push_back(r);
			}
			else {
				delete(r);
			}			
		}
	}

}


Lut::~Lut() {
	
		for (True_table::iterator it_row = true_table->begin(); it_row != true_table->end(); ++it_row)
			delete(*it_row);
		delete (true_table);
}

void Gate::add_name_of_input(char *name){
	name_of_inputs.push_back(name);
}

void Gate::set_type(Type_of_gate _type) {
	type=_type;
}

void Gate::add_input(Gate *gat) {
	inputs.push_back(gat);
}

void Gate::add_output(Gate *gat) {
	outputs.push_back(gat);
}

unsigned int Gate::get_depth() const {
	return depth;
}

unsigned int Gate::get_depth_of_lut() const {
	return depth_of_lut;
}

unsigned int Gate::get_id() const {
	return id;
}

void Gate::set_depth(unsigned int D) {
	depth = D;
}

Type_of_gate Gate::get_type(void) const {
	return type;
}

Gate::Gate(bool _k_bounded):depth_of_lut(0) {
		char buf[256];
		sprintf_s(buf, "L%d", c_id); 
		name = buf; 
		id=c_id++; 
		k_bounded = _k_bounded;
}

Gate::Gate(char* _name, bool _k_bounded):name(_name),depth_of_lut(0){
	id=c_id++; k_bounded=_k_bounded;
}


void Gate::pridejVstupy( std::multiset<Gate*, cmp>& vstupy, std::set<Gate*>& vstupy_mn) {
	std::set<Gate*>::iterator result;
		for (std::list<Gate*>::iterator it=inputs.begin(); it != inputs.end(); ++it) {
			result = vstupy_mn.find(*it);
			if (result == vstupy_mn.end()) {
				vstupy_mn.insert(*it);
				vstupy.insert(*it);  // zde prusvih, musim si zjistit, jestli uz hradlo neni v seznamu....pouzivam multiset
			}
		}
}

unsigned int Lut::spoctiHloubku(){
	if (input_luts.empty()) {
		return 1;
	} else {
		if ( depth > 0 ) {
			return depth;
		} else {
			for (std::set<Lut*>::iterator it = input_luts.begin(); it != input_luts.end(); ++it) {
				unsigned int dep = (*it)->spoctiHloubku()+1;
				if (dep > depth) depth = dep;
			}
		}
		
	}
	return depth;
}