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

#include <iostream>
#include <algorithm>
using namespace std;


// Static members

short functionMem::lastId=-1;
vector<functionMem *> functionMem::listMems;


// ------
// Function mem class
// ------

functionMem::functionMem(string name_, bool readOnly_, operation *op) :
	functionInst(op){

	name = name_;
	readOnly = readOnly_;
	lastId++;
	id = lastId;
	alreadyPutDeclarations = false;

    listMems.push_back(this);
}

functionMem::~functionMem(){
    listMems.erase(find(listMems.begin(),listMems.end(),this));
	if(op){
		op->fun = 0;
		// We do not destroy op
	}
}

functionInst *functionMem::Copy(){
    functionMem *fun = new functionMem(*this);
    lastId++;
    listMems.push_back(fun);
	fun->id = lastId;
    return fun;
}

/**
 * WARNING : DO NOT FORGET TO CALL THIS FUNCTION AFTER CREATING functionMem
 * Verify that IO numbers is good
 * Initialize memory
 * Create the data vector
 **/
bool functionMem::VerifyNumberOfIO(){
    dataLength = op->GetNumberOfOutputs();

    short nb = op->GetNumberOfInputs();

    if(readOnly){
        if(nb<=31){
            addrLength = nb;
        }else{
            return false;
        }
    }else{
        nb--;
        nb -= dataLength;
        if(nb<0 || nb>31*2 || nb%2==1){
            return false;
        }else{
            addrLength = nb/2;
        }
    }

    data.assign(1<<addrLength,0);

    return true;
}

void functionMem::Eval(){
    unsigned long addr=0;
    unsigned long value=0;

    // Read
    for(short i=0;i<addrLength;i++){
        addr <<=1;
        addr |= (op->inputs[i]->GetValue() ? 1 : 0);
    }
    value = data[addr];
    for(short i=dataLength-1;i>=0;i--){
        op->outputs[i]->SetValue(value & 1);
        value >>=1;
    }

    if(!readOnly && op->inputs[addrLength]->GetValue()){
        // Write (if WE=1)
        addr = 0;
        value = 0;
        for(short i=addrLength+1;i<2*addrLength+1;i++){
            addr <<=1;
            addr |= (op->inputs[i]->GetValue() ? 1 : 0);
        }
        for(short i=2*addrLength+1;i<2*addrLength+dataLength+1;i++){
            value <<=1;
            value |= (op->inputs[i]->GetValue() ? 1 : 0);
        }
        data[addr] = value;
    }
}

void functionMem::CPutDeclarations(ostream &f, const string newPrefix){
    if(alreadyPutDeclarations)return;
    alreadyPutDeclarations = true;

	f << "unsigned long mem_" << id << "[" << (1<<addrLength) << "]={";
	for(unsigned long i=0;i<(((unsigned long)1)<<addrLength);i++){
        if(i){
            f << ", ";
        }
        f << data[i];
	}
	f << "};" << endl;
	//f << "unsigned long mem_val=0, mem_addr=0;" << endl;
}

void functionMem::CPutCommands(ostream &f, const string prefix, const string newPrefix){

    // Read
    f << "\t\tmem_addr=0;" << endl;
    for(short i=0;i<addrLength;i++){
        f << "\t\tmem_addr<<=1;" << endl;
        f << "\t\tmem_addr|=" << prefix << "_" << op->inputs[i]->id << " & 1;" << endl;
    }
    f << "\t\tmem_val=mem_" << id << "[mem_addr];" << endl;
    for(short i=dataLength-1;i>=0;i--){
        f << "\t\t" << prefix << "_" << op->outputs[i]->id << "=mem_val & 1;" << endl;
        f << "\t\tmem_val>>=1;" << endl;
    }

    if(!readOnly){
        // Write (if WE=1)
        f << "\t\tif(" << prefix << "_" << op->inputs[addrLength]->id <<"){" << endl;
        f << "\t\t\tmem_addr=0;" << endl;
        for(short i=addrLength+1;i<2*addrLength+1;i++){
            f << "\t\t\tmem_addr<<=1;" << endl;
            f << "\t\t\tmem_addr|=" << prefix << "_" << op->inputs[i]->id << " & 1;" << endl;
        }
        f << "\t\t\tmem_val=0;" << endl;
        for(short i=2*addrLength+1;i<2*addrLength+dataLength+1;i++){
            f << "\t\t\tmem_val<<=1;" << endl;
            f << "\t\t\tmem_val|=" << prefix << "_" << op->inputs[i]->id << " & 1;" << endl;
        }
        f << "\t\t\tmem_" << id << "[mem_addr]=mem_val;" << endl;
        f << "\t\t}" << endl;
    }
}
