#include "LambdaDependCorrection.hh"

////////////////////////////////////////
LambdaDependCorrection::
LambdaDependCorrection(ElementContainerMatrix* ecm):
    _MessageTag ("LambdaDependCorrection::"){
    _depType = "";
    _st = new StringTools();
    if (ecm !=NULL)
        SetTarget(ecm);
    _bankNames.resize(20,"");
    _bankNames[0]="SD";
    _bankNames[1]="SU";
    _bankNames[2]="SMBL16";
    _bankNames[3]="SMBL14";
    _bankNames[4]="SC";
    _bankNames[5]="MD";
    _bankNames[6]="MU";
    _bankNames[7]="ML";
    _bankNames[8]="MR";
    _bankNames[10]="HU";
    _bankNames[11]="HL";
    _bankNames[12]="HD";
    _bankNames[13]="BW";
    _bankNames[15]="SCC";
    _debugmode = UtsusemiEnvGetDebugMode();
}
////////////////////////////////////////
LambdaDependCorrection::
~LambdaDependCorrection(){
    delete _st;
}

const std::string LambdaDependCorrection::DEP_GIVENCURVE="GIVENCURVE";
const std::string LambdaDependCorrection::DEP_SINGLELINE="SINGLELINE";

////////////////////////////////////////
void LambdaDependCorrection::_addToParams( std::string bank, std::string type, std::vector<double> params ){
    if (bank=="SM"){
        if (_depParams.Check(_bankNames[2])==1) _depParams.Remove(_bankNames[2]);
        if (_depParams.Check(_bankNames[3])==1) _depParams.Remove(_bankNames[3]);
    }else{
        if (_depParams.Check(bank)==1){  // If already bank added, remove previous one
            _depParams.Remove( bank );
        }
    }
    std::pair< std::string, std::vector<double> > tmp;
    tmp.first = type;
    tmp.second.resize(params.size(),0.0);
    copy( params.begin(), params.end(), tmp.second.begin() );
    if (bank=="SM"){
        _depParams.Add(_bankNames[2],tmp);
        _depParams.Add(_bankNames[3],tmp);
    }else{
        _depParams.Add(bank,tmp);
    }
}
////////////////////////////////////////
void LambdaDependCorrection::ClearParams( std::string bank ){
    if (!CheckBankName(bank)){
        UtsusemiError(_MessageTag+"ClearParams >>> invalid bank name");
        return;
    }
    transform( bank.begin(), bank.end(), bank.begin(), ::toupper );
    if (bank=="ALL"){
        for (UInt4 i=0;i<(_bankNames.size());i++){
            if (_depParams.Check( _bankNames[i] )==1) _depParams.Remove( _bankNames[i] );
        }
    }else if (bank=="SM"){
        if (_depParams.Check(_bankNames[2])==1) _depParams.Remove(_bankNames[2]);
        if (_depParams.Check(_bankNames[3])==1) _depParams.Remove(_bankNames[3]);
    }else{
        if (_depParams.Check( bank )==1) _depParams.Remove( bank );
        else{
            UtsusemiWarning(_MessageTag+"ClearParams >>> Not found such a bank name "+bank);
        }
    }
}
////////////////////////////////////////
bool LambdaDependCorrection::SetCurveTextDep( std::string bank, std::string filepath ){
    if (!CheckBankName(bank)){
        UtsusemiError(_MessageTag+"SetCurveTextDep >>> invalid bank name");
        return false;
    }
    transform( bank.begin(), bank.end(), bank.begin(), ::toupper );
    std::ifstream fin(filepath.c_str());
    if (!fin){
        UtsusemiError(_MessageTag+"SetCurveTextDep >>> Failed to open such a filepath = "+filepath);
        return false;
    }
    std::vector<Double> data;
    std::string a_line;
    getline(fin, a_line);
    while( !fin.eof() ){
        if (a_line.find("#")!=0){
            std::string::size_type ind1 = a_line.find(",");
            std::string::size_type ind2 = a_line.size()-1;
            Double q = _st->StringToDouble( a_line.substr(0,ind1) );
            Double v = _st->StringToDouble( a_line.substr(ind1+1,ind2-ind1) );
            data.push_back(q);
            data.push_back(v);
        }
        getline(fin, a_line);
    }
    bool isAll=false;
    if (bank=="ALL") isAll=true;
    else{
        if (!(CheckBankName(bank))){
            UtsusemiError(_MessageTag+"SetSingleLineDep >>> invalid bank name parameter");
            return false;
        }
    }
    if (isAll){
        for (UInt4 i=0;i<(_bankNames.size());i++){
            _addToParams( _bankNames[i], DEP_GIVENCURVE, data );
        }
    }else{
        _addToParams( bank, DEP_GIVENCURVE, data );
    }

    return true;
}
////////////////////////////////////////
bool LambdaDependCorrection::SetSingleLineDep( std::string bank, std::string params ){
    if (!CheckBankName(bank)){
        UtsusemiError(_MessageTag+"SetSingleLineDep >>> invalid bank name");
        return false;
    }
    transform( bank.begin(), bank.end(), bank.begin(), ::toupper );
    bool isAll=false;
    std::string delim(",");
    std::vector<std::string> p_v = _st->SplitString( params, delim );
    if (p_v.size()!=2){
        UtsusemiError(_MessageTag+"SetSingleLineDep >>> invalid parameter [<A>,<B>] for A+B*(Lambda)");
        return false;
    }
    std::vector<double> params_v(2,0.0);
    params_v[0] = _st->StringToDouble( p_v[0] );
    params_v[1] = _st->StringToDouble( p_v[1] );
    if (bank=="ALL") isAll=true;
    else{
        if (!(CheckBankName(bank))){
            UtsusemiError(_MessageTag+"SetSingleLineDep >>> invalid bank name parameter");
            return false;
        }
    }
    if (isAll){
        for (UInt4 i=0;i<(_bankNames.size());i++){
            _addToParams( _bankNames[i], DEP_SINGLELINE, params_v );
        }
    }else{
        _addToParams( bank, DEP_SINGLELINE, params_v );
    }

    return true;
}
////////////////////////////////////////
double LambdaDependCorrection::_calFact(double x0, double y0, double x1, double y1, double x ){
    return ((x0*y1-x1*y0) + (y0-y1)*x)/(x0-x1);
}
////////////////////////////////////////
bool LambdaDependCorrection::CheckBankName(std::string bank){
    transform( bank.begin(), bank.end(), bank.begin(), ::toupper );
    if ( (bank=="ALL") || (bank=="SM") ) return true;
    for (UInt4 i=0;i<_bankNames.size();i++)
        if (bank==_bankNames[i]) return true;
    return false;
}
////////////////////////////////////////
bool LambdaDependCorrection::Execute(ElementContainerMatrix* _ecm){
    ElementContainerMatrix* ecm;
    if (_ecm!=NULL) ecm = _ecm;
    else{
        ecm = Put();
    }

    std::vector<Int4> bankIdList=ecm->PutHeaderPointer()->PutInt4Vector("BANKIDLIST");

    for (UInt4 i=0; i<(ecm->PutSize()); i++){
        ElementContainerArray* eca = ecm->PutPointer(i);
        UInt4 bankId = (UInt4)(eca->PutHeaderPointer()->PutInt4("BANKID"));
        std::pair< std::string, std::vector<Double> > params = _depParams[ _bankNames[bankId] ];
        if (params.first==DEP_GIVENCURVE){
            std::vector<Double> lamb;
            std::vector<Double> val;
            for (UInt4 j=0; j<(params.second.size()/2); j++){
                lamb.push_back( params.second[ 2*j ] );
                val.push_back( params.second[ 2*j +1] );
            }
            ElementContainer* ec0 = ecm->PutPointer(i)->PutPointer(0);
            std::vector<double>* lambv=ec0->PutP("Lamb");
            std::vector<Double> fact;
            for (UInt4 k=0; k<(lambv->size()); k++){
                Double a_lam = lambv->at(k);
                if (a_lam<lamb[0]){
                    fact.push_back( _calFact( lamb[0],val[0],lamb[1],val[1],a_lam ) );
                }else if (a_lam>lamb.back()){
                    UInt4 size = lamb.size();
                    fact.push_back( _calFact( lamb[size-2],val[size-2],lamb[size-1],val[size-1],a_lam ) );
                }else{
                    for (UInt4 l=0; l<(lamb.size()-1); l++)
                        if ((a_lam>=lamb[l])&&(a_lam<lamb[l+1]))
                            fact.push_back( _calFact( lamb[l],val[l],lamb[l+1],val[l+1],a_lam ) );
                }
            }
            if (_debugmode){
                if (i==0){
                    for (UInt4 j=0;j<fact.size();j++) std::cout << fact[j] << ",";
                    std::cout << std::endl;
                }
            }
            for (UInt4 j=0; j<(eca->PutSize()); j++){
                ElementContainer* ec = ecm->PutPointer(i)->PutPointer(j);
                std::string ykey=ec->PutYKey();
                std::string ekey=ec->PutEKey();
                std::vector<Double>* yvect=ec->PutP(ykey);
                std::vector<Double>* evect=ec->PutP(ekey);
                for (UInt4 k=0;k<(lambv->size());k++){
                    if (fact[k]!=0.0){
                        (*yvect)[k] = ((*yvect)[k])/fact[k];
                        (*evect)[k] = ((*evect)[k])/fact[k];
                    }
                }
            }
        }
        if (params.first==DEP_SINGLELINE){
            for (UInt4 j=0; j<(eca->PutSize()); j++){
                ElementContainer* ec = ecm->PutPointer(i)->PutPointer(j);
                std::string ykey=ec->PutYKey();
                std::string ekey=ec->PutEKey();
                std::vector<Double>* lambv=ec->PutP("Lamb");
                std::vector<Double>* yvect=ec->PutP(ykey);
                std::vector<Double>* evect=ec->PutP(ekey);
                for (UInt4 k=0;k<(lambv->size());k++){
                    double fact = ( params.second[0]+params.second[1]*(*lambv)[k] );
                    (*yvect)[k] = ((*yvect)[k])/fact;
                    (*evect)[k] = ((*evect)[k])/fact;
                }
            }
        }
    }

    return true;
}

