#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);

	// pouzij posledni dva vstupy jak vstupy hradla v
}

void Mapper::map(std::list<Lut*>& luts, const unsigned int K) {
	
	std::set<Gate*> spocitany;
	std::set<Gate*> k_spocitani;
	std::multiset<Gate*, cmp> vstupy;
	std::set<Gate*> vstupy_mn;
	std::set<Row*> ke_smazani;

	True_table* t;
	std::list<int> inputs_of_lut;
	std::list<Gate*> names_inputs_of_lut;

	for (std::list<Gate*>::iterator it = outputs.begin(); it != outputs.end(); ++it)
		k_spocitani.insert(*it);
	Gate* g;

	while (!k_spocitani.empty()) {	
		g = *(k_spocitani.begin());		
		spocitany.insert(g);
		k_spocitani.erase(g);
		vstupy.clear();
		vstupy_mn.clear();
		inputs_of_lut.clear();
		names_inputs_of_lut.clear();

		if ( !g->get_inputs().empty() ) {

			vstupy.insert(g);
			vstupy_mn.insert(g);

			while (vstupy.size() < K && !vstupy.empty()) {
				Gate* hradlo_s_nej_hloubkou;
				if (vstupy.size() < 1)
					hradlo_s_nej_hloubkou = *vstupy.begin();
				else
					hradlo_s_nej_hloubkou = *(--vstupy.end());
				if (hradlo_s_nej_hloubkou->get_depth() == 0) break;
	
				//pridej jeho vstupy
				vstupy.erase(--vstupy.end());
				vstupy_mn.erase(hradlo_s_nej_hloubkou);
				hradlo_s_nej_hloubkou->pridejVstupy(vstupy, vstupy_mn);			
			}
	
			bool zmena = true;
			
			while (zmena) {
				std::multiset<Gate*, cmp>::iterator it = vstupy.begin();
				zmena = false;
				while (it != vstupy.end()) {
				std::multiset<Gate*, cmp>::iterator it_x = it;
				++it;
				
				if ( (*it_x)->get_inputs().size() == 1 ) {
					
					vstupy_mn.erase(*it_x);
					zmena = true;
					(*it_x)->pridejVstupy(vstupy, vstupy_mn);
					vstupy.erase(it_x);
				}
				else {
					for (std::list<Gate*>::iterator it_gate = (*it_x)->get_inputs().begin(); it_gate != (*it_x)->get_inputs().end(); ++it_gate) {
						std::set<Gate*>::iterator result = vstupy_mn.find(*it_gate);
						if (result != vstupy_mn.end() ) {
							vstupy_mn.erase(*it_x);
							zmena = true;
							(*it_x)->pridejVstupy(vstupy, vstupy_mn);
							vstupy.erase(it_x);	
							break;
						}
					}
				}
				}
			}
				for (std::multiset<Gate*, cmp>::iterator it = vstupy.begin(); it != vstupy.end(); ++it) {
				inputs_of_lut.push_back((*it)->get_id());
				names_inputs_of_lut.push_back(*it);

				std::set<Gate*>::iterator result = spocitany.find(*it);
				if (!(*it)->get_inputs().empty() && result == spocitany.end()) {
					k_spocitani.insert (*it);
				}
			}

			t = g->compute_true_table(vstupy_mn, 1);			
			Lut* l = new Lut();
			l->depth = 0;
			l->id = g->get_id();
			l->name = g->get_name();	
			l->true_table = t;
			l->inputs = inputs_of_lut;
			l->names_inputs = names_inputs_of_lut;
			luts.push_back (l);		
			map_of_luts[l->id] = l;
		}
	}

		

	// provazani LUTu navzajem (pro snadne pocitani hloubky obvodu
	for (std::map<int, Lut*>::iterator it = map_of_luts.begin(); it != map_of_luts.end(); ++it) {
		Lut* lut = it->second;
		Lut* input_lut = NULL;

		for (std::list<int>::const_iterator it2= lut->inputs.begin(); it2 != lut->inputs.end(); ++it2){
			std::map<int, Lut*>::iterator it_input = map_of_luts.find(*it2);
			if (it_input != map_of_luts.end()) {
				lut->input_luts.insert(it_input->second);
			}			
		}
	}
	
}

