#include "mapper.h"


void Mapper::transform_to_K_bounded(const unsigned int K){
	std::list<Gate*> list;
	list = inputs;

	while (!(list.empty())) {
		Gate* gate = list.front();
		list.pop_front();		
	
		if (inputs_k_bounded(gate)) { // pokud jeho vstupy jiz spocitany
			if ( (gate->get_inputs() ).empty() ){ // vstupni uzel, nastav hloubku na 0
				gate->set_k_bounded();
				gate->set_depth ( 0);
			} else
				dmig(gate, K); //zpracuj vnitrni uzel
			
			//vloz nasledniky do listu
			std::list<Gate*> outputs_of_gate = gate->get_outputs();
			for (std::list<Gate*>::iterator it = outputs_of_gate.begin(); it != outputs_of_gate.end(); ++it) {
			
				std::list<Gate*>::iterator result = std::find(list.begin(), list.end(), *it);
				if (result == list.end())
					list.push_back(*it);
			}
		} else {
			list.push_back (gate);
		}
	}
}


bool Mapper::inputs_k_bounded(Gate *g) {
	std::list<Gate*> in = g->get_inputs();
	for (std::list<Gate*>::iterator it = in.begin(); it != in.end(); ++it) {
		if ( ! ((*it)->is_k_bounded()))
			return false;
	}
	return true;
}

void Mapper::dmig(Gate *g, unsigned int K) {
	std::list<Gate*>& inputs = g->get_inputs();
	while (inputs.size() > K) {

		Gate* min1 = inputs.front();
		for (std::list<Gate*>::iterator it = inputs.begin(); it != inputs.end(); ++it) {
			if ( (*it)->get_depth() < min1->get_depth()) {
				min1 = *it;
			}
		}

		inputs.remove (min1);
		Gate* min2 = inputs.front();
		for (std::list<Gate*>::iterator it = inputs.begin(); it != inputs.end(); ++it) {
			if ( (*it)->get_depth() < min1->get_depth()) {
				min2 = *it;
			}
		}
		inputs.remove (min2);

		// vytvor novy uzel
		Type_of_gate t;
		switch (g->get_type()) {
			case G_NAND : t = G_AND;
						  break;
			case G_NOR:	t = G_OR;
						break;
			default: t=g->get_type();
					 break;
		}

		// nastav vstupy vystupy
		Gate* newGate = new Gate(true);
		newGate->set_type(t);
		std::list<Gate*>& min1_outputs = min1->get_outputs();
		std::list<Gate*>& min2_outputs = min2->get_outputs();
		min1_outputs.remove(g);
		min2_outputs.remove(g);

		min1_outputs.push_back(newGate);
		min2_outputs.push_back(newGate);

		newGate->add_input(min1);
		newGate->add_input(min2);
		newGate->add_output(g);
		newGate->set_k_bounded();
		unsigned int min1_depth = min1->get_depth();
		unsigned int min2_depth = min2->get_depth();

		if (min1_depth > min2_depth) {
			newGate->set_depth(min1_depth+1);
		} else {
			newGate->set_depth(min2_depth+1);
		}
		inner_nodes.push_back(newGate);
		inputs.push_back(newGate);		
	}

	g->set_k_bounded();
	unsigned int depth;
	depth = (inputs.front())->get_depth();
	for (std::list<Gate*>::iterator it = inputs.begin(); it != inputs.end(); ++it) {
		if ( (*it)->get_depth() > depth) {
				depth = (*it)->get_depth();
			}
	}
	g->set_depth(depth+1);
}

void Mapper::compute_cuts(const unsigned int K) {
	std::list<Gate*> list;
	list = inputs;

	while (!(list.empty())) {
		Gate* gate = list.front();
		list.pop_front();		
	
		if (gate->are_computed_inputs_cuts()) { // pokud jeho vstupy jiz spocitany cuts
			gate->compute_cuts(K);
			
			//vloz nasledniky do listu
			std::list<Gate*> outputs_of_gate = gate->get_outputs();
			for (std::list<Gate*>::iterator it = outputs_of_gate.begin(); it != outputs_of_gate.end(); ++it) {			
				std::list<Gate*>::iterator result = std::find(list.begin(), list.end(), *it);
				if (result == list.end())
					list.push_back(*it);
			}
		} else {
			list.push_back (gate);
		}
	}
}

void Mapper::make_luts(std::list<Lut*>& luts) {
	std::list<Gate*> list;
	std::set<int> spocitany;
	std::list<int> inputs_of_lut;
	std::list<Gate*> names_inputs_of_lut;

	list = outputs;
	True_table* t;
	while (!(list.empty())) {
		Gate* g = list.front();
		list.pop_front();
		if (!g->empty_cuts()) {
			Cut& c = g->get_cut_minimum_delay();
		inputs_of_lut.clear();
		names_inputs_of_lut.clear();
		
		int i = g->get_id();
		t = g->compute_true_table(c, 1); // musi vratit pravdivostni tabulku.
		spocitany.insert (g->get_id());
		Lut* l = new Lut();
		l->id = g->get_id();
		l->name = g->get_name();
		
		l->true_table = t;
		luts.push_back (l);
		//vytvor lut s danou pravdivostni tabulkou....

		// vlozeni vstupu lutu pro spocitani
		for (std::set<Gate*>::const_iterator it = c.gates.begin(); it != c.gates.end(); ++it) {
			inputs_of_lut.push_back((*it)->get_id());
			names_inputs_of_lut.push_back(*it);

			std::set<int>::iterator result = spocitany.find((*it)->get_id());
			std::list<Gate*>::iterator result2 = std::find(list.begin(), list.end(), *it);

			if (result2 == list.end())
			if ( (!((*it)->get_inputs().empty())) && (result == spocitany.end())) {//primarni vstupy nechci vkladat...
				list.push_back(*it);
			}
		}

		l->inputs = inputs_of_lut;
		l->names_inputs = names_inputs_of_lut;
		}

	}
}