#include "IncohCalcFor3HeSpinFilter.hh"
//////////////////////////////////////////////////////////
IncohCalcFor3HeSpinFilter::
IncohCalcFor3HeSpinFilter()
{
    Initialize();
}
//////////////////////////////////////////////////////////
IncohCalcFor3HeSpinFilter::
IncohCalcFor3HeSpinFilter( ElementContainerMatrix* dat1, ElementContainerMatrix* dat2, Double A, Double P_He_p, Double P_He_m, bool isAbsCorr )
{
    Initialize();
    SetData( dat1, dat2 );
    if (A!=0.0) _A = A;
    if (P_He_p!=0.0) _P_He_p = P_He_p;
    if (P_He_m!=0.0) _P_He_m = P_He_m;
    _isAbsCorr = isAbsCorr;
}
//////////////////////////////////////////////////////////
IncohCalcFor3HeSpinFilter::
~IncohCalcFor3HeSpinFilter()
{
    delete _st;
}
//////////////////////////////////////////////////////////
void IncohCalcFor3HeSpinFilter::
Initialize(){
    _NumOfMulth = UtsusemiGetNumOfMulTh();
    _MessageTag = "IncohCalcFor3HeSpinFilter::";
    _A = 0.0;
    _P_He_p = 0.0;
    _P_He_m = 0.0;
    _P_Multi = 0.0;
    _isAbsCorr = true;
    _DAT1 = NULL;
    _DAT2 = NULL;
    _pol_lam.clear();
    _pol_val.clear();
    _st = new StringTools();
}
//////////////////////////////////////////////////////////
void IncohCalcFor3HeSpinFilter::
SetData( ElementContainerMatrix* dat1, ElementContainerMatrix* dat2 ){
    _DAT1 = dat1;
    _DAT2 = dat2;
}
//////////////////////////////////////////////////////////
bool IncohCalcFor3HeSpinFilter::
SetMultiScatPval( Double Pval ){
    if ((Pval<=0.0) || (Pval>1.0)){
        UtsusemiError( _MessageTag+"SetMultiScatPval >> over range Pval ( 0.0 < Pval <= 1.0 )" );
        return false;
    }
    _P_Multi = Pval;
    return true;
}
//////////////////////////////////////////////////////////
bool IncohCalcFor3HeSpinFilter::
SetPolarizationTable( std::string _pathToData ){
    std::string foundPath=FindParamFilePath( _pathToData );
    if (foundPath==""){
        UtsusemiError( _MessageTag+"SetPolarizationTable >> No such file ("+_pathToData+")" );
        return false;
    }
    std::ifstream ifs( foundPath.c_str() );
    if (ifs.fail()){
        UtsusemiError( _MessageTag+"SetPolarizationTable >> Failed to open file ("+_pathToData+")" );
        return false;
    }
    std::string aline;
    _pol_lam.clear();
    _pol_val.clear();
    while(getline(ifs,aline)){
        if (aline.substr(0,1)!="#"){
            std::vector<std::string> conts = _st->SplitString( aline, "," );
            if (conts.size()>2){
                _pol_lam.push_back( _st->StringToDouble( conts[0] ) );
                _pol_val.push_back( _st->StringToDouble( conts[1] ) );
            }
        }
    }
    ifs.close();
    return true;
}
//////////////////////////////////////////////////////////
bool IncohCalcFor3HeSpinFilter::
Execute( ElementContainerMatrix* _coh, ElementContainerMatrix* _inc, ElementContainerMatrix* _NSF, ElementContainerMatrix* _SF ){
    std::cout << "#[inamura 200625] IncohCalcFor3HeSpinFilter::Ececute ";
    if (_isAbsCorr) std::cout << "_isAbsCorrby2theta = true" << std::endl;
    else std::cout << "_isAbsCorrby2theta = false" << std::endl;
    if (_pol_lam.empty()){
        UtsusemiError(_MessageTag+"Execute >> Not read polarization table. Do SetPolarizationTable.");
        return false;
    }
    if ((_A==0.0)||(_P_He_p==0.0)||(_P_He_m==0.0)){
        UtsusemiError(_MessageTag+"Execute >> Not set A or He pressure. Do SetAval or SetHePressure.");
        return false;
    }
    if ((_DAT1==NULL)||(_DAT2==NULL)){
        UtsusemiError(_MessageTag+"Execute >> Not set data. Do SetData.");
        return false;
    }

    if (_DAT1->PutPointer(0)->PutPointer(0)->CheckKey("Lamb")==0){
        UtsusemiError(_MessageTag+"Execute >> Given data is invalid.");
        return false;
    }

    // Interpolate polarization values with lambda values in given data
    std::vector<Double>* lambda = _DAT1->PutPointer(0)->PutPointer(0)->PutP("Lamb");
    std::vector<Double> Pn_v;
    std::vector<Double> A_v;
    for (UInt4 i=0; i<(lambda->size()); i++){
        Double lamb = lambda->at(i);
        Double Pn = 0.0;
        for (UInt4 j=0; j<(_pol_lam.size()-1); j++){
            if ( (lamb>=_pol_lam[j])&&(lamb<=_pol_lam[j+1]) ){
                Pn = _pol_val[j] + (_pol_val[j+1]-_pol_val[j])/(_pol_lam[j+1]-_pol_lam[j]) * (lamb - _pol_lam[j] );
                break;
            }
        }
        Pn_v.push_back(Pn);
        A_v.push_back( _A*lamb );
    }
    if (A_v.size()!=Pn_v.size()){
        UtsusemiError(_MessageTag+"Execute >> Failed to interpolate Polarization values.");
        return false;
    }

#ifdef MULTH
    omp_set_num_threads( _NumOfMulth );
#endif

    _coh->InputHeader( _DAT1->PutHeader() );
    _inc->InputHeader( _DAT1->PutHeader() );
    bool isRetNSF = false;
    if ((_NSF!=NULL)&&(_SF!=NULL)&&(_P_Multi>0)){
        _NSF->InputHeader( _DAT1->PutHeader() );
        _SF->InputHeader( _DAT1->PutHeader() );
        isRetNSF = true;
    }

    UInt4 ecm_size = _DAT1->PutSize();
    _coh->Allocate( ecm_size );
    _inc->Allocate( ecm_size );
    if (isRetNSF){
        _NSF->Allocate( ecm_size );
        _SF->Allocate( ecm_size );
    }
    for (UInt4 i=0; i<ecm_size; i++){
        ElementContainerArray* eca_co = new ElementContainerArray( _DAT1->PutPointer(i)->PutHeader() );
        ElementContainerArray* eca_ic = new ElementContainerArray( _DAT1->PutPointer(i)->PutHeader() );
        ElementContainerArray* eca_NSF = NULL;
        ElementContainerArray* eca_SF = NULL;
        if (isRetNSF){
            eca_NSF = new ElementContainerArray( _DAT1->PutPointer(i)->PutHeader() );
            eca_SF = new ElementContainerArray( _DAT1->PutPointer(i)->PutHeader() );
        }

        UInt4 eca_size = _DAT1->PutPointer(i)->PutSize();
        eca_co->Allocate( eca_size );
        eca_ic->Allocate( eca_size );
        if (isRetNSF){
            eca_NSF->Allocate( eca_size );
            eca_SF->Allocate( eca_size );
        }
        for (UInt4 j=0; j<eca_size; j++ ){
            ElementContainer* ec1 = _DAT1->PutPointer(i)->PutPointer(j);
            ElementContainer* ec2 = _DAT2->PutPointer(i)->PutPointer(j);

            ElementContainer* ec_co = new ElementContainer( *ec1 );
            ElementContainer* ec_ic = new ElementContainer( *ec1 );
            ElementContainer* ec_NSF = NULL;
            ElementContainer* ec_SF = NULL;
            if (isRetNSF){
                ec_NSF = new ElementContainer( *ec1 );
                ec_SF = new ElementContainer( *ec1 );
            }

            std::vector<Double>* I1 = ec1->PutP( ec1->PutYKey() );
            std::vector<Double>* I2 = ec2->PutP( ec2->PutYKey() );
            std::vector<Double>* E1 = ec1->PutP( ec1->PutEKey() );
            std::vector<Double>* E2 = ec2->PutP( ec2->PutEKey() );

            std::vector<Double> Ico(I1->size(),0.0);
            std::vector<Double> Iic(I1->size(),0.0);
            std::vector<Double> Eco(I1->size(),0.0);
            std::vector<Double> Eic(I1->size(),0.0);
            std::vector<Double> Insf(I1->size(),0.0);
            std::vector<Double> Isf(I1->size(),0.0);
            std::vector<Double> Ensf(I1->size(),0.0);
            std::vector<Double> Esf(I1->size(),0.0);
#pragma omp parallel for
#if (_OPENMP >= 200805)  // OpenMP 3.0 and later
            for (UInt4 k=0; k<(I1->size()); k++){
#else
            for (Int4 k=0; k<(I1->size()); k++){
#endif
                /**
                Double v1  = ((I1->at(k))+(I2->at(k)))*Pn_v[k]*sinh( A_v[k]*_P_He );
                Double vco = (3.0*((I1->at(k))-(I2->at(k)))*cosh( A_v[k]*_P_He )) + v1;
                Double vic = (((I2->at(k))-(I1->at(k)))*cosh( A_v[k]*_P_He )) + v1;
                Double v2  = 8.0*Pn_v[k]*cosh( A_v[k]*_P_He )*sinh( A_v[k]*_P_He );
                Ico[k] = exp( A_v[k] )*vco/v2;
                Iic[k] = 3.0*exp( A_v[k] )*vic/v2;
                */
                Double i1 = I1->at(k);
                Double i2 = I2->at(k);
                Double e1 = E1->at(k);
                Double e2 = E2->at(k);

                Double A = 0.0;
                if (_isAbsCorr){
                    HeaderBase* h_ec = ec1->PutHeaderPointer();
                    Double two_theta = h_ec->PutDouble("PolarAngle");
                    A = A_v[k]/cos( two_theta/180.0*M_PI );
                }else{
                    A = A_v[k];
                }
                Double E = exp(A);
                Double Cp = cosh( A*_P_He_p );
                Double Cm = cosh( A*_P_He_m );
                Double Sp = sinh( A*_P_He_p );
                Double Sm = sinh( A*_P_He_m );
                Double P = Pn_v[k];

                // Calculate intensity
                if (_P_Multi==0.0){
                    Double vco = 3.0*(i1-i2)*Cp + (i1+i2)*P*Sp;
                    Double vic =     (i2-i1)*Cp + (i1+i2)*P*Sp;
                    Double vm = 8.0*P*Cp*Sp;
                    Ico[k] = E*vco/vm;
                    Iic[k] = 3.0*E*vic/vm;

                    // Calculate error
                    Double K = E/8.0/P/Cp/Sp;
                    Eco[k] = sqrt( pow(K*(3.0*Cp+P*Sp),2)*e1*e1 + pow(K*(-3.0*Cp+P*Sp),2)*e2*e2 );
                    Eic[k] = sqrt( pow(3.0*K*(-Cp+P*Sp),2)*e1*e1 + pow(3.0*K*(Cp+P*Sp),2)*e2*e2 );
                }else{
                    //Double vnsf = (i1-i2)*C + (i1+i2)*P*S;
                    //Double vsf  = (i2-i1)*C + (i1+i2)*P*S;
                    //Double vm = 4.0*P*C*S;
                    Double vnsf = i1*(P*Sm+Cm) + i2*(P*Sp-Cp);
                    Double vsf  = i1*(P*Sm-Cm) + i2*(P*Sp+Cp);
                    Double vm = 2.0*P*(Sp*Cm + Cp*Sm);
                    Insf[k] = E*vnsf/vm;
                    Isf[k] = E*vsf/vm;
                    Ico[k] = Insf[k] - (1.0-_P_Multi)/_P_Multi*Isf[k];
                    Iic[k] = 1.0/_P_Multi*Isf[k];

                    // Calculate error
                    //Double K = E/4.0/P/C/S;
                    //Ensf[k] = sqrt( pow(K*(C+P*S),2)*e1*e1 + pow(K*(-1.0*C+P*S),2)*e2*e2 );
                    //Esf[k] = sqrt( pow(K*(-C+P*S),2)*e1*e1 + pow(K*(C+P*S),2)*e2*e2 );
                    Double K = E/vm;
                    Ensf[k] = sqrt( pow(K*(P*Sm+Cm),2)*e1*e1 + pow(K*(P*Sp-Cp),2)*e2*e2 );
                    Esf[k] = sqrt( pow(K*(P*Sm-Cm),2)*e1*e1 + pow(K*(P*Sp+Cp),2)*e2*e2 );
                    Double pp = (1.0-_P_Multi)/_P_Multi*(1.0-_P_Multi)/_P_Multi;
                    Eco[k] = sqrt( Ensf[k]*Ensf[k] + pp*Esf[k]*Esf[k] );
                    Eic[k] = 1.0/_P_Multi*Esf[k];
                }
            }

            // Construct coherent data
            std::string xkey = ec1->PutXKey();
            std::string ykey = ec1->PutYKey();
            std::string ekey = ec1->PutEKey();

            ec_co->Remove(ykey);
            ec_co->Remove(ekey);
            ec_co->Add(ykey, Ico);
            ec_co->Add(ekey, Eco);
            ec_co->SetKeys(xkey,ykey,ekey);
            eca_co->Set( j, ec_co );

            // Construct incoherent data
            ec_ic->Remove(ykey);
            ec_ic->Remove(ekey);
            ec_ic->Add(ykey, Iic);
            ec_ic->Add(ekey, Eic);
            ec_ic->SetKeys(xkey,ykey,ekey);
            eca_ic->Set( j, ec_ic );

            if (_P_Multi>0.0){
                // Construct non-spin-flip data on multi scattring
                ec_NSF->Remove(ykey);
                ec_NSF->Remove(ekey);
                ec_NSF->Add(ykey, Insf);
                ec_NSF->Add(ekey, Ensf);
                ec_NSF->SetKeys(xkey,ykey,ekey);
                eca_NSF->Set( j, ec_NSF );

                // Construct spin-flip data on multi scattring
                ec_SF->Remove(ykey);
                ec_SF->Remove(ekey);
                ec_SF->Add(ykey, Isf);
                ec_SF->Add(ekey, Esf);
                ec_SF->SetKeys(xkey,ykey,ekey);
                eca_SF->Set( j, ec_SF );
            }
        }

        _coh->Set( i, eca_co );
        _inc->Set( i, eca_ic );
        if (isRetNSF){
            _NSF->Set( i, eca_NSF );
            _SF->Set( i, eca_SF );
        }
    }
    return true;
}
