#include "TofToEnergyTransfer.hh"
//////////////////////////////////////////////////////////
TofToEnergyTransfer::
TofToEnergyTransfer()
{
}

//////////////////////////////////////////////////////////
TofToEnergyTransfer::
~TofToEnergyTransfer()
{
}

//////////////////////////////////////////////////////////
void TofToEnergyTransfer::
Tof2EnergyTransferBase( double L1, double Tof_offset, std::vector<Double> new_bin )
{
    ElementContainerMatrix *ecm = Put();

    /* removed for Multi-threading (2010.07.22)
    std::vector<double> tof,posivect;
    std::string xkey,ykey,ekey;
    Double L2p;
    */
    UInt4 num_of_eca, num_of_ec;

    HeaderBase *h;
    /* check the existence of key Ei in header  */
    h = ecm->PutHeaderPointer();
    if (h->CheckKey(UTSUSEMI_KEY_HEAD_EI)==0){
        UtsusemiError("TofToEnergyTransfer >> Ei is not set in ElementContainerMatrix.");
        return;
    }

    Double Ei = h->PutDouble(UTSUSEMI_KEY_HEAD_EI);
    /* modified to unify to treat constant values
    Double vi = 437.4*sqrt(Ei);
    Double mn = 1.6749e-27;
    */
    //UnitConvChopper *UCC = new UnitConvChopper();
    UtsusemiUnitConverter *UCC = new UtsusemiUnitConverter();
    Double vi = UCC->EtoV( Ei );  // [meter/micro-sec]
    Double mn = UCC->Mn();        // [kg]
    delete UCC;

    //Double J2meV = 6.24151e21;
    Double J2meV = UCC->J2meV();
    // removed for Multi-threading (2010.07.22)
    //std::vector<Double> et;

    /* need or not for TOF Rebinning    */
    UInt4 rebin_flag = 0;
    if (new_bin.size()!=0){
        rebin_flag = 1;
    }

    num_of_eca = ecm->PutTableSize();
    //std::cout << "TofToEnergyTransfer >> PSD = ";
    // added for Multi-threading (2010.07.22)
    #ifdef MULTH
    omp_set_num_threads( MULTH );
    #endif
    //#pragma omp parallel for [removed by inamura 100924]
    #ifdef _OPENMP
    #pragma omp parallel for

    #if (_OPENMP >= 200805)  // OpenMP 3.0 and later
    for (UInt4 psd=0;psd<num_of_eca;psd++){
    #else
    for (Int4 psd=0;psd<(Int4)num_of_eca;psd++){
    #endif

    #else
    for (UInt4 psd=0;psd<num_of_eca;psd++){
    #endif  // #ifdef _OPENMP
        ElementContainerArray *eca = ecm->PutPointer(psd);
        //std::cout << psd << ",";
        std::string type_s = eca->PutHeaderPointer()->PutString(UTSUSEMI_KEY_HEAD_DETTYPE);
        std::string::size_type index = type_s.find( UTSUSEMI_KEY_HEAD_DETTYPE_MONITOR );
        if (index ==std::string::npos){
            num_of_ec = eca->PutTableSize();

            for (UInt4 pixel=0;pixel<num_of_ec;pixel++){
                ElementContainer *ec = eca->PutPointer(pixel);

                std::string xkey = ec->PutXKey();
                std::string ykey = ec->PutYKey();
                std::string ekey = ec->PutEKey();
                std::vector<double> tof = ec->PutX();
                //std::vector<double> *et = new std::vector<double>;
                //et->clear(); //[added by inamura 100924]
                std::vector<double> et;
                et.clear();

                std::vector<double> posivect = ec->PutHeaderPointer()->PutDoubleVector(UTSUSEMI_KEY_HEAD_PIXELPOSITION);
                Double L2p = sqrt( posivect[0]*posivect[0]+posivect[1]*posivect[1]+posivect[2]*posivect[2] )/1000.0;

                for (UInt4 i=0;i<(tof.size());i++){
                    //(2010.01.15) include Tof_Offset.
                    //et.push_back(Ei - 0.5*mn*(L2p*L2p)/pow((tof[i]*(1.0e-6) - L1/vi),2.0) * J2meV);
                    //et.push_back(Ei - 0.5*mn*(L2p*L2p)/pow(((tof[i]-Tof_offset)*(1.0e-6) - L1/1000.0/vi),2.0) * J2meV);
                    et.push_back(Ei - 0.5*mn*(L2p*L2p)/pow(((tof[i]-Tof_offset) - L1/1000.0/vi)*(1.0e-6),2.0) * J2meV);
                }

                ec->Add(UTSUSEMI_KEY_ENERGY,et,UTSUSEMI_KEY_ENERGY_UNIT);
                ec->SetKeys(UTSUSEMI_KEY_ENERGY,ykey,ekey);
                ec->Remove(xkey);
                //delete et;
            }
        }
    }
    //std::cout << std::endl;

    // [added by inamura 101005 to check stability]->
    if (rebin_flag==1){
        for (UInt4 psd=0;psd<num_of_eca;psd++){
            ElementContainerArray *eca = ecm->PutPointer(psd);
            //std::cout << psd << ",";
            std::string type_s = eca->PutHeaderPointer()->PutString(UTSUSEMI_KEY_HEAD_DETTYPE);
            std::string::size_type index = type_s.find( UTSUSEMI_KEY_HEAD_DETTYPE_MONITOR );
            if (index ==std::string::npos){
                num_of_ec = eca->PutTableSize();
                #ifdef _OPENMP
                #pragma omp parallel for

                #if (_OPENMP >= 200805)  // OpenMP 3.0 and later
                for (UInt4 pixel=0;pixel<num_of_ec;pixel++){
                #else
                for (Int4 pixel=0;pixel<(Int4)num_of_ec;pixel++){
                #endif

                #else
                for (UInt4 pixel=0;pixel<num_of_ec;pixel++){
                #endif  // #ifdef _OPENMP
                    //Binning( eca->PutPointer(pixel),new_bin );
                    Binning( psd, pixel, new_bin );
                }
            }
        }
    }
    // <-[inamura 101005]
    std::vector<std::string> st_vect = h->PutStringVector(UTSUSEMI_KEY_HEAD_DATAPROCESSED);
    st_vect.push_back("TOF TO ENERGY TRANSFER CONVERSION");
    h->OverWrite(UTSUSEMI_KEY_HEAD_DATAPROCESSED,st_vect);

}
//////
void TofToEnergyTransfer::
Binning( ElementContainer *ec,std::vector<Double> new_bin ){
    std::string ykey = ec->PutYKey();
    std::string ekey = ec->PutEKey();
    ElementContainer new_ec = ec->Binning(new_bin);
    ec->Replace(UTSUSEMI_KEY_ENERGY,new_ec.PutX());
    ec->Replace(ykey,new_ec.PutY());
    ec->Replace(ekey,new_ec.PutE());
    ec->SetKeys(UTSUSEMI_KEY_ENERGY,ykey,ekey);
}
/////
void TofToEnergyTransfer::
Binning( UInt4 psd, UInt4 pixel ,std::vector<Double> new_bin ){
    ElementContainerMatrix* ecm = Put();
    ElementContainer *ec = ecm->PutPointer(psd)->PutPointer(pixel);
    std::string ykey = ec->PutYKey();
    std::string ekey = ec->PutEKey();
    ElementContainer new_ec = ec->Binning(new_bin);
    ec->Replace(UTSUSEMI_KEY_ENERGY,new_ec.PutX());
    ec->Replace(ykey,new_ec.PutY());
    ec->Replace(ekey,new_ec.PutE());
    ec->SetKeys(UTSUSEMI_KEY_ENERGY,ykey,ekey);
}
//////////////////////////////////////////////////////////
void TofToEnergyTransfer::
Tof2EnergyTransfer( double Tof_offset)
{
    ElementContainerMatrix *ecm = Put();
    double L1;
    if ( (ecm->PutHeaderPointer()->CheckKey(UTSUSEMI_KEY_HEAD_L1))==0 ){
        UtsusemiError("TofToEnergyTransfer >> L1 is not set in ElementContainerMatrix.");
        return;
    }
    L1 = ecm->PutHeaderPointer()->PutDouble(UTSUSEMI_KEY_HEAD_L1);

    std::vector<Double> no_bin;
    no_bin.clear();

    Tof2EnergyTransferBase( L1, Tof_offset, no_bin );
}

//////////////////////////////////////////////////////////
void TofToEnergyTransfer::
Tof2EnergyTransfer( double Tof_offset, std::vector<Double> new_bin )
{
    ElementContainerMatrix *ecm = Put();
    double L1;
    if ( (ecm->PutHeaderPointer()->CheckKey(UTSUSEMI_KEY_HEAD_L1))==0 ){
        UtsusemiError("TofToEnergyTransfer >> L1 is not set in ElementContainerMatrix.");
        return;
    }
    L1 = ecm->PutHeaderPointer()->PutDouble(UTSUSEMI_KEY_HEAD_L1);

    Tof2EnergyTransferBase( L1, Tof_offset, new_bin );
}

//////////////////////////////////////////////////////////
void TofToEnergyTransfer::
Tof2EnergyTransfer( double L1, double Tof_offset)
{
    std::vector<Double> no_bin;
    no_bin.clear();
    Tof2EnergyTransferBase( L1, Tof_offset, no_bin );
}

//////////////////////////////////////////////////////////
void TofToEnergyTransfer::
Tof2EnergyTransfer( double L1, double Tof_offset, std::vector<Double> new_bin )
{
    Tof2EnergyTransferBase( L1, Tof_offset, new_bin );
}
