#include "AdvDeltaConv.hh"
#include <cassert>
#include <iostream>
#include <ostream>
#include <cmath>
#include <numeric>
#include <cfloat>

/** default constructor */
AdvDeltaConv::AdvDeltaConv() : AdvFuncBase(*(new string("DeltaConv")), *(new string("dc")), NumberOfParamForDeltaConv) {
}

/** copy constructor */
AdvDeltaConv::AdvDeltaConv(const AdvDeltaConv &) {
}

/** destructor */
AdvDeltaConv::~AdvDeltaConv() {
}

/**
 *  evaluate the value of the function
 *
 *  \param[in] x
 *  \param[in] h the height of the function
 *  \param[in] c the center of the function
 *  \param[in] w the half-value half width of the function
 *
 */


#ifdef HAVE_DIFFERENTIAL_MEMBER
/**
 *  evaluate the value of the 1st. differential coefficient
 */
Double AdvDeltaConv::der1st(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

/*
 *  evaluate the value of the 2nd. differential coefficient
 */
Double AdvDeltaConv::der2nd(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  d
 *  --G(x, h, c, w)
 *  dx
 */
Double AdvDeltaConv::derW(const Double x, const Double h, const Double c, const Double w) {
    std::cout << "Convolution::derW is not implemented" << endl;
    return DBL_MAX;
}

#endif // HAVE_DIFFERENTIAL_MEMBER

#ifdef USE_POINTER
/**
 *  evaluate the value of the function
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */

Double AdvDeltaConv::DeltaFunc(const Double x, const Double *p) {
    if(x==p[1])
        {
        return p[0];
        }
    else
        {
        return 0;
        }
}

Double AdvDeltaConv::DeltaConvFunc(const Double x, const Double *p) {

    if(ResY.size()==0) ReadResolutionFile();
/*
    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (int i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = DeltaFunc(x-ResX.at(i), *p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
*/

    int num_i=0;
    Double normFactor = 0.0;
    for (int i=0;i<ResX.size()-2;i++)
        {
        Double x_res = (ResX.at(i)+ResX.at(i+1))*0.5;
        //normFactor += ResY.at(i);
        normFactor += ResY.at(i)*(ResX.at(i+2)-ResX.at(i))*0.5;
        if(x_res < x -p[1])
            {
            num_i = i;
            }
        }

if(num_i==0 || num_i==(ResX.size()-3))
{
return 0;
}
        double diff_x;
        if(num_i == ResX.size()-2){
            diff_x = (ResX.at(ResX.size()) - ResX.at(ResX.size()-2))*0.5;
        }else{
            diff_x = (ResX.at(num_i+2) - ResX.at(num_i))*0.5;
         }

        double diff_x_data = x -p[1] - (ResX.at(num_i)+ResX.at(num_i+1))*0.5;
        double diff_y = ResY.at(num_i+1) - ResY.at(num_i);

        return (ResY.at(num_i)+ diff_y*diff_x_data/diff_x)*p[0]/normFactor;
        //return (ResY.at(num_i)+ diff_y*diff_x_data/diff_x)*p[0];
}

Double AdvDeltaConv::eval(const Double x, const Double *p) {
    return DeltaConvFunc(x, *p);
}

/**
 *  evaluate the value of the 1st. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvDeltaConv::der1st(const Double x, const Double *p) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  evaluate the value of the 2nd. differential coefficient
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 */
Double AdvDeltaConv::der2nd(const Double x, const Double *p) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

/**
 *  evaluate the gradient of the coefficient for parameters
 *
 *  @param[in] x  argument of the function
 *  @param[in] p  parameters of the function
 *
 */
Double *AdvDeltaConv::gradient(const Double x, const Double *p) {
//    std::cout << "Convolution::gradient is not implemented" << endl;
    return NULL;
}

#endif // USE_POINTER

#ifdef USE_VECTOR
/**
 *  evaluate the value of the function
 *
 *  \param[in] x  argument of the function
 *  \param[in] p  parameters of the function
 */

Double AdvDeltaConv::DeltaFunc(const Double x, const vector<Double> &p) {
    if(x==p[1])
        {
        return p.at(0);
        }
    else
        {
        return 0;
        }
}

Double AdvDeltaConv::AdvDeltaConvFunc(const Double x, const vector<Double> &p) {

    if(ResY.size()==0) ReadResolutionFile();
/*
    Double normFactor = 0.0;
    Double conv_result = 0.0;
    for (int i=0;i<ResX.size()-1;i++)
        {
        Double f_res = ResY.at(i);
        Double f_det = DeltaFunc(x-ResX.at(i), p);
        conv_result += f_det*f_res;
        normFactor += ResY.at(i);
        }
     return conv_result/normFactor;
*/
    int num_i=0;
    Double normFactor = 0.0;
    for (int i=0;i<ResX.size()-2;i++)
        {
        Double x_res = (ResX.at(i)+ResX.at(i+1))*0.5;
        //normFactor += ResY.at(i);
        normFactor += ResY.at(i)*(ResX.at(i+2)-ResX.at(i))*0.5;
        if(x_res < x-p[1])
            {
            num_i = i;
            }
        }

if (num_i==0 || num_i==(ResX.size()-3))
{
return 0;
}

        double diff_x;
        if (num_i == ResX.size()-1){
            diff_x = (ResX.at(ResX.size()) - ResX.at(ResX.size()-2))*0.5;
        }else{
            diff_x = (ResX.at(num_i+2) - ResX.at(num_i))*0.5;
         }

        double diff_x_data = x -p[1] - (ResX.at(num_i)+ResX.at(num_i+1))*0.5;
        double diff_y = ResY.at(num_i+1) - ResY.at(num_i);
        return  (ResY.at(num_i)+ diff_y*diff_x_data/diff_x)*p[0]/normFactor;
//        return  (ResY.at(num_i)+ diff_y*diff_x_data/diff_x)*p[0];

}

Double AdvDeltaConv::eval(const Double x, const vector<Double> &p) {
        return AdvDeltaConvFunc(x, p);
}

Double AdvDeltaConv::der1st(const Double x, const vector<Double> &p) {
    std::cout << "Convolution::der1st is not implemented" << endl;
    return DBL_MAX;
}

Double AdvDeltaConv::der2nd(const Double x, const vector<Double> &p) {
    std::cout << "Convolution::der2nd is not implemented" << endl;
    return DBL_MAX;
}

vector<Double> AdvDeltaConv::gradient(const Double x, const vector<Double> &p) {
//    std::cout << "Convolution::gradient is not implemented" << endl;
    vector<Double> v;
    v.clear();
    return v;}
#endif // USE_VECTOR
