#include <iostream>
#include <cctype>
#include <fstream>
#include <algorithm>
#include <iomanip>


#include <stdio.h>
#include <string.h>

#include "parserNetlist.h"
#include "functionInst.h"
#include "functionMem.h"
#include "functionUser.h"
#include "graph.h"

using namespace std;

extern bool verbose;
extern bool noGraphCopy, noITS;

// WARNING
// A line with { is ignored

/// List of all user functions (in order of creation)
vector<functionUser *> listFuns;

functionUser *currentFun, *currentInstFun;
operation *currentOp;
variable *currentVar;

bool errorHasOccured;	// =true when an error has occured

/**
 * for creating user function
 * 0 : outputs
 * 1 : function's name
 * -- : open bracket "("
 * 2 : inputs
 * -- : close bracket ")"
 * 3 : wait for "{"
 * 4 : function core
 * -- : "}"
 * \see step2
 * \see ReadFunction
 */
short step1=0;

/**
 * same thing but for instantiating function
 * -1 : no instantiating function
 * -2 : receive output of non instanting function
 * 3-4 : no use
 * \see step1
 * \see ReadFunction
 */
short step2=-1;

/**
 * Type of read assertion
 **/
enum TYPE_ASSERT{
    TYPE_EXPR = 0,      // a = b^c
    TYPE_USER_FUN,      // inst a,b = fun1(c,d)
    TYPE_RAM,           // ram do1,do0 = ram1(ao0,we,ai0,di1,di0)
    TYPE_ROM            // rom do1,do0 = rom1(ao0)
}typeAssert;

/// Current read line
short linenum=0;


// Internal for ParserNetlist

void ParserError(string="",bool=true);
variable *ProcessExpression(char a1[], variable *out);
bool IsCorrectName(string name);
bool Is01(string name);

/**
 * Return true if it's a correct name for a variable/function
 * Print an error if this is not a good name
 */
bool IsCorrectName(string name){
    for(unsigned short i=0;i<name.size();i++){
        if(!isalnum(name[i]) && name[i]!='_' && name[i]!='[' && name[i]!=']'){
            ParserError("bad variable/function name : \"" + name + "\"");
            return false;
        }
    }
    return true;
}

bool Is01(string name){
    if(name=="0"){
        ParserError("\"0\" is a reserved keyword");
        return false;
    }
    if(name=="1"){
        ParserError("\"1\" is a reserved keyword");
        return false;
    }
    return true;
}

/**
 * Function called when there is a
 *  - syntax error (syntax=true)
 *  - internal error (syntax=false)
 **/
void ParserError(string s, bool syntax){
	errorHasOccured = true;
	cerr << "    Netlist Parser ERROR : " << (syntax ? string("syntax") : string("internal")) << " error" << /*" at step " << step1 << "," << step2 <<*/ " - line : " << linenum << endl;
	if(s!=""){
		cerr << "        " << s << endl;
	}
}

/**
 * Process a name in a function header
 * Used by ReadFunction in mode "inst = false"
 * @see ReadFunction
 **/
void ProcessName1(string name){
	switch(step1){
		case 0 :
            if(!IsCorrectName(name)){
                break;
            }
            if(!Is01(name)){
                break;
            }
			currentFun->AddOutput(new variable(name));
			break;
		case 1 :
            if(!IsCorrectName(name)){
                break;
            }
            if(!Is01(name)){
                break;
            }
			currentFun->SetName(name);
			break;
		case 2 :
            if(!IsCorrectName(name)){
                break;
            }
            if(!Is01(name)){
                break;
            }
			currentFun->AddInput(new variable(name));
			break;
		default :
			ParserError("",false);
			break;
	}
}

/**
 * Process a name in core function
 * Used by ReadFunction in mode "inst = true"
 * @see ReadFunction
 **/
void ProcessName2(string name){
	switch(step2){
		case -1 :
			if(name=="inst"){
			    // instantiate a user function
				step2 = 0;
				typeAssert = TYPE_USER_FUN;
				currentInstFun = 0;
				currentOp = new operation(linenum);
				// When there is a graph copy, this add is a virtual add, it will be destoyed when we really copy combinatorial graph of the instantiated function
				// But it's useful to push_back currenOp for having no problem when we free memory
                currentFun->AddOperation(currentOp);
			}else if(name=="ram"){
			    // instantiate a user function
				step2 = 0;
				typeAssert = TYPE_RAM;
				currentOp = new operation(linenum);
				currentFun->AddOperation(currentOp);
			}else if(name=="rom"){
			    // instantiate a user function
				step2 = 0;
				typeAssert = TYPE_ROM;
				currentOp = new operation(linenum);
				currentFun->AddOperation(currentOp);
			}else{
			    typeAssert = TYPE_EXPR;
                if(!IsCorrectName(name)){
                    break;
                }
				currentVar = currentFun->GetVariable(name);
				step2 = -2;
			}
			break;
		case 0 :
            if(!IsCorrectName(name)){
                break;
            }
            if(!Is01(name)){
                break;
            }
			currentOp->AddOutput(currentFun->GetVariable(name));
			break;
		case 1 :
			if(typeAssert == TYPE_USER_FUN){
				vector<functionUser *>::iterator it = find_if(listFuns.begin(),listFuns.end(),FunctionUserSameName(name));
				if(it==listFuns.end()-1){
					ParserError("function \"" + name + "\" can't instantiate itself (recursion is forbidden).");
					currentInstFun = 0;
				}else if(it==listFuns.end()){
					ParserError("function \"" + name + "\" does'nt exist.");
					currentInstFun = 0;
				}else{
				    currentInstFun = *it;
				    if(currentInstFun->numInst==0){
                        ParserError("you can't instanciate function \"" + name + "\" twice.");
                        currentInstFun = 0;
				    }else{
                        if(currentInstFun->numInst>0){
                            currentInstFun->numInst--;
                            // Prevent from using more than one instance of this function
                            currentFun->numInst = 1;
                        }
                        if(noGraphCopy){
                            /*functionUserInst *fun = */new functionUserInst(*it,currentOp);
                        }
				    }
				}
			}else if(typeAssert == TYPE_RAM || typeAssert == TYPE_ROM){
			    /*functionMem *fun = */new functionMem(name,typeAssert == TYPE_ROM,currentOp);
			    if(typeAssert == TYPE_RAM && noGraphCopy){
			        // Prevent from using more than one instance of this function
			        currentFun->numInst = 1;
			    }
			}
			break;
		case 2 :
            if(!IsCorrectName(name)){
                break;
            }
			currentOp->AddInput(currentFun->GetVariable(name));
			break;
		default :
			ParserError("",false);
			break;
	}
}

/**
 * Read a function header (like a,b = fun1(c,d){}) if inst=false
 * or read an assertion (like "inst a,b = fun1(c,d)" or "a = b^c")
 * typeAssert indicate which type of assertion this is
 * @param ProcessName ProcessName1 when inst=false and ProcessName2 when inst=true
 **/
 void ReadFunction(string &line, short &step, bool inst, void (&ProcessName)(string)){
 	short beginName=-1;
 	string name;

 	typeAssert = TYPE_EXPR;

	for(unsigned short i=0;i<line.size();i++){
		char c = line[i];
		if(isspace(c)){
			if(beginName!=-1){	// A name has begun
				name=line.substr(beginName,i-beginName);	// End of a name
				ProcessName(name);
				beginName=-1;
			}
		}else if(c=='='){
			if(beginName!=-1){	// A name has begun
				name=line.substr(beginName,i-beginName);	// End of a name
				ProcessName(name);
				beginName=-1;
			}

			if(step==-2 && inst){
				char *chr=new char[line.size()+1-i];
				strcpy(chr, line.substr(i+1).c_str());
				ProcessExpression(chr,currentVar);
				delete [] chr;
				return;
			}else if(step==0){
				step=1;
			}else{
				ParserError("'=' has nothing to do here");
			}
		}else if(c==','){
			if(beginName==-1){	// No name has begun
				//ParserError("',' has nothing to do here");
			}else{
				name=line.substr(beginName,i-beginName);	// End of a name
				ProcessName(name);
				beginName=-1;
			}
		}else if(c=='('){
			if(step==1){
			    if(beginName!=-1){
                    name=line.substr(beginName,i-beginName);	// End of a name
                    ProcessName(name);
                    beginName=-1;
			    }

				step++;
			}else{
				ParserError("'(' has nothing to do here");
			}
		}else if(c==')'){
			if(step==2){
				// There is eventually no argument
				if(beginName!=-1){
					name=line.substr(beginName,i-beginName);	// End of a name
					ProcessName(name);
					beginName=-1;
				}

				step++;
			}else{
				ParserError("')' has nothing to do here");
			}
		}else if(c=='{'){
			if(step==3 && !inst){
				step++;
			}else{
				ParserError("'{' has nothing to do here"); //}
			}
		}else{
			if(beginName==-1){
				beginName=i;	// A name is beginning here
			}
		}

		if(errorHasOccured){
		    return;
		}
	}

	// If we have a name not treated (when lines are cut for example)
	if(beginName!=-1){
		name=line.substr(beginName);	// End of a name
		ProcessName(name);
		beginName=-1;
	}

	// Instantiated function or memory case
	if(step>0 && inst){
	    // Verify we have the good number of inputs/outputs
	    if(typeAssert==TYPE_USER_FUN){
	        if(currentInstFun){
                if(currentInstFun->GetNumberOfInputs()!=currentOp->GetNumberOfInputs()){
                    ParserError("function has not the good number of inputs");
                }else if(currentInstFun->GetNumberOfOutputs()!=currentOp->GetNumberOfOutputs()){
                    ParserError("function has not the good number of outputs");
                }else if(!noGraphCopy){
                    currentFun->AddInstFunctionUser(currentInstFun,currentOp);
                }
	        }
	    }else if(typeAssert==TYPE_RAM || typeAssert==TYPE_ROM){
            if(! dynamic_cast<functionMem*>(currentOp->GetFun())->VerifyNumberOfIO()){
                ParserError("memory has not the good number of inputs/outputs (verify the relation between number of I/O and that data length <= 32 and address length <= 31)");
            }
	    }else{
	        ParserError("typeAssert",false);
	    }
	}
}

/**
 * Process an expression (for example : a & (b |c))
 * Out is the output of the result of function
 * @return the real out (can be different from out if out.IsTemp()==true)
 **/
variable *ProcessExpression(char a1[], variable *out)	//a is the expression, and out is the position of variable to receive the result of this expression
{
	int i,len=strlen(a1);
	char b[len+1],c[len+1];
	char *a;

	a=a1;

	// Remove space before
	while(a-a1<=len && isspace(*a))a++;
	len = len - (a-a1);

	// Remove space after
	while(len>=1 && isspace(a[len-1]))len--;

	if(len==0){
		ParserError("a length is null : ",true);
		return out;
	}

	a[len] = 0;
//	if (len==1)
//	{
//		op[oplen].in1=getorder(a);
//		op[oplen].out=out;
//		op[oplen].name_of_op=6;			//directly give the value of in1 to the variable out
//		add_edge(op[oplen].in1,out);
//		oplen++;
//		return;
//	}

	int mid=-2,bracket=0;					//mid: the point to seperate the instruction - we search ...(*)
										//bracket: the number of brackets at every moment
	bool isBrackets=false;

	for (i=len-1; i>=0; i--)
	{
		if (a[i]=='(') {bracket++;isBrackets=true;} else
		if (a[i]==')') {bracket--;isBrackets=true;};
		if (bracket==0) { mid=i-1; break;}
	}
	if(mid==-2){
		ParserError("Brackets mismatched");
		return out;
	}
	if (mid<0 && isBrackets)							// indicating the form : ( expression ), and in this occassion, just omit this pair of brackets
	{
		for (i=1; i<len-1; i++) b[i-1]=a[i]; b[len-2]=0;
		return ProcessExpression(b,out);;
	}

	while(mid>0 && a[mid]!='&' && a[mid]!='|' && a[mid]!='^')mid--;

	if(mid<=0){	// We only have a variable name or we have an error
        if (a[0]=='Z')
        {
            for (i=1; i<len; i++)
                b[i-1]=a[i];
            b[len-1]=0;
            variable *out1=currentFun->GetVariable();
            out1 = ProcessExpression(b, out1);
            currentFun->AddRegister(new registerV(out1,out));
            return out;
        }
        if (a[0]=='~')
        {
            for (i=1; i<len; i++)
                b[i-1]=a[i];
            b[len-1]=0;

            variable *out1=currentFun->GetVariable();

            operation *op=new operation(linenum);
            currentFun->AddOperation(op);

            /*functionNot *fun=*/new functionNot(op);
            op->AddInput(ProcessExpression(b, out1));
            op->AddOutput(out);
            return out;
        }

		if(isBrackets){
			ParserError("bad brackets -> maybe you have forgotten inst keyword");
			return out;
		}

		if(!IsCorrectName(string(a))){
			return out;
		}

		// We connect two variables through a functionInstEqual
		// or if out variable is a temporary variable, simply return the good variable

		variable *out1=currentFun->GetVariable(string(a));

		if(out->IsTemp()){
			currentFun->DeleteVariable(out);
			return out1;
		}else{
			operation *op=new operation(linenum);
			currentFun->AddOperation(op);

			/*functionEqual *fun=*/new functionEqual(op);
			op->AddInput(out1);
			op->AddOutput(out);
			return out;
		}
	}

										// seperate a into b and c, and connect b and c by the operator a[mid]
	for (i=0; i<mid; i++) b[i]=a[i]; b[mid]=0;
	for (i=mid+1; i<len; i++) c[i-mid-1]=a[i]; c[len-mid-1]=0;

	variable *in1=currentFun->GetVariable();	// create a temperate variable to store the result of b
	variable *in2=currentFun->GetVariable();  // create a temperate variable to store the result of c

	operation *op=new operation(linenum);
	currentFun->AddOperation(op);

	op->AddOutput(out);

	if (a[mid]=='&') /*functionInst *fun=*/new functionAnd(op); else
	if (a[mid]=='|') /*functionInst *fun=*/new functionOr(op); else
	if (a[mid]=='^') /*functionInst *fun=*/new functionXor(op);

	op->AddInput(ProcessExpression(b,in1));
	op->AddInput(ProcessExpression(c,in2));

	return out;
}

/**
 * NetList Parser
 * @return the main functionUser if succeed otherwise return 0
 **/
functionUser *ParserNetlist(string filename){

	ifstream f(filename.c_str(),ios::in);
	string line;

	step1 = 0;
	errorHasOccured = false;

	listFuns.push_back(new functionUser);
	currentFun = listFuns.back();

	if(verbose)cout << endl << "---------- NetList Parser ----------" << endl << endl;

	if(!f){
		cerr << "Netlist parser ERROR : reading file." << endl;
		return 0;
	}

	while(f){
		// this iterator is a pointer to begin of a name
		string name;

		getline(f,line);
		linenum++;

		size_t size = line.find('#');
		if(size!=string::npos){
			line = line.substr(0,size);	// We delete all is after #
		}

		if(step1!=4){
			// We are outside of a function
			// We need to read a function header

			// Model : "out1,out2 = fun(in1,in2){ }"

			ReadFunction(line,step1,false,ProcessName1);
			if(errorHasOccured)break;
		}else{	// We are in a function
			if(line.find('}')!=string::npos){	// There is a "{"
				step1=0;

                vector<functionUser *>::iterator it = find_if(listFuns.begin(),listFuns.end(),FunctionUserSameName(currentFun->GetName()));
				if(it!=listFuns.end()-1){
					ParserError("function \"" + currentFun->GetName() + "\" already exists.");
					currentInstFun = 0;
				}else{
                    if(verbose)cout << "Read function : " << currentFun->GetName() << endl;

                    listFuns.push_back(new functionUser);
                    currentFun = listFuns.back();
				}
				continue;
			}

			step2 = -1;

			ReadFunction(line,step2,true,ProcessName2);
			if(errorHasOccured)break;
		}
	}

	f.close();

	// Destroy last function created because it's normally an empty function
	currentFun->DestroyGraph();
	delete currentFun;
	listFuns.pop_back();

	if(errorHasOccured){
		return 0;
	}

	if(listFuns.size()==0){
		cerr << "Netlist parser ERROR : no function is defined." << endl;
		return 0;
	}

	if(verbose)cout << endl << "---------- TopoSort ----------" << endl << endl;

	// Toposort all function if noITS=false
	// Otherwise toposort only the last function (it's sufficient !)
	for(vector<functionUser *>::iterator it = noITS ? listFuns.end()-1 : listFuns.begin() ; it != listFuns.end() ; it++){
		string error="";
		bool ret=(*it)->SortListOps(error);

		cout.setf(ios::left);
		if(verbose || !ret || error!="")cout << "Function : " << setw(30) << (*it)->GetName();
		cout.unsetf(ios::left);

		if(!ret){
			cout << "ERR" << endl;
			cerr << "    Toposort ERROR : " << error << endl;
			errorHasOccured = true;
		}else if(error!=""){
			cout << "WARN" << endl;
			cout << "    Toposort WARNING : " << error << endl;
		}else if(verbose){
			cout << "OK" << endl;
		}
	}

	// When we copy graph, we can destroy all functionUser except the last one
	if(!noGraphCopy){
	    // We save last function
	    currentFun = listFuns.back();
	    listFuns.pop_back();

	    // We destroy other functions
	    ParserNetlistFree();
	    listFuns.clear();

	    // We restore last function
	    listFuns.push_back(currentFun);
	}

	if(errorHasOccured){
		return 0;
	}else{
		return listFuns.back();
	}
}

/**
 * Free all graph of user functions
 * Has to be called at the end of the program
 **/
void ParserNetlistFree(){
	for(vector<functionUser *>::iterator it = listFuns.begin() ; it != listFuns.end() ; it++){
		(*it)->DestroyGraph();
		delete (*it);
	}
}

