#include <string>
#include"Reader.h"
#include"Circuit.h"
#include"Input.h"
#include"Output.h"
#include"Rule.h"
#include<iostream>
#include<sstream>
#include<queue>
 
Reader::Reader(string* input){
    this->input=input;
}

Reader::~Reader(){
    this->input=NULL;
    this->circuit=NULL;
}

Circuit* Reader::parse(){
	//mody prekladaca, ktore sa menia podla prevzateho tokenu
	static const int MODE_NONE=0;
    static const int MODE_INPUT=1;
    static const int MODE_OUTPUT=2;
    static const int MODE_RULE=3;
    
	//vysledna reprezentacia obvodu
    Circuit* circuit=new Circuit();
    Rule* tmpRule;
    Rule* tmpRInput;
    Input* tmpInput;
    Output* tmpOutput;
    
    int mode=MODE_NONE;
    bool syntaxOK=true;
    
    //vyber prveho tokenu
	Token* token=Token::getFirst(input); 
	//pokial sme nevybrali uplne vsetko ale este stale je syntax spravna
    while(token!=NULL && syntaxOK){
        syntaxOK=false;
		//rozdelenie parsovania podla modov
        switch (mode){
            case MODE_NONE:
				//zistime, co sme vbrali zo suboru a podla toho sa prepneme do ineho modu
                if(token->type==Token::TYPE_AN_STRING){
                    if(token->content.compare("INPUT")==0){ 
                        mode=MODE_INPUT;
                        syntaxOK=true;
                    }else if(token->content.compare("OUTPUT")==0){
                        mode=MODE_OUTPUT;
                        syntaxOK=true;
                    }else{
                        mode=MODE_RULE;
                        syntaxOK=true;
                        //cout << "create RULE: " << token->content << endl;
                        tmpRule=new Rule(token->content);
                    }
                }
                break;
            case MODE_INPUT:
                if(token->type==Token::TYPE_AN_STRING){
                    mode=MODE_NONE;
                    syntaxOK=true;
                    //cout << "create INPUT: " << token->content << endl; 
                    tmpInput=new Input(token->content);
                    circuit->addInput(tmpInput);
                    tmpRule=new Rule(token->content);
                    tmpRule->setType("INPUT");
                    circuit->addRule(tmpRule);
                }
                break;
            case MODE_OUTPUT:
                if(token->type==Token::TYPE_AN_STRING){
                    mode=MODE_NONE;
                    syntaxOK=true;
                    //cout << "create OUTPUT: " << token->content << endl; 
                    tmpOutput=new Output(token->content);
                    circuit->addOutput(tmpOutput);
                }
                break;
            case MODE_RULE:
                if(token->content.compare("=")==0){
                    token=Token::getFirst(input);
                    if(token->type==Token::TYPE_AN_STRING){
                        //cout << "\t TYPE: "<< token->content <<endl; 
                        tmpRule->setType(token->content);
                        token=Token::getFirst(input);
                        if(token->content.compare("(")==0){
                            token=Token::getFirst(input);
                            while(token->content.compare(")")!=0){
                                syntaxOK=false;
                                if(token->type==Token::TYPE_AN_STRING){
                                    //cout << "\t RINPUT: " << token->content << endl;
                                    tmpRInput=new Rule(token->content);
                                    tmpRule->addInput(tmpRInput);
                                }
                                token=Token::getFirst(input);
                                if(token->content.compare(",")==0||token->content.compare(")")==0){
                                    syntaxOK=true;
                                }
                            }
                            if(syntaxOK){
                                circuit->addRule(tmpRule);
                            }
                            mode=MODE_NONE;
                        }
                    }
                }
                break;
        }
		//pokial vsetko prebehlo v poriadku, vyberieme dalsi token
        token=Token::getFirst(input);
    }
	//vratime vysledny vytvoreny obvod
    return circuit;
}

Token::Token(string content, int type){
    this->content=content;
    this->type=type;
}

Token::~Token(){
    
}

Token* Token::getFirst(string* input){
    Token* token;
    char firstChar=' ';
    stringstream contentBuf;
    int type=Token::TYPE_CHAR;
    
    if(input->length()>0){
        firstChar=input->at(0);
        input->erase(0,1);
        //odstranenie komentarov
        while(firstChar=='#'){
            input->replace(0,input->find("\n"),"");
            firstChar=input->at(0);
        }
        //cout << "INPUT: " << *input << endl;
        //Ak patri pociatocny retazec medzi alfanumericke znaky, nacitameho, az kym 
        //toto pravidlo neporusi
        if((firstChar>='a'&&firstChar<='z')||
                (firstChar>='A'&&firstChar<='Z')||
                (firstChar>='0'&&firstChar<='9')){
            contentBuf << firstChar;
            type=Token::TYPE_AN_STRING;
            if(input->length()>0){
                firstChar=input->at(0);
            }else{
                firstChar=' ';
            }
            while((firstChar>='a'&&firstChar<='z')||
                    (firstChar>='A'&&firstChar<='Z')||
                    (firstChar>='0'&&firstChar<='9')){
                input->erase(0,1);
                contentBuf << firstChar;
                if(input->length()>0){
                    firstChar=input->at(0);
                }else{
                    firstChar=' ';
                }
            }
        }else{
            if(firstChar!=' '&&firstChar!='\n'&&firstChar!='\t'){
                contentBuf << firstChar;
            }else{
                return Token::getFirst(input);
            }
        }
        //V contentBuf by sa mal teraz nachadzat token
        token=new Token(contentBuf.str(),type);
        return token;
    }else{
        return NULL;
    }
   
}

