#include "SASDetectorEfficiencyCorrection.hh"
//////////////////////////////////////////////////////////////////////////
SASDetectorEfficiencyCorrection::
SASDetectorEfficiencyCorrection(){
    _MessageTag = "SASDetectorEfficiencyCorrection::";
    _NumOfMulTh = UtsusemiGetNumOfMulTh();
}
//////////////////////////////////////////////////////////////////////////
SASDetectorEfficiencyCorrection::
SASDetectorEfficiencyCorrection( std::string _detInfo, std::string _detName ){
    _MessageTag = "SASDetectorEfficiencyCorrection::";
    _NumOfMulTh = UtsusemiGetNumOfMulTh();
    Initialize( _detInfo, _detName );
}
//////////////////////////////////////////////////////////////////////////
SASDetectorEfficiencyCorrection::
SASDetectorEfficiencyCorrection( UInt4 _runNo, std::string _detName, UInt4 _modeNo ){
    _MessageTag = "SASDetectorEfficiencyCorrection::";
    _NumOfMulTh = UtsusemiGetNumOfMulTh();
    Initialize( _runNo, _detName, _modeNo );
}
//////////////////////////////////////////////////////////////////////////
SASDetectorEfficiencyCorrection::
SASDetectorEfficiencyCorrection( std::string _datFile ){
    _MessageTag = "SASDetectorEfficiencyCorrection::";
    _NumOfMulTh = UtsusemiGetNumOfMulTh();
    LoadDataFile( _datFile );
}
//////////////////////////////////////////////////////////////////////////
SASDetectorEfficiencyCorrection::
~SASDetectorEfficiencyCorrection(){
}
//////////////////////////////////////////////////////////////////////////
bool SASDetectorEfficiencyCorrection::
Initialize( std::string _detInfo, std::string _detName ){

    // Make file search paths
    // /opt/mlfsoft/python-utsusemi/ana/xml
    std::string utsusemi_base_path="";//UtsusemiEnvGetBaseDir();
    std::string utsusemi_user_path="";//UtsusemiEnvGetInstDir();

    // UTSUSEMI_BASE_DIR
    const char *data_c = getenv("UTSUSEMI_BASE_DIR");
    if (data_c!=NULL){
        std::string tmp(data_c);
        utsusemi_base_path = tmp;
    }
    // UTSUSEMI_USER_PATH
    const char *data2_c = getenv("UTSUSEMI_USR_DIR");
    if (data2_c!=NULL){
        std::string tmp(data2_c);
        utsusemi_user_path = tmp;
    }

    if (utsusemi_user_path==""){
        const char *data3_c = getenv("HOME");
        if (data3_c!=NULL){
            std::string tmp(data3_c);
            utsusemi_user_path=tmp;//UtsusemiEnvGetUserDir();
        }
    }
    if (utsusemi_user_path!=""){
        char utsusemi_usr_xml_path_c[200];
        std::snprintf( utsusemi_usr_xml_path_c, sizeof(utsusemi_usr_xml_path_c), "%s/%s", utsusemi_user_path.c_str(), UTSUSEMIUSERPRIVXMLFILEPATH.c_str());
        std::string tmp( utsusemi_usr_xml_path_c );
        std::vector<std::string> xml_search_paths;
        xml_search_paths.push_back( tmp );
        if (utsusemi_base_path!="") {
            char tmp1_c[200];
            std::snprintf( tmp1_c, sizeof(tmp1_c), "%s/%s", utsusemi_base_path.c_str(), UTSUSEMIUSERPRIVXMLFILEPATH.c_str());
            std::string tmp1( tmp1_c );
            xml_search_paths.push_back( tmp1 );
        }
        this->SetXmlPath( xml_search_paths );
    }else{
        UtsusemiWarning( _MessageTag+"Initialize > can not get Utsusemi environment variables " );
    }
    return MlfDetectorEfficiencyCorrection::Initialize( _detInfo, _detName );

}
//////////////////////////////////////////////////////////////////////////
bool SASDetectorEfficiencyCorrection::
Initialize( UInt4 _runNo, std::string _detName, UInt4 _modeNo ){
    StringTools st;
    std::string msg = _MessageTag+"::Initialize with RunNo : "+st.UInt4ToString(_runNo)+" >> ";

    UtsusemiAnaEnvironReader *UAR = new UtsusemiAnaEnvironReader( _runNo, true );
    if (UAR->_Status){
        std::vector<std::string> pfiles = UAR->PutParamFiles( _runNo, _modeNo,true );
        if (!(pfiles.empty())){
            UtsusemiMessage(msg + "Found file = "+pfiles[1] );
            delete UAR;
            return Initialize( pfiles[1], _detName );
        }else{
            msg += "Filed to find param files from runNo="+st.UInt4ToString( _runNo )+" and mode No="+st.UInt4ToString( _modeNo );
        }
    }else{
        msg += "Failed to find environ_ana.xml ";
    }
    UtsusemiError(msg);
    delete UAR;
    return false;
}
//////////////////////////////////////////////////////////////////////////
bool SASDetectorEfficiencyCorrection::
LoadDataFile( std::string _datFile, std::string additionalPath ){
    std::string full_path = FindParamFilePath( _datFile, additionalPath );
    if (full_path==""){
        return false;
    }
    return MlfDetectorEfficiencyCorrection::LoadApproximateData( full_path );

}
//////////////////////////////////////////////////////////////////////////
void SASDetectorEfficiencyCorrection::
Execute( ElementContainerMatrix* _ecm, UInt4 _bankId ){
    UtsusemiUnitConverter *UUC = new UtsusemiUnitConverter();
    UInt4 ThNum = 0;

#ifdef MULTH
    omp_set_num_threads( _NumOfMulTh );
#endif

    for (UInt4 i=0; i<(_ecm->PutSize()); i++){
        HeaderBase* hh_ECA = _ecm->PutPointer(i)->PutHeaderPointer();
        if (hh_ECA->PutInt4("BANKID")!=_bankId) continue;
        std::vector<Double> detPosi;
        detPosi.clear();
        if (hh_ECA->CheckKey("DETPOSINFO")==1) detPosi = hh_ECA->PutDoubleVector("DETPOSINFO");
        for (UInt4 j=0; j<(_ecm->PutPointer(i)->PutSize()); j++){
            ElementContainer* ec = _ecm->PutPointer(i)->PutPointer(j);
            HeaderBase* hh_ec = ec->PutHeaderPointer();

            Double theta = 0.0;
            std::vector<Double> pv = hh_ec->PutDoubleVector("PixelPosition");
            Double ll = sqrt( pv[0]*pv[0] + pv[1]*pv[1] + pv[2]*pv[2] );
            // No detector posiion info => assume that the detector direction is vertical (along Y-axis)
            if (detPosi.empty()){
                Double ll0= sqrt( pv[0]*pv[0] + pv[2]*pv[2] );
                theta = fabs( acos( ll0/ll )/M_PI*180.0 ); //degree
            }else{
                Double LL = sqrt( detPosi[3]*detPosi[3] + detPosi[4]*detPosi[4] + detPosi[5]*detPosi[5] );
                Double PL = pv[0]*detPosi[3]+pv[1]*detPosi[4]+pv[2]*detPosi[5];
                theta = fabs( acos(PL/ll/LL)/M_PI*180.0 ); //degree
                if (theta>90.0) theta=theta-90.0;
                else theta = 90.0 -theta;
                //if (outFlag) std::cout << "step ij = "<< i << "," << j << " : PL/ll/LL="<< PL/ll/LL << "-> theta=" << theta << std::endl;
            }

            std::vector<Double>* lam = ec->PutP( ec->PutXKey() );
            std::vector<Double>* yy = ec->PutP( ec->PutYKey() );

#pragma omp parallel for
#if (_OPENMP >= 200805)  // OpenMP 3.0 and later
            for (UInt4 k=0; k<(yy->size()); k++){
#else
            for (Int4 k=0; k<(yy->size()); k++){
#endif
#ifdef MULTH
                ThNum = omp_get_thread_num();
#endif

                if (EffiApproxBox==NULL){
                    Double sq_Ef = (UUC->VtoLambda())/(UUC->EtoV())/(lam->at(k));
                    Double Ef = sq_Ef*sq_Ef;
                    yy->at(k) = (yy->at(k))/(_MNG->at(ThNum)->GetEfficiency( Ef, theta ));
                }else{
                    Double eff=GetEfficiencyApproximateVal( lam->at(k), theta );
                    if (eff>0.0)
                        yy->at(k) = (yy->at(k))/(eff);
                    else{
                        Double sq_Ef = (UUC->VtoLambda())/(UUC->EtoV())/(lam->at(k));
                        Double Ef = sq_Ef*sq_Ef;
                        yy->at(k) = (yy->at(k))/(_MNG->at(ThNum)->GetEfficiency( Ef, theta ));
                    }
                }
            }
        }
    }
    delete UUC;
}

//////////////////////////////////////////////////////////////////////////
Double SASDetectorEfficiencyCorrection::
GetEfficiencyApproximateVal(double lambda, double theta){
    if (EffiApproxBox==NULL){
        std::cout << "GetEfficiencyApproximateVal : EffiApproxBox is empty" << std::endl;
        return 0.0;
    }

    Double posi_lambda = (lambda - _min_Lambda)/_delta_Lambda;
    Double posi_theta  = (theta - _min_Th)/_delta_Th;
    UInt4 ind_lambda = (UInt4)( floor( posi_lambda ) );
    UInt4 ind_theta  = (UInt4)( floor( posi_theta ) );
    Double A = posi_lambda - floor( posi_lambda );
    Double B = posi_theta - floor( posi_theta );

    if ((A < 0) || (A > 1.0)){
        std::cout << "GetEfficiencyApproximateVal : Out of range on given lambda = " << lambda << std::endl;
        return -1.0;
    }else if ((B < 0) || (B > 1.0)){
        std::cout << "GetEfficiencyApproximateVal : Out of range on given Theta = " << theta << std::endl;
        return -2.0;
    }

    Double ret = (1.0 - A) * (1.0 - B) * EffiApproxBox[ind_lambda][ind_theta]
        + (1.0 - A) * B * EffiApproxBox[ind_lambda][ind_theta + 1]
        + A * (1.0 - B) * EffiApproxBox[ind_lambda + 1][ind_theta]
        + A * B * EffiApproxBox[ind_lambda + 1][ind_theta + 1];

    return ret;
}
