#include "ShortTermPlasticity.h"

ShortTermPlasticity::ShortTermPlasticity(int x,int y,double temporal_step,double Am,double Vm,double Em, double th, bool isTh):module(x,y,temporal_step){
    slope = Am;
    offset = Vm;
    exponent = Em;

    kf = 0.0;
    kd = 0.0;
    tau = 1.0;

    if(isTh){
        isThreshold = true;
        threshold = th;
    }else{
        isThreshold = false;
        threshold = 0.0;
    }
    
    inputImage = new CImg<double>*[7];
    for (int i=0;i<7;i++)
        inputImage[i]=new CImg<double>(sizeY,sizeX,1,1,0.0);
    outputImage=new CImg<double> (sizeY,sizeX,1,1,0.0);
}

ShortTermPlasticity::ShortTermPlasticity(const ShortTermPlasticity& copy):module(copy){
    slope=copy.slope;
    offset=copy.offset;
    exponent=exponent;

    kf=copy.kf;
    kd=copy.kd;
    tau=copy.tau;

    isThreshold=copy.isThreshold;
    threshold=copy.threshold;
    
    inputImage = new CImg<double>*[7];
    for (int i=0;i<7;i++)
        inputImage[i]=new CImg<double>(*(copy.inputImage[i]));
    outputImage=new CImg<double> (*(copy.outputImage));
}

ShortTermPlasticity::~ShortTermPlasticity(){
    for (int i=0;i<7;i++)
        delete inputImage[i];
    delete[] inputImage;
    delete outputImage;
}

//------------------------------------------------------------------------------//


// Set functions

bool ShortTermPlasticity::setSlope(double s){
    slope = s;
    return(true);
}

bool ShortTermPlasticity::setOffset(double o){
    offset = o;
    return(true);
}

bool ShortTermPlasticity::setExponent(double e){
    exponent = e;
    return(true);
}

bool ShortTermPlasticity::setThreshold(double t){
    threshold = t;
    isThreshold = true;
    return(true);
}

bool ShortTermPlasticity::setkf(double p1){
    kf = p1;
    return(true);
}

bool ShortTermPlasticity::setkd(double p2){
    kd = p2;
    return(true);
}

bool ShortTermPlasticity::setTau(double p3){
    tau = p3;
    return(true);
}


//------------------------------------------------------------------------------//

bool ShortTermPlasticity::allocateValues(){
    // Resize buffer images to current retina size
    for (int i=0;i<7;i++)
        inputImage[i]->assign(sizeY, sizeX, 1, 1, 0.1);
    outputImage->assign(sizeY, sizeX, 1, 1, 0.1);
    
    // exp(-step/tau)
    inputImage[5]->fill(-step/tau);
    inputImage[5]->exp();

    return(true);
}

void ShortTermPlasticity::feedInput(double sim_time, const CImg<double>& new_input, bool isCurrent, int port){
    // copy input image
    *inputImage[0] = new_input;
}

void ShortTermPlasticity::update(){
    // kmInf = (kd/(abs(input)))
    // km(t+1) = kmInf + [km(t) - kmInf]*exp(-step/tau)
    // P = P + kf*(km*abs(input) - P)

    // ImagePointer[0] -> input image
    // ImagePointer[1] -> abs(input image)
    // ImagePointer[2] -> P
    // ImagePointer[3] -> km(t)
    // ImagePointer[4] -> kmInf
    // ImagePointer[5] -> exp(-step/tau)
    // ImagePointer[6] -> km(t-1)

    // km(t-1)
    (*(inputImage[6]))=(*(inputImage[3]));
    (*(inputImage[3])).fill(0.0);

    // abs(input)
    *inputImage[1] = *inputImage[0];
    (inputImage[1])->abs();
    (*inputImage[1])+= DBL_EPSILON_STP;

    // kmInf
    (inputImage[4])->fill(kd);
    (inputImage[4])->div((*(inputImage[1])));

    // update of km(t)
    (*inputImage[3]) += (*inputImage[4]);
    (*inputImage[3]) -= (*(inputImage[4])).mul(*(inputImage[5]));
    (*inputImage[3]) += (*(inputImage[6])).mul(*(inputImage[5]));

    // km(t)*abs(input)
    (*inputImage[1]) = *inputImage[0];
    (inputImage[1])->abs();
    (inputImage[1])->mul(*inputImage[3]);

    // update of P
    (*inputImage[2])+= kf*(*inputImage[1] - *inputImage[2]);

    // Threshold
    if(isThreshold){
        cimg_forXY((*inputImage[0]),x,y) {
            if((*inputImage[0])(x,y,0,0) < threshold)
                (*inputImage[0])(x,y,0,0) = threshold;
        }
    }

    // slope, constant offset and exponent
    (*inputImage[0])*=slope;
    (*inputImage[0])+=offset;
    (*inputImage[0])+=(*inputImage[2]);
    inputImage[0]->pow(exponent);

    *outputImage = *inputImage[0];
}

//------------------------------------------------------------------------------//

bool ShortTermPlasticity::setParameters(vector<double> params, vector<string> paramID){

    bool correct = true;

    for (vector<double>::size_type i = 0;i < params.size() && correct;i++){
        const char * s = paramID[i].c_str();

        if (strcmp(s,"slope")==0){
            correct = setSlope(params[i]);
        }
        else if (strcmp(s,"offset")==0){
            correct = setOffset(params[i]);
        }
        else if (strcmp(s,"exponent")==0){
            correct = setExponent(params[i]);
        }
        else if (strcmp(s,"threshold")==0){
            correct = setThreshold(params[i]);
        }
        else if (strcmp(s,"kf")==0){
            correct = setkf(params[i]);
        }
        else if (strcmp(s,"kd")==0){
            correct = setkd(params[i]);
        }
        else if (strcmp(s,"tau")==0){
            correct = setTau(params[i]);
        }
        else{
              correct = false;
        }

    }

    return correct;
}

//------------------------------------------------------------------------------//

CImg<double>* ShortTermPlasticity::getOutput(){
    return outputImage;
}
