#include "StaticNonLinearity.h"

StaticNonLinearity::StaticNonLinearity(int x, int y, double temporal_step, int t):module(x,y,temporal_step){
    type = t;
    isThreshold = false;
    
    inputImage=new CImg<double> (sizeY,sizeX,1,1,0.0);
    outputImage=new CImg<double> (sizeY,sizeX,1,1,0.0);
    markers=new CImg<double> (sizeY,sizeX,1,1,0.0);
}

StaticNonLinearity::StaticNonLinearity(const StaticNonLinearity& copy):module(copy){
    type = copy.type;
    isThreshold = copy.isThreshold;
    
    inputImage=new CImg<double>(*(copy.inputImage));
    outputImage=new CImg<double>(*(copy.outputImage));
    markers=new CImg<double>(*(copy.markers));
}

StaticNonLinearity::~StaticNonLinearity(void){
    delete inputImage;
    delete outputImage;
    delete markers;
}

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


// Set functions
void StaticNonLinearity::setSlope(double s, int segment){
    slope[segment] = s;
}

void StaticNonLinearity::setOffset(double o, int segment){
    offset[segment] = o;
}

void StaticNonLinearity::setExponent(double e, int segment){
    exponent[segment] = e;
}

void StaticNonLinearity::setThreshold(double t, int segment){
    threshold[segment] = t;
    isThreshold = true;
}

void StaticNonLinearity::setType(int t){
    type = t;
}

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

bool StaticNonLinearity::allocateValues(){
    inputImage->assign(sizeY,sizeX,1,1,0.1);
    outputImage->assign(sizeY,sizeX,1,1,0.1);
    markers->assign(sizeY,sizeX,1,1,0.1);
    return(true);
}

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

void StaticNonLinearity::update(){  

    // polynomial function
    if(type==0){

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


        (*inputImage)*=slope[0];
        (*inputImage)+=offset[0];
        inputImage->pow(exponent[0]);
    }

    // piecewise function
    else if(type==1){
            markers->fill(0.0);
            for(size_t k=0;k<slope.size();k++){
                cimg_forXY((*inputImage),x,y) {
                    if((*inputImage)(x,y,0,0) >= start[k] && (*inputImage)(x,y,0,0) < end[k] && (*markers)(x,y,0,0)==0.0){
                        (*inputImage)(x,y,0,0)*=slope[k];
                        (*inputImage)(x,y,0,0)+=offset[k];
                        (*inputImage)(x,y,0,0) = pow((*inputImage)(x,y,0,0),exponent[k]);
                        (*markers)(x,y,0,0)=1.0;

                    }
                }

            }
    }

    // Symmetric sigmoid (only for negative values)
    else if(type==2){
        double absVal = 0.0;
        cimg_forXY((*inputImage),x,y) {
            absVal = abs((*inputImage)(x,y,0,0));
            (*inputImage)(x,y,0,0) = sgn<double>((*inputImage)(x,y,0,0))*(exponent[0] / (1.0 + exp(-absVal*slope[0] + offset[0])));
        }

    }

    // Standard sigmoid
    else if(type==3){
        double value = 0.0;
        cimg_forXY((*inputImage),x,y) {
            value = (*inputImage)(x,y,0,0);
            (*inputImage)(x,y,0,0) = (exponent[0] / (1.0 + exp(-value*slope[0] + offset[0])));
        }

    }

    *outputImage = *inputImage;
}

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

bool StaticNonLinearity::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){
            slope.push_back(params[i]);
        }else if (strcmp(s,"offset")==0){
            offset.push_back(params[i]);
        }else if (strcmp(s,"exponent")==0){
            exponent.push_back(params[i]);
        }else if (strcmp(s,"max")==0){
            exponent.push_back(params[i]);
        }else if (strcmp(s,"threshold")==0){
            threshold.push_back(params[i]);
            isThreshold = true;
        }
        else if (strcmp(s,"start")==0){
            start.push_back(params[i]);
        }
        else if (strcmp(s,"end")==0){
            end.push_back(params[i]);
        }
        else{
              correct = false;
        }
    }

    return correct;
}

void StaticNonLinearity::clearParameters(vector<string> paramID){

    for (size_t i = 0;i<paramID.size();i++){
        const char * s = paramID[i].c_str();

        if (strcmp(s,"slope")==0){
            slope.clear();
        }else if (strcmp(s,"offset")==0){
            offset.clear();
        }else if (strcmp(s,"exponent")==0){
            exponent.clear();
        }else if (strcmp(s,"max")==0){
            exponent.clear();
        }else if (strcmp(s,"threshold")==0){
            threshold.clear();
        }
        else if (strcmp(s,"start")==0){
            start.clear();
        }
        else if (strcmp(s,"end")==0){
            end.clear();
        }
    }
}

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


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

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

template <typename T> int StaticNonLinearity::sgn(T val) {
    return (T(0) < val) - (val < T(0));
}
