#include "AdvPeakSearch.hh"

// class name
const std::string AdvPeakSearch::className = std::string("AdvPeakSearch");

/**
 *  default constructor
 */
AdvPeakSearch::AdvPeakSearch() {
    std::string memberName=std::string("PeakSerach()");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
};

/**
 *  constructor
 *
 *  \param[in] src  an element container as source
 */
AdvPeakSearch::AdvPeakSearch(ElementContainer *src) : AdvOperationBase(src) {
    std::string memberName=std::string("PeakSerach(ElementContainer *)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

/**
 *  constructor
 *
 *  \param[in] src    an element container as source
 *  \param[in] method an algorithm for smoothing while peak search
 */
#ifndef SWIGPYTHON
AdvPeakSearch::AdvPeakSearch(ElementContainer *src,       AdvMethod *method        ) : AdvOperationBase(src, method) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethod *)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}
#endif

/**
 *  constructor
 *
 *  \param[in] src  an element container as source
 *  \param[in] type an algorithm for smoothing while peak search
 */
AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const AdvMethodType &methodType) : AdvOperationBase(src, methodType) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethodType &)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

/**
 *  constructor
 *
 *  \param[in] src        an element container as source
 *  \param[in] methodName an algorithm for smoothing while peak search
 */
AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const std::string     &methodName) : AdvOperationBase(src, methodName) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, std::string &)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

/**
 *  constructor
 *
 *  \param[in] src        an element container as source
 *  \param[in] methodName an algorithm for smoothing while peak search
 *  \param[in] xLower     the lower bound of the domain
 *  \param[in] xUpper     the upper bound of the domain
 */
#ifndef SWIGPYTHON
AdvPeakSearch::AdvPeakSearch(ElementContainer *src,       AdvMethod *method,         const Double xLower, const Double xUpper) : AdvOperationBase(src, method, xLower, xUpper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethod *, Double, Double)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}
#endif

AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const AdvMethodType &methodType, const Double xLower, const Double xUpper) : AdvOperationBase(src, methodType, xLower, xUpper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethodType &, Double, Double)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const std::string &methodName,     const Double xLower, const Double xUpper) : AdvOperationBase(src, methodName, xLower, xUpper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, std::string &, Double, Double)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

#ifndef SWIGPYTHON
AdvPeakSearch::AdvPeakSearch(ElementContainer *src,        AdvMethod *method,        const UInt4  lower,  const UInt4  upper) : AdvOperationBase(src, method,     lower, upper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethod *, UInt4, UInt4)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}
#endif

AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const AdvMethodType &methodType, const UInt4  lower,  const UInt4  upper) : AdvOperationBase(src, methodType, lower, upper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, AdvMethodType &, UInt4, UInt4)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

AdvPeakSearch::AdvPeakSearch(ElementContainer *src, const std::string &methodName,     const UInt4  lower,  const UInt4  upper) : AdvOperationBase(src, methodName, lower, upper) {
    std::string memberName=std::string("PeakSerach(ElementContainer *, std::string &, UInt4, UInt4)");
    DebugMessage(className, memberName, "enter\n");
    DebugMessage(className, memberName, "exit\n");
}

AdvPeakSearch::~AdvPeakSearch() {
};

void AdvPeakSearch::execute() {

    std::string memberName=std::string("execute()");

    if (! this->method->checkParam(*(this->source), this->domain, this->param) ) {
        return;
    }

    this->method->toInnerForm(*(this->source), this->domain, this->param);
    this->method->fit();
    //this->method->eval();

    // incluease-decrease table
    std::vector< std::vector<Double> > trend=this->method->getTrend();
    //this->resultY=new std::vector<Double>(trend[0].begin, trend[0].end());
    DebugMessage(className, memberName, "getTrend\n");

    // search peaks and sholder
    std::string xKey=this->source->PutXKey();
    std::string yKey=this->source->PutYKey();
    this->peakData=new AdvPeakData();
    Double x, xl, xu, h, w;
    for (UInt4 i=0; i<trend[0].size()-1; ++i) {
        if        ( isPeak(trend, i) ) {       // found a peak
            // the position of the peak
            x=this->source->Put(xKey, i+1);
            // the height of the peak
            h=(trend[0].at(i) + trend[0].at(i+1))/2.0;

            xl=searchHalfHeightInIncreaseRegion(trend, this->source->Put(xKey), i,   h);
            xu=searchHalfHeightInDecreaseRegion(trend, this->source->Put(xKey), i+1, h);
            w=std::max(x-xl, xu-x);

            DebugMessage(className, memberName, "%-2s x=%10.5f, xl=%10.5f, xr=%10.5f, h=%23.15e w=%10.5f\n", "p", x, xl, xu, h, w);
            peakData->add(PEAK, x, h, w, xl, xu);
        } else if (isSholderInIncreaseRegion(trend, i)) {
            x=this->source->Put(xKey, i+1);
            h=(trend[0].at(i) + trend[0].at(i+1))/2.0;

            xl=searchHalfHeightInIncreaseRegion(trend, this->source->Put(xKey), i,   h);
            xu=x;
            w=x-xl;

            DebugMessage(className, memberName, "%-2s x=%10.5f, xl=%10.5f, xr=%10.5f, h=%23.15e w=%10.5f\n", "si", x, xl, xu, h, w);
            peakData->add(SHOLDER_I, x, h, w, xl, xu);
        } else if (isSholderInDecreaseRegion(trend, i)) {
            x=this->source->Put(xKey, i+1);
            h=(trend[0].at(i) + trend[0].at(i+1))/2.0;

            xl=x;
            xu=searchHalfHeightInDecreaseRegion(trend, this->source->Put(xKey), i+1, h);
            w=xu-x;

            DebugMessage(className, memberName, "%-2s x=%10.5f, xl=%10.5f, xr=%10.5f, h=%23.15e w=%10.5f\n", "sd", x, xl, xu, h, w);
            peakData->add(SHOLDER_D, x, h, w, xl, xu);
        }
    }
}

/**
 *  @param[in]  trend  increase-decrease teble
 *  @param[in]  pos    index on the table
 *  @retval true if peak condition is sattisfied
 */
Bool AdvPeakSearch::isPeak(std::vector< std::vector<Double> > &trend, const UInt4 pos) const {
    return (trend[1].at(pos) > 0.0 && 0.0 > trend[1].at(pos+1));
}

/**
 *  @param[in]  trend  increase-decrease teble
 *  @param[in]  pos    index on the table
 *  @retval true if solder condition is sattisfied
 */
Bool AdvPeakSearch::isSholderInIncreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos) const {
    return (0.0 < trend[1].at(pos) && 0.0 < trend[1].at(pos+1) && trend[2].at(pos) < 0.0 && 0.0 < trend[2].at(pos+1) ) ;
}

/**
 *  @param[in]  trend  increase-decrease teble
 *  @param[in]  pos    index on the table
 *  @retval true if solder condition is sattisfied
 */
Bool AdvPeakSearch::isSholderInDecreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos) const {
    return ( 0.0 > trend[1].at(pos) && 0.0 > trend[1].at(pos+1) && trend[2].at(pos) > 0.0 && 0.0 > trend[2].at(pos+1) );
}

/**
 *  @param[in]  trend  increase-decrease teble
 *  @param[in]  pos    index on the table
 *  @raram[in]  h      peak height
 *  @retval true if half-value condition is sattisfied at the pos
 */
Bool AdvPeakSearch::isHalfHeightInIncreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (trend[0].at(pos-1) < h/2.0 && h/2.0 < trend[0].at(pos));
}

Bool AdvPeakSearch::isHalfHeightInDecreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (trend[0].at(pos-1) > h/2.0 && h/2.0 > trend[0].at(pos));
}

/**
 *  @param[in]  trend  increase-decrease teble
 *  @param[in]  pos    index on the table
 *  @raram[in]  h      peak height
 *  @retval true if the height at the bottom of a vallery that is higher than the half height of the peak
 */
Bool AdvPeakSearch::isHigherValley(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return ( h/2.0 < trend[0].at(pos) && h/2.0 < trend[0].at(pos+1) &&
             trend[1].at(pos) < 0.0   && 0.0 < trend[1].at(pos+1)   &&
             trend[2].at(pos) > 0.0   && trend[2].at(pos+1) > 0.0       );
}

Bool AdvPeakSearch::isHigherSholderInIncreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (trend[0].at(pos-1) < h/2.0 && h/2.0 < trend[0].at(pos) && isSholderInIncreaseRegion(trend, pos));
}

Bool AdvPeakSearch::isHigherSholderInDecreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (trend[0].at(pos-1) > h/2.0 && h/2.0 > trend[0].at(pos) && isSholderInDecreaseRegion(trend, pos));
}

Bool AdvPeakSearch::isHigherEndInIncreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (pos == 1 && h/2 < trend[0].at(pos-1) && trend[0].at(pos-1) < trend[0].at(pos));
}

Bool AdvPeakSearch::isHigherEndInDecreaseRegion(std::vector< std::vector<Double> > &trend, const UInt4 pos, const Double h) const {
    return  (pos == trend[0].size()-2 &&  trend[0].at(pos) > trend[0].at(pos+1) && trend[0].at(pos+1) > h/2.0);
}


/**
 *  seatch a half value point in the increase region of the peak
 *
 *  @param[in] trend   increase and decread table
 *  @raram[in] srcX    bin
 *  @raram[in] initPos the index of the staritng bin for search
 *  @param[in] h       the heigtht of the peak
 */
Double AdvPeakSearch::searchHalfHeightInIncreaseRegion(std::vector< std::vector<Double> > &trend, std::vector<Double> srcX, const UInt4 initPos, const Double h) {

    std::string memberName="searchHalfHeightInIncreaseRegion(std::vector< std::vector<Double> > &, std::vector<Double>, const UInt4, const Double)";
    Double x=srcX.at(0);
     // search a half height point in increase region
    for (UInt4 j=initPos; j>0; --j) {
        if ( isHalfHeightInIncreaseRegion(trend, j, h) ) {
            DebugMessage(className, memberName, "j=%u h\n", j);
            x=srcX.at(j);
            break;
        } else if (isHigherValley(trend, j, h)                 ) {
            DebugMessage(className, memberName, "j=%u v\n", j);
            x=srcX.at(j);
            break;
        } else if (isHigherSholderInIncreaseRegion(trend, j, h)) {
            DebugMessage(className, memberName, "j=%u s\n", j);
            x=srcX.at(j);
            break;
        } else if (isHigherEndInIncreaseRegion(trend, j, h)    ) {
            DebugMessage(className, memberName, "j=%u e\n", j);
            x=srcX.at(j-1);
            break;
        }
    }

    return x;
}

/**
 *  search a half value point in the decrease region of the peak
 *
 *  @param[in] trend increase and decrease table
 *  @param[in] srcX  bin
 *  @param[in] initPos the index of the starting bin for search
 *  @param[in] h       the heigtht of the peak
 */
Double AdvPeakSearch::searchHalfHeightInDecreaseRegion(std::vector< std::vector<Double> > &trend, std::vector<Double> srcX, const UInt4 initPos, const Double h) {

    std::string memberName="searchHalfHeightInDecreaseRegion(std::vector< std::vector<Double> > &, std::vector<Double>, const UInt4, const Double)";
    Double x=srcX.at(0);
    // search a half height point in decrease region
    for (UInt4 j=initPos; j< trend[0].size()-1U; ++j) {
        if ( isHalfHeightInDecreaseRegion(trend, j, h) ) {
            DebugMessage(className, memberName, "j=%u h\n", j);
            x=srcX.at(j+1);
            break;
        } else if (isHigherValley(trend, j, h)                 ) {
            DebugMessage(className, memberName, "j=%u v\n", j);
            x=srcX.at(j+1);
            break;
        } else if (isHigherSholderInDecreaseRegion(trend, j, h)) {
            DebugMessage(className, memberName, "j=%u s\n", j);
            x=srcX.at(j+1);
            break;
        } else if (isHigherEndInDecreaseRegion(trend, j, h)    ) {
            DebugMessage(className, memberName, "j=%u e\n", j);
            x=srcX.at(j+2);
            break;
        }
    }
    return x;
}

