#include "ToPowderChoppers.hh"
//////////////////////////////////////////////////////////
ToPowderChoppers::
ToPowderChoppers()
{
    isDebugMode = false;
}

//////////////////////////////////////////////////////////
ToPowderChoppers::
~ToPowderChoppers()
{
}

//////////////////////////////////////////////////////////
ToPowderChoppers::
ToPowderChoppers( ElementContainerMatrix *ecm )
{
  //SetTarget( ecm );
    SetInputP( ecm );
    isDebugMode = true;
}

//////////////////////////////////////////////////////////
Int4 ToPowderChoppers::
_Execute( Double startQ, Double endQ, Double deltaQ )
{
  //ElementContainerMatrix* ecm = Put();
    ElementContainerMatrix* ecm = PutInput();
    HeaderBase* hh = ecm->PutHeaderPointer();
    
    bool isParamOK = true;

    Double _Ei = 0.0;
    if (hh->CheckKey(UTSUSEMI_KEY_HEAD_EI)==1){
        _Ei = hh->PutDouble(UTSUSEMI_KEY_HEAD_EI);
    }else{
        isParamOK = false;
    }

    if (deltaQ<=0.0) isParamOK = false;

    UtsusemiUnitConverter ucc;
    
    UInt4 numQ = 0;
    vector<Double> hw_vec;
    hw_vec.clear();
    
    if ((startQ>=0.0)&&(endQ>=0.0)&&(endQ>startQ)){
        numQ = (UInt4)( floor( (endQ - startQ)/deltaQ ) );
    }else{
        SearchInHeader sih( ecm );

        vector<Double> lim_theta = sih.FindLimitElementDoubleVector(UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES,0);
        Double min_theta = lim_theta[0]*M_PI/180.0;
        Double max_theta = lim_theta[1]*M_PI/180.0;
        if (isDebugMode) cout << "theta range=" << min_theta << "," << max_theta << endl;
        
        for (UInt4 i=0;i<ecm->PutTableSize();i++){
            HeaderBase* eca_h = ecm->PutPointer(i)->PutHeaderPointer();
            if ((eca_h->CheckKey(UTSUSEMI_KEY_HEAD_DETTYPE)==0)
                ||((eca_h->CheckKey(UTSUSEMI_KEY_HEAD_DETTYPE)==1)&&(eca_h->PutString(UTSUSEMI_KEY_HEAD_DETTYPE)!=UTSUSEMI_KEY_HEAD_DETTYPE_MONITOR))){
              hw_vec = ecm->PutPointer(i)->PutPointer(0)->PutX();
            }
        }
        if (hw_vec.size()==0) {
            isParamOK = false;
        }else{
            Double ki2 = ucc.EtoK2( _Ei );
            Double minQ = sqrt( 2.0*ki2*( 1.0 - cos( min_theta ) ) );
            Double maxQ = sqrt( 2.0*ki2*( 1.0 - cos( max_theta ) ) );
            for (UInt4 i=0;i<hw_vec.size();i++){
                Double kf2 = ucc.EtoK2( _Ei - hw_vec[i] );
                Double tmin = sqrt( ki2 + kf2 - 2.0*sqrt(ki2*kf2)*cos( min_theta ) );
                Double tmax = sqrt( ki2 + kf2 - 2.0*sqrt(ki2*kf2)*cos( max_theta ) );
                if (tmin<minQ){
                    minQ = tmin;
                }
                if (tmax>maxQ){
                    maxQ = tmax;
                }
            }
            if (isDebugMode) cout << "Q range=" << minQ << "," << maxQ << endl;
            
            startQ = deltaQ/2.0;
            while( (startQ+deltaQ)<minQ ){
                startQ += deltaQ;
            }
            numQ = 0;
            while( (startQ+deltaQ*(Double)(numQ))<maxQ ){
                numQ += 1;
            }
        }
    }
    
    if (isDebugMode) cout << "startQ, numQ=" << startQ <<","<< numQ << endl;
    
    Double theta = 0.0;
    vector<Double> thetaBinQConst;
    
    for (UInt4 i=0;i<numQ;i++){
        Double tQ = startQ + (Double)(i)*deltaQ;
        Double alpha = 1.0 - tQ*tQ/ucc.EtoK2( _Ei )/2.0;
        if ( (alpha>=-1.0) && (alpha<=1.0) ){
            theta = acos( alpha )/M_PI*180.0;  //[rad]->[degree]
            thetaBinQConst.push_back( theta );
        }else{
            thetaBinQConst.push_back(-1.0000e10 );
            isParamOK = false;
        }
    }
    
    #ifdef _OPENMP
    #pragma omp parallel for

    #if (_OPENMP >= 200805)  // OpenMP 3.0 and later
    for (UInt4 i=0;i<ecm->PutTableSize();i++){
    #else
    for (Int4 i=0;i<ecm->PutTableSize();i++){
    #endif

    #else
    for (UInt4 i=0;i<ecm->PutTableSize();i++){
    #endif  // #ifdef _OPENMP
        ElementContainerArray* eca = ecm->PutPointer(i);
        for (UInt4 j=0;j<eca->PutTableSize();j++){
            ElementContainer* ec = eca->PutPointer(j);
            HeaderBase* hh = ec->PutHeaderPointer();
            vector<Double> p_vec = hh->PutDoubleVector(UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES);
            if (hh->CheckKey(UTSUSEMI_KEY_HEAD_POLARANGLE)==1){
                hh->OverWrite(UTSUSEMI_KEY_HEAD_POLARANGLE, p_vec[0] );
            }else{
                hh->Add(UTSUSEMI_KEY_HEAD_POLARANGLE,p_vec[0]);
            }
        }
    }
    
    
    UInt4 num_of_binQ = thetaBinQConst.size()-1;
    vector< ElementContainer > eca_res_vec( num_of_binQ );
    vector<Double> thetaBinCenter_vec( num_of_binQ, -1.0 );
    vector<Double> thetaBinWidth_vec( num_of_binQ, -1.0 );
    
    for (UInt4 i=0;i<num_of_binQ;i++){
        SearchInHeader* sih2 = new SearchInHeader( ecm );
        sih2->Search(UTSUSEMI_KEY_HEAD_POLARANGLE,thetaBinQConst[i],thetaBinQConst[i+1]);
        /*
        vector<Int4> psd_vec = sih2->PutResultsPsd();
        vector<Int4> pix_vec = sih2->PutResultsPixel();
        */
        vector<UInt4> psd_vec = sih2->PutResultIndex(0);
        vector<UInt4> pix_vec = sih2->PutResultIndex(1);
        delete sih2;
        
        //if (psd_vec[0]!=-1){
        if (psd_vec.empty()){
        }else{
            AverageElementContainerChoppers* aec = new AverageElementContainerChoppers( ecm, psd_vec, pix_vec );
            ElementContainer ec_res = aec->GetAverage();
            delete aec;
            
            HeaderBase* hh = ec_res.PutHeaderPointer();
            if ((hh->CheckKey(UTSUSEMI_KEY_HEAD_MASKED)==0)||((hh->CheckKey(UTSUSEMI_KEY_HEAD_MASKED)==1)&&(hh->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==0))){
                vector<Double> pa_vec;
                pa_vec.clear();
                pa_vec.push_back( (thetaBinQConst[i+1]+thetaBinQConst[i])/2.0 );
                pa_vec.push_back( (thetaBinQConst[i+1]-thetaBinQConst[i])/2.0 );
                
                if (hh->CheckKey(UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES)==1){
                    hh->OverWrite(UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES,pa_vec);
                }else{
                    hh->Add(UTSUSEMI_KEY_HEAD_PIXELPOLARANGLES,pa_vec);
                }
                
                if (hh->CheckKey(UTSUSEMI_KEY_HEAD_POLARANGLE)==1){
                    hh->OverWrite(UTSUSEMI_KEY_HEAD_POLARANGLE,pa_vec[0]);
                }else{
                    hh->Add(UTSUSEMI_KEY_HEAD_POLARANGLE,pa_vec[0]);
                }
                vector<Double> aa_vec(2,-1.0);
                if (hh->CheckKey(UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES)==1)
                    hh->OverWrite(UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES,aa_vec);
                else
                    hh->Add(UTSUSEMI_KEY_HEAD_PIXELAZIMANGLES,aa_vec);

                if (hh->CheckKey(UTSUSEMI_KEY_HEAD_AZIMANGLE)==1){
                    hh->OverWrite(UTSUSEMI_KEY_HEAD_AZIMANGLE,aa_vec[0]);
                }else{
                    hh->Add(UTSUSEMI_KEY_HEAD_AZIMANGLE,aa_vec[0]);
                }
                eca_res_vec[i] = ec_res;
                thetaBinCenter_vec[i] = pa_vec[0];
                thetaBinWidth_vec[i] = pa_vec[1]*2.0;
                
            }
        }
    }
    
    ElementContainerArray eca_res;
    vector<Double> thetaBinCenter;
    vector<Double> thetaBinWidth;
    
    for (UInt4 i=0;i<num_of_binQ;i++){
        if (thetaBinCenter_vec[i]>=0.0){
            thetaBinCenter.push_back( thetaBinCenter_vec[i] );
            thetaBinWidth.push_back( thetaBinWidth_vec[i] );
            eca_res.Add( eca_res_vec[i] );
        }
    }
    
    HeaderBase hh_eca_res;
    hh_eca_res.Add("Bin",thetaBinQConst);
    hh_eca_res.Add("BinCenter",thetaBinCenter);
    hh_eca_res.Add("BinWidth",thetaBinWidth);
    hh_eca_res.Add(UTSUSEMI_KEY_HEAD_SAMPLETYPE,UTSUSEMI_KEY_HEAD_SAMPLETYPE_POWDER);
    
    eca_res.InputHeader( hh_eca_res );
    
    ElementContainerMatrix* ecm_res = new ElementContainerMatrix( ecm->PutHeader() );
    ecm_res->Add(eca_res);
    HeaderBase* hh_ecm_res = ecm_res->PutHeaderPointer();
    if (hh_ecm_res->CheckKey(UTSUSEMI_KEY_HEAD_SAMPLETYPE)==1){
        hh_ecm_res->OverWrite(UTSUSEMI_KEY_HEAD_SAMPLETYPE,UTSUSEMI_KEY_HEAD_SAMPLETYPE_POWDER);
    }else{
        hh_ecm_res->Add(UTSUSEMI_KEY_HEAD_SAMPLETYPE,UTSUSEMI_KEY_HEAD_SAMPLETYPE_POWDER);
    }
    
    SetOutput( ecm_res );
    return 0;
}

//////////////////////////////////////////////////////////
ElementContainerMatrix ToPowderChoppers::
Execute( Double startQ, Double endQ, Double deltaQ )
{
    if (_Execute( startQ, endQ, deltaQ )!=0){
        UtsusemiError("ToPowderChoppers::Execute >> Errors ");
    }
    return Put();
}

//////////////////////////////////////////////////////////
ElementContainerMatrix ToPowderChoppers::
Execute( Double deltaQ )
{
    if (_Execute( -1.0, -1.0, deltaQ )!=0){
        UtsusemiError("ToPowderChoppers::Execute >> Errors ");
    }
    return Put();
}
