#include "import.h"


void LexSymbol::pridejZnak(char zn) {
  if (delkaIdent < MaxLenIdent-1)
	ident[delkaIdent++] = zn;
  ident[delkaIdent] = '\0';
}


LexAn::LexAn(char* nameOfFile) {
	fin.open(nameOfFile);
	if (!fin.is_open()) {
		std::cout << "Cannot open file " << nameOfFile << std::endl;
		exit (-1);
    }

	ctiVstup();
}

LexAn::~LexAn() {
	fin.close();
}

char LexAn::ctiZnak() {
	char znak;
	
	if (fin.get(znak)){
		return znak;
	}
	else
		return '\0';
}

void LexAn::ctiVstup(){
   znak = ctiZnak();
   if (znak>='A' && znak<='Z' || znak>='a' && znak<='z' || znak>='0' && znak<='9')
      vstup = 'p';
   else if (znak == '\0')
      vstup = 'e';
   else if (znak == '\n')
      vstup = 'n';
   else if (znak <= ' ')
      vstup = 'm';
   else vstup = znak;
}

LexSymb LexAn::klicoveSlovo(char* key) {
   int i = 0;
   while (i < count_key_words) 
      if (strcmp(key, TabKS[i].slovo)==0) 
         return TabKS[i].symb;
      else
         ++i;
   return T_IDENT;
}

LexSymbol LexAn::getSymbol(){
	LexSymbol lSymb;
	while (1) {
		switch (vstup) {
			case 'm': ctiVstup();
					  break;
		    
			case 'n': ctiVstup();
				      break;

			case '#': ctiVstup();
				      while (vstup != 'n')
						  ctiVstup();
					  ctiVstup();
					  break;

			case '(': ctiVstup();
					  lSymb.nastavTyp(T_LPAR);
					  return lSymb;
					  break;	
			
			case ')': ctiVstup();
					  lSymb.nastavTyp(T_RPAR);
					  return lSymb;
					  break;
			
			case ',': ctiVstup();
					  lSymb.nastavTyp(T_COMMA);
					  return lSymb;
					  break;
			case 'e': lSymb.nastavTyp(T_EOI);
					  return lSymb;
					  break;

			case '=': ctiVstup();
					  lSymb.nastavTyp(T_EQUAL);
					  return lSymb;
					  break;

			default: lSymb.pridejZnak(znak);
					 ctiVstup();
					 while (vstup!='m' && vstup != '#' && vstup !='(' &&
						    vstup!=')' && vstup != ',' && vstup !='e' && vstup != 'n') {
								lSymb.pridejZnak(znak);
								ctiVstup();
					 }
					 lSymb.nastavTyp(klicoveSlovo(lSymb.getIdent()));
					 return lSymb;
					 break;
		}
	}
}

void Import::compare(LexSymb ls){
	if (show() == ls) 
		symbol.nastavTyp(T_NULL);
	else
		//throw VyjimkaSynAn("Neocekavany symbol.");
		std::cout << "chyba srovnani";
}

LexSymb Import::show(){
	if (symbol.getTyp() == T_NULL) symbol = lex.getSymbol();
	return symbol.getTyp();
}


void Import::inputs_outputs(std::list<Gate*>& _inputs, std::list<Gate*>& _outputs) {
	while ( show() == T_INPUT || show() == T_OUTPUT)
		switch ( show() ) {
			case T_INPUT:	{compare (T_INPUT);
							compare (T_LPAR);
							compare (T_IDENT);
							Gate* gate = new Gate(symbol.getIdent(), false);
							gate->set_depth(0);
							map_of_gates[symbol.getIdent()] = gate;
							_inputs.push_back(gate);
							compare (T_RPAR);
							break;}

			case T_OUTPUT:	{compare (T_OUTPUT);
							compare (T_LPAR);
							compare (T_IDENT);
							Gate* gate = new Gate(symbol.getIdent(), false);
							gate->set_depth(0);
							map_of_gates[symbol.getIdent()] = gate;
							_outputs.push_back(gate);
							compare (T_RPAR);
							break;}
	}
}

void Import::inner_nodes(std::list<Gate*>& _inner_nodes) {
	while ( show() != T_EOI ) {
		compare (T_IDENT);
		Gate* gate=NULL;
		std::map<std::string,Gate*>::iterator it = map_of_gates.find(symbol.getIdent());

		if (it != map_of_gates.end())
			gate = it->second;
		else {
			gate = new Gate(symbol.getIdent(), false);
			map_of_gates[symbol.getIdent()] = gate;
			_inner_nodes.push_back(gate);
		}
		
		compare (T_EQUAL);
		gate->set_type(type());   //tato funkce bude vracet navratovou hodnotu...typu Type

		compare (T_LPAR);
		inputs_of_gate (gate); //tato funkce bude prebirat nove vytvoreny uzel
		compare (T_RPAR);
	}
}


void Import::inputs_of_gate(Gate* gate) {
	compare (T_IDENT);
	gate->add_name_of_input(symbol.getIdent());
	rest_inputs_of_gate(gate);
}

void Import::rest_inputs_of_gate(Gate* gate){
	switch ( show() ) {
		case T_COMMA:	compare (T_COMMA);
						inputs_of_gate(gate);
						break;

		default:		break;//epsilon
	}
}

Type_of_gate Import::type() {
	switch ( show() ) {
		case T_AND:		compare (T_AND);
						return G_AND;
						break;

		case T_OR:		compare (T_OR);
						return G_OR;
						break;

		case T_NAND:	compare (T_NAND);
						return G_NAND;
						break;

		case T_NOR:		compare (T_NOR);
						return G_NOR;
						break;

		case T_XOR:		compare (T_XOR);
						return G_XOR;
						break;

		case T_XNOR:	compare (T_XNOR);
						return G_XNOR;
						break;

		case T_BUF:		compare (T_BUF);
						return G_BUF;
						break;

		case T_INV:		compare (T_INV);
						return G_INV;
						break;

		default:		std::cout << "Neocekavany typ hradla";  // zde by se mela vyhazovat vyjimka
						exit(-1);
						break;
	}
}

void Import::read_circuit(std::list<Gate*>& _inputs,std::list<Gate*>& _outputs, std::list<Gate*>& _inner_nodes) {
	inputs_outputs(_inputs, _outputs);
	inner_nodes(_inner_nodes);

	for (std::map<std::string, Gate*>::iterator it = map_of_gates.begin(); it != map_of_gates.end(); ++it) {
		Gate* gate = it->second;
		Gate* input_gate = NULL;
		for (std::list<std::string>::const_iterator it2= gate->get_names_of_inputs().begin(); it2 != gate->get_names_of_inputs().end(); ++it2){
			std::map<std::string, Gate*>::iterator it_input = map_of_gates.find(*it2);
			if (it_input != map_of_gates.end()) { // pokud vnitrni uzel nalezen
				input_gate = it_input->second;
			} else {
				std::cout << "Chyba vstupniho obvodu. Vstupni uzel hradla " << (*it2) << " nenalezen!";
	            exit (-1);
			}			
			input_gate->add_output(gate);
			gate->add_input(input_gate);
		}
	}
}



