#include "CalcScanTrajectory.hh"
//////////////////////////////////////////////////////////
CalcScanTrajectory::
CalcScanTrajectory(){
    _initialize();
}
//////////////////////////////////////////////////////////
CalcScanTrajectory::
CalcScanTrajectory(ElementContainerMatrix *ecm){
    SetTarget(ecm);
    _initialize();
}
//////////////////////////////////////////////////////////
CalcScanTrajectory::
~CalcScanTrajectory(){
    delete _st;
}
//////////////////////////////////////////////////////////
void CalcScanTrajectory::
_initialize(){
    _Q_plotrange_min = 0.0;
    _Q_plotrange_max = 4.0;
    _hw_min = -100.0;
    _hw_bin = 0.1;
    _lamb_list.clear();
    _st = new StringTools();
}
//////////////////////////////////////////////////////////
void CalcScanTrajectory::
SetQPlotRange( double Q_plotrange_min, double Q_plotrange_max ){
    if (Q_plotrange_min>Q_plotrange_max) {
        double tmp = Q_plotrange_min;
        Q_plotrange_max = Q_plotrange_min;
        Q_plotrange_min = tmp;
    }
    if (Q_plotrange_min<0.0) Q_plotrange_min = 0.0;
    _Q_plotrange_min = Q_plotrange_min;
    _Q_plotrange_max = Q_plotrange_max;

}
//////////////////////////////////////////////////////////
void CalcScanTrajectory::
SetHWmin( double hw_min ){
    _hw_min = hw_min;
}
//////////////////////////////////////////////////////////
bool CalcScanTrajectory::
SetHWbin( double hw_bin ){
    if (hw_bin<0.0) return false;
    _hw_bin = hw_bin;
    return true;
}
//////////////////////////////////////////////////////////
bool CalcScanTrajectory::
SetLambdaList( std::vector<Double> lamb_list ){
    _lamb_list.clear();
    _lamb_list.resize( lamb_list.size() );
    copy( lamb_list.begin(), lamb_list.end(), _lamb_list.begin() );
    return true;
}
//////////////////////////////////////////////////////////
bool CalcScanTrajectory::
SetLambdaList( PyObject *lamb_pylist ){
    UInt4 lsize = (UInt4) PyList_Size( lamb_pylist );
    if (lsize>0){
        _lamb_list.clear();
        for (UInt4 i=0; i<lsize; i++ ){
            if( PyFloat_CheckExact( PyList_GetItem( lamb_pylist, i ) ) ){
                _lamb_list.push_back( PyFloat_AsDouble( PyList_GetItem( lamb_pylist, i ) ) );
            }
            else if( PyLong_CheckExact( PyList_GetItem( lamb_pylist, i ) ) ){
                _lamb_list.push_back( ( Double )( PyLong_AsLong( PyList_GetItem( lamb_pylist, i ) ) ) );
            }
#ifdef IS_PY3
#else
            else if( PyInt_CheckExact( PyList_GetItem( lamb_pylist, i ) ) ){
                _lamb_list.push_back( ( Double )( PyInt_AsLong( PyList_GetItem( lamb_pylist, i ) ) ) );
            }
#endif
            else{
                return false;
            }
        }
        return true;
    }else{
        return false;
    }
}
//////////////////////////////////////////////////////////
bool CalcScanTrajectory::
Execute( ElementContainerArray* eca, double Qela_min, double Qela_max, double Qela_bin ){
    if (_lamb_list.empty()) return false;

    UtsusemiUnitConverter UC;
    Double lamb_min=_lamb_list[0];
    for (UInt4 i=0; i<_lamb_list.size(); i++)
        if (lamb_min>_lamb_list[i]) lamb_min=_lamb_list[i];

    Double Ei_max=UC.VtoE( UC.VtoLambda()/lamb_min );
    std::vector<Double> hw_bin_param=CalcRangeAsBinCenterZero( _hw_min, Ei_max, _hw_bin );

    Double hw = hw_bin_param[0];
    std::vector<Double> hw_list;
    while(hw<=hw_bin_param[1]){
        hw_list.push_back(hw);
        hw += _hw_bin;
    }
    Double hw_max = hw_list.back();
    Double hw_min = hw_list.front();
    std::vector<Double> q_list;
    Double q = _Q_plotrange_min - (Qela_bin/2.0);
    while(q<=_Q_plotrange_max){
        q_list.push_back( q );
        q += Qela_bin;
    }

    std::vector< std::vector<Double> > I_Mat;
    std::vector<Double> empty_int( hw_list.size(), 0.0 );
    for (UInt4 i=0; i<q_list.size(); i++)
        I_Mat.push_back( empty_int );

    std::vector<Double> th_list;
    std::vector<Double> L2_list;
    ElementContainerMatrix * _ecm = Put();
    for (UInt4 i=0; i<_ecm->PutSize(); i++){
        for (UInt4 j=0; j<_ecm->PutPointer(i)->PutSize(); j++){
            Double pol = _ecm->PutPointer(i)->PutPointer(j)->PutHeaderPointer()->PutDouble("PolarAngle");
            std::vector<Double> p = _ecm->PutPointer(i)->PutPointer(j)->PutHeaderPointer()->PutDoubleVector("PixelPosition");
            Double L2 = sqrt( p[0]*p[0] + p[1]*p[1] + p[2]*p[2] );
            Double theta = pol*M_PI/180.0;
            th_list.push_back(theta);
            L2_list.push_back(L2);
        }
    }

#ifdef MULTH
    omp_set_num_threads( UtsusemiGetNumOfMulTh() );
#endif
    for (UInt4 i=0; i<_lamb_list.size(); i++){
        Double Ei = UC.VtoE( UC.VtoLambda()/_lamb_list[i] );
        std::string msg = "Ei = "+_st->DoubleToString(Ei)+" ( Lambda = "+_st->DoubleToString(_lamb_list[i])+" )";
        UtsusemiMessage(msg);

        Double ki2 =  UC.EtoK2( Ei );
        Double ki = sqrt( ki2 );
#pragma omp parallel for
#if (_OPENMP >= 200805)  // OpenMP 3.0 and later
        for( UInt4 j=0; j<L2_list.size(); j++ ){
#else
        for( Int4 j=0; j<(Int4)L2_list.size(); j++ ){
#endif
            Double theta = th_list[j];
            Double L2 = L2_list[j];
            Double Q_ela = sqrt( 2.0*(ki2)*(1.0-cos(theta)) ); // Check Q range at elastic
            if ((Q_ela>=Qela_min)&&(Q_ela<=Qela_max)){
                Double hw = hw_min;
                UInt4 i_hw = 0;
                while(hw<=hw_max){
                    Double kf2 = UC.EtoK2( Ei - hw );
                    Double kf = sqrt( kf2 );
                    Double Q = sqrt( ki2 + kf2 -2.0*ki*kf*cos(theta) );
                    if ((Q>=_Q_plotrange_min)&&(Q<=_Q_plotrange_max)){
                        UInt4 i_Q = (UInt4)((Q-_Q_plotrange_min)/Qela_bin);
                        if (i_Q>=I_Mat.size()){
                            std::string msg2 = "Out of Range : i_Q = "+_st->UInt4ToString(i_Q)+", I_Mat size= "+_st->UInt4ToString(I_Mat.size());
                            UtsusemiWarning(msg2);
                            break;
                        }else if (i_hw>=(I_Mat[0].size())){
                            std::string msg2 = "Out of Range : i_hw = "+_st->UInt4ToString(i_hw)+", I_Mat size= "+_st->UInt4ToString(I_Mat[0].size());
                            UtsusemiWarning(msg2);
                            break;
                        }else{
                            I_Mat[i_Q][i_hw] += 1.0;
                        }
                    }
                    hw += _hw_bin;
                    i_hw += 1;
                }
            }
        }
    }

    for (UInt4 i=0; i<I_Mat.size(); i++){
        HeaderBase hh = HeaderBase();
        std::vector<Double> xv;
        xv.push_back( _Q_plotrange_min - (Qela_bin)/2.0 + Double(i)*Qela_bin );
        xv.push_back( _Q_plotrange_min - (Qela_bin)/2.0 + Double(i+1)*Qela_bin );
        hh.Add("XRANGE",xv);

        ElementContainer EC = ElementContainer(hh);
        std::vector<Double> Err( I_Mat[i].size(), 1.0 );
        EC.Add("hw",hw_list);
        EC.Add("Pixels",I_Mat[i]);
        EC.Add("Error",Err);
        EC.SetKeys("hw","Pixels","Error");

        eca->Add(EC);
    }
    return true;
}
