#include "functionUser.h"
#include "graph.h"
#include "tools.h"
#include "functionInst.h"

#include <iostream>
#include <algorithm>
#include <sstream>
#include <set>

using namespace std;


// ------
// Class functionUser
// ------

functionUser::functionUser(string name_){
	name = name_;
	numInst = -1;
	var0.SetValue(0);
	var0.SetName("0");
	var1.SetValue(1);
	var1.SetName("1");
}

void functionUser::DestroyGraph(){
	VectorDeleteAll<registerV>(registers);
	//VectorDeleteAll<variable>(inputs);
	//VectorDeleteAll<variable>(outputs);
	VectorDeleteAll<variable>(listVars);
	VectorDeleteAll<operation>(listOps);
}

// Tools function

void functionUser::AddInput(variable *input){
	inputs.push_back(input);
	AddVariable(input);
	//inputs.insert(lower_bound(inputs.begin(),inputs.end(),input,CompareVariableName),input);
}

void functionUser::AddOutput(variable *output){
	outputs.push_back(output);
	AddVariable(output);
	//outputs.insert(lower_bound(outputs.begin(),outputs.end(),output,CompareVariableName),output);
}

void functionUser::AddVariable(variable *var){
	listVars.insert(lower_bound(listVars.begin(),listVars.end(),var,CompareVariableName),var);
}

void functionUser::AddOperation(operation *op){
	listOps.push_back(op);
}

/**
 * Instanciated a function without functionUserInst
 * It copies graph and modify pointers
 * WARNING : this destroy op
 **/
void functionUser::AddInstFunctionUser(functionUser *fun, operation *op){
    map<variable *, variable *> newVar;
    // this map maps variable from functionUser to real variables

    // We destroy remove last added op -> it's op normally !
    listOps.pop_back();

    // First we take care of inputs, outputs
	vector<variable *>::iterator it1,it2;

    // Map inputs to new inputs
	it1 = fun->inputs.begin();
	it2 = op->inputs.begin();
	for( ; it1!=fun->inputs.end() && it2!=op->inputs.end() ; it1++, it2++){
	    // We need to remove the last ops added to input because it is a pointer to op and op will be destroyed !
	    (*it2)->ops.pop_back();
	    newVar[*it1] = *it2;
	}

    // Map outputs to new outputs
	it1 = fun->outputs.begin();
	it2 = op->outputs.begin();
	for( ; it1!=fun->outputs.end() && it2!=op->outputs.end() ; it1++, it2++){
	    newVar[*it1] = *it2;
	}

	// Map 0/1 variables
	newVar[&(fun->var0)] = &var0;
	newVar[&(fun->var1)] = &var1;

	// Then create new variables for internal graph of the function
	it1 = fun->listVars.begin();
	for( ; it1!=fun->listVars.end() ; it1++){
	    // This variable is not an input nor an output
	    if(newVar.find(*it1)==newVar.end()){
	        variable *var;
	        var = new variable((*it1)->GetName());
	        newVar[*it1]=var;
	        AddVariable(var);
	    }
	}

	// Now we can copy combinatorial graph !
	vector<operation *>::iterator it;
	it = fun->listOps.begin();
	for( ; it!=fun->listOps.end() ; it++){
	    listOps.push_back(new operation(*it,newVar,op->line));
	}

	// And do not forget registers !
    vector<registerV *>::iterator itR;
    for(itR=fun->registers.begin();itR!=fun->registers.end();itR++){
        registers.push_back(new registerV(newVar[(*itR)->input], newVar[(*itR)->output]));
    }

	delete op;
}

void functionUser::AddRegister(registerV *reg){
	registers.push_back(reg);
}

void functionUser::DeleteVariable(variable *var){
	listVars.erase(find(listVars.begin(),listVars.end(),var));
	delete var;
}

/**
 * Get the variable of name name if it exists and if name!=""
 * Otherwise get a new variable if create is true
 * Complexity : O(ln n) (for each list) because list are sorted !!
 **/
variable *functionUser::GetVariable(string name, bool create){
    if(name=="0"){
        return &var0;
    }else if(name=="1"){
        return &var1;
	}

	vector<variable *>::iterator it;
	variable *var;
	var = new variable(name);

	if(name!=""){
		// name is in variables ?
		it = lower_bound(listVars.begin(),listVars.end(),var,CompareVariableName);
		if(it!=listVars.end()  && (*it)->name==name){
			delete var;
			return (*it);
		}
	}else{
		it = listVars.begin();
	}

	if(create){
		// We create a new variable
		// Notice that we do not use AddVariable because we know where to place the new variable !
		listVars.insert(it,var);

		return var;
	}else{
		return 0;
	}
}

short functionUser::GetNumberOfInputs(){
	return inputs.size();
}

short functionUser::GetNumberOfOutputs(){
	return outputs.size();
}

bool functionUser::IsInput(variable *v){
	return find(inputs.begin(),inputs.end(),v)!=inputs.end();
}

bool functionUser::DecreaseOpsIndegree(variable *var, unsigned short &nbElt, string &error){
    ostringstream str;

    for(vector<operation *>::iterator it2=var->ops.begin();it2!=var->ops.end();it2++){
        (*it2)->indegree--;
        if((*it2)->indegree<0){
            str << "there is a combinatorial cycle at line " << (*it2)->line << ".";
            error = str.str();
            return false;
        }
        if((*it2)->indegree==0){
            sortedListOps.push_back(*it2);
            nbElt++;
        }
    }
    return true;
}

/**
 * Sort list of operations
 * Return true if all is OK
 * Return false if there is a problem
 * Give an error message or a warning message through error
 **/
bool functionUser::SortListOps(string &error){

	ostringstream str;

	unsigned short nbElt=0;	// nbElt : number of sorted operation
	unsigned short nbNonUsedVariables=0;

	sortedListOps.clear();
	sortedListOps.reserve(listOps.size());

	// Initialized used=false in variable
	for(vector<variable *>::iterator it=listVars.begin();it!=listVars.end();it++){
	    (*it)->used = false;
	    nbNonUsedVariables++;
	}

	// Initialize indegree
	// Operations with a functionInst without inputs can be made directly (operations with indegree==0

	for(vector<operation *>::iterator it=listOps.begin();it!=listOps.end();it++){
		(*it)->indegree = (*it)->inputs.size();

		if((*it)->indegree==0){
			sortedListOps.push_back(*it);
			nbElt++;
		}
	}

	// Operation with inputs or ouputs of registers or 0/1 vairable can be made directly

	for(vector<variable *>::iterator it=inputs.begin();it!=inputs.end();it++){
        if((*it)->used==false)nbNonUsedVariables--;
        (*it)->used = true;
        if(!DecreaseOpsIndegree(*it,nbElt,error)){
            return false;
        }
	}

	for(vector<registerV *>::iterator it=registers.begin();it!=registers.end();it++){
        if((*it)->output->used==false)nbNonUsedVariables--;
        (*it)->output->used = true;
        if(!DecreaseOpsIndegree((*it)->output,nbElt,error)){
            return false;
        }
	}

    if(!DecreaseOpsIndegree(&var0,nbElt,error)){
        return false;
    }

    if(!DecreaseOpsIndegree(&var1,nbElt,error)){
        return false;
    }

	// We add an operation in sorted list when all its inputs are already computed (i.e. indegree==0)

	short i;
	for(i=0;i<nbElt;i++){
		for(vector<variable *>::iterator it=sortedListOps[i]->outputs.begin();it!=sortedListOps[i]->outputs.end();it++){
		    if((*it)->used==false)nbNonUsedVariables--;
		    (*it)->used = true;
            if(!DecreaseOpsIndegree(*it,nbElt,error)){
                return false;
            }
		}
	}

	if(nbElt<listOps.size() || nbNonUsedVariables>0){
	    if(nbElt<listOps.size()){
            str << "only " << nbElt << "/" << listOps.size() << " operations are useful - ";

            set<unsigned int> linesUseless;

            for(vector<operation *>::iterator it=listOps.begin();it!=listOps.end();it++){
                if((*it)->indegree){
                    linesUseless.insert((*it)->line);
                }
            }
            if(linesUseless.size()>1){
                str << "operations at lines ";
            }else{
                str << "operations at line ";
            }
            for(set<unsigned int>::iterator it=linesUseless.begin() ; it!=linesUseless.end() ; it++){
                if(it!=linesUseless.begin()){
                    str << ", ";
                }
                str << (*it);
            }
            str << " are useless ; ";
	    }
	    if(nbNonUsedVariables>0){
	        str << nbNonUsedVariables << " variables are not used, in particular : ";
	        bool first = true;
            for(vector<variable *>::iterator it=listVars.begin();it!=listVars.end();it++){
                if((*it)->used == false && !((*it)->IsTemp())){
                    if(!first) str << ", ";
                    else first=false;
                    str << (*it)->GetName();
                }
            }

            // Find variable never output (that means that are not output by any operation even useless operation)
            // Used mean here : output

            nbNonUsedVariables = 0;

            // Initialized used=false in variable
            for(vector<variable *>::iterator it=listVars.begin();it!=listVars.end();it++){
                (*it)->used = false;
                nbNonUsedVariables++;
            }

            // Input variable are output
            for(vector<variable *>::iterator it=inputs.begin();it!=inputs.end();it++){
                if((*it)->used==false)nbNonUsedVariables--;
                (*it)->used = true;
            }

            // Output of registers are output
            for(vector<registerV *>::iterator it=registers.begin();it!=registers.end();it++){
                if((*it)->output->used==false)nbNonUsedVariables--;
                (*it)->output->used = true;
            }

            // Output of function are output
            for(vector<operation *>::iterator it=listOps.begin();it!=listOps.end();it++){
                for(vector<variable *>::iterator it2=(*it)->outputs.begin();it2!=(*it)->outputs.end();it2++){
                    if((*it2)->used==false)nbNonUsedVariables--;
                    (*it2)->used = true;
                }
            }

            if(nbNonUsedVariables>0){
                str << endl << nbNonUsedVariables << " variables are never output by any operation : ";
                bool first = true;
                for(vector<variable *>::iterator it=listVars.begin();it!=listVars.end();it++){
                    if((*it)->used == false && !((*it)->IsTemp())){
                        if(!first) str << ", ";
                        else first=false;
                        str << (*it)->GetName();
                    }
                }
            }
	    }

        error = str.str();
	}

	return true;
}

// Evaluation function

/**
 * Evaluate combinatorial operations
 * SortListOps need to be called before
 **/
void functionUser::Eval(){
    for(vector<operation*>::iterator it=sortedListOps.begin();it!=sortedListOps.end();it++){
        (*it)->fun->Eval();
    }
}
/**
 * USE only for main function
 **/
void functionUser::MainNextClockEdge(){
	for(vector<registerV *>::iterator it=registers.begin() ; it!=registers.end() ; it++){
		(*it)->SaveRegister();
	}
	for(vector<registerV *>::iterator it=registers.begin() ; it!=registers.end() ; it++){
		(*it)->NextClockEdge();
	}
}

// Functions for creating a C program

// prefix is used to differentiate variable for multiple instances of a same function

void functionUser::CPutDeclarations(ostream &f, const string prefix){
    unsigned short i=0;
	for(i=0;i<listVars.size();i++){
		listVars[i]->id = i;
		f << "char " << prefix << "_" << i << "=" << listVars[i]->GetValue() << ";" << endl;
	}

	var0.id = i;
	f << "char " << prefix << "_" << i << "=" << var0.GetValue() << ";" << endl;
	i++;

	var1.id = i;
	f << "char " << prefix << "_" << i << "=" << var1.GetValue() << ";" << endl;
	i++;


	for(i=0;i<registers.size();i++){
		f << "char " << prefix << "_" << registers[i]->input->id << "r=0;" << endl;
	}
	for(i=0;i<sortedListOps.size();i++){
		ostringstream str;
		str << prefix << "_" << i;
		sortedListOps[i]->fun->CPutDeclarations(f,string(str.str()));
	}
}

void functionUser::CPutCommands(ostream &f, const string prefix){
	for(unsigned short i=0;i<sortedListOps.size();i++){
		ostringstream str;
		str << prefix << "_" << i;
		sortedListOps[i]->fun->CPutCommands(f,prefix,string(str.str()));
	}
}

// ONLY FOR MAIN FUNCTION !!
void functionUser::CPutRegistersSave(ostream &f, const string prefix){
	for(vector<registerV *>::iterator it=registers.begin() ; it!=registers.end() ; it++){
		f << "\t\t" << prefix << "_" << (*it)->input->id << "r=" << prefix << "_" << (*it)->input->id  << ";" << endl;
	}

	for(unsigned short i=0;i<sortedListOps.size();i++){
		ostringstream str;
		str << prefix << "_" << i;
		sortedListOps[i]->fun->CPutRegistersSave(f,string(str.str()));
	}
}

void functionUser::CPutRegistersNext(ostream &f, const string prefix){
	for(vector<registerV *>::iterator it=registers.begin() ; it!=registers.end() ; it++){
		f << "\t\t" << prefix << "_" << (*it)->output->id << "=" << prefix << "_" << (*it)->input->id  << "r;" << endl;
	}

	for(unsigned short i=0;i<sortedListOps.size();i++){
		ostringstream str;
		str << prefix << "_" << i;
		sortedListOps[i]->fun->CPutRegistersNext(f,string(str.str()));
	}
}
