#include "AdvMultiDataPeakFit.hh"
#include "AdvFuncComb.hh"

/**
 *  class name
 */
const string AdvMultiDataPeakFit::className = string("AdvMultiDataPeakFit");

AdvMultiDataPeakFit::AdvMultiDataPeakFit() {
};


AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src) : AdvMultiDataOperationBase(src) {
}

#ifndef SWIGPYTHON
AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, AdvMultiDataMethod *method ) : AdvMultiDataOperationBase(src, method) {
}
#endif

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const AdvMethodType &methodType) : AdvMultiDataOperationBase(src, methodType) {
}

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const string &methodName    ) : AdvMultiDataOperationBase(src, methodName) {
}

#ifndef SWIGPYTHON
AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, AdvMultiDataMethod *method,     const Double xLower, const Double xUpper) : AdvMultiDataOperationBase(src, method,     xLower, xUpper) {
}
#endif

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const AdvMethodType &methodType, const Double xLower, const Double xUpper) : AdvMultiDataOperationBase(src, methodType, xLower, xUpper) {
}

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const string     &methodName, const Double xLower, const Double xUpper) : AdvMultiDataOperationBase(src, methodName, xLower, xUpper) {
}

#ifndef SWIGPYTHON
AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, AdvMultiDataMethod *method,     const UInt4 lower, const UInt4 upper) : AdvMultiDataOperationBase(src, method,     lower, upper) {
}
#endif

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const AdvMethodType &methodType, const UInt4 lower, const UInt4 upper) : AdvMultiDataOperationBase(src, methodType, lower, upper) {
}

AdvMultiDataPeakFit::AdvMultiDataPeakFit(ElementContainerArray *src, const string     &methodName, const UInt4 lower, const UInt4 upper) : AdvMultiDataOperationBase(src, methodName, lower, upper) {
}

AdvMultiDataPeakFit::~AdvMultiDataPeakFit() {
};

void AdvMultiDataPeakFit::execute() {

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

    this->method->toInnerForm(*(this->source), this->domains, this->param);
    this->method->fit();
    if ( this->method->isMultiThreaded() ) {
        while (this->method->isFitting()) {
            sleep(1);
        }
    }
    this->method->eval();

    //vector< vector<Double> > trend = this->method->getTrend();

}

Double AdvMultiDataPeakFit::chiSq() {
    return 0.0;
}

AdvParamSet *AdvMultiDataPeakFit::getLatestConvergenceStat() const {
    return this->method->isMultiThreaded() ? this->method->getLatestConvergenceStat() : NULL;
}

ElementContainer AdvMultiDataPeakFit::getResult(UInt4 i) {
    ElementContainer *ec=new ElementContainer(this->source->PutHeader());
    this->method->toElementContainer(*(this->source), i, *ec);
    return *ec;
}

ElementContainerArray AdvMultiDataPeakFit::getResult() {

    ElementContainerArray *eca=new ElementContainerArray(this->source->PutHeader());
    this->method->toElementContainerArray(*(this->source), *eca);
    return *eca;
}

ElementContainerArray AdvMultiDataPeakFit::getResultComponents(UInt4 id) {
    string memberName = string("getResultComponents()");

    DebugMessage(className, memberName, "enter\n");

    ElementContainer *srcElem = this->source->PutPointer(id);
    string xKey  = srcElem->PutXKey();
    string yKey  = srcElem->PutYKey();
    string eKey  = srcElem->PutEKey();
    string xUnit = srcElem->PutUnit(xKey);
    string yUnit = srcElem->PutUnit(yKey);
    string eUnit = srcElem->PutUnit(yKey);
    HeaderBase header = srcElem->PutHeader();
    string FUNC_KEY     =string("functions");
    string PARAM_KEY    =string("parameter values");
    string PARAM_ERR_KEY=string("param errors");

    AdvParamSet fittedParamSet = this->method->getFittedParam();
    //fittedParamSet.dump();

    ElementContainerArray *ecArray = new ElementContainerArray();
    if ( ! fittedParamSet.contain(FUNC_KEY) ) {
        errorMessage(className, memberName, "can't get functions\n");
    } else if ( ! fittedParamSet.contain(PARAM_KEY) ) {
        errorMessage(className, memberName, "can't get fitted parameters\n");
    } else if ( ! fittedParamSet.contain(PARAM_ERR_KEY) ) {
        errorMessage(className, memberName, "can't get parameter errors\n");
    } else {
        AdvFuncComb *func = new AdvFuncComb(fittedParamSet.getFuncMatrix(FUNC_KEY).at(id), fittedParamSet.getVector(PARAM_KEY), fittedParamSet.getVector(PARAM_ERR_KEY));
        //func->dump();

        vector<Double> *resultBin=this->domains.at(id).getBin();
        vector<Double> *resultX  =this->domains.at(id).createXC();

        for (UInt4 i=0; i<func->getNumberOfComponents(); ++i) {
            DebugMessage(className, memberName, "%u-th. component\n", i);
            ElementContainer *ec = new ElementContainer(header);

            ec->Add(xKey, *resultBin,                            xUnit);
            ec->Add(yKey, func->evalComponent(     *resultX, i), yUnit);
            DebugMessage(className, memberName, "evaluate values\n", i);
            ec->Add(eKey, func->evalErrorComponent(*resultX, i), eUnit);
            DebugMessage(className, memberName, "evaluate errors\n", i);
            ec->SetKeys(xKey, yKey, eKey);

            ec->AddToHeader(string(FUNC_KEY),      func->getName(i));
            ec->AddToHeader(string(PARAM_KEY),     func->getSubsequenceOfParam(i));
            ec->AddToHeader(string(PARAM_ERR_KEY), func->getSubsequenceOfParamError(i));
            ecArray->Add(*ec);
        }
        delete func;
    }
    DebugMessage(className, memberName, "exit\n");
    return *ecArray;
}

ElementContainerMatrix AdvMultiDataPeakFit::getResultComponents() {

    ElementContainerMatrix *retval = new ElementContainerMatrix();
    for (UInt4 i=0; i<this->source->PutSize(); ++i) {
        retval->Add(this->getResultComponents(i));
    }
    return *retval;
}
