#include "QzProjection.hh"
//////////////////////////////////////////////////////////
QzProjection::
QzProjection()
{
    Initialize();
}
//////////////////////////////////////////////////////////
QzProjection::
QzProjection( ElementContainerMatrix* ecm )
{
    Initialize();
    SetTarget(ecm);
}
//////////////////////////////////////////////////////////
QzProjection::
~QzProjection()
{
}
//////////////////////////////////////////////////////////
void QzProjection::
Initialize(){
    _QxBin.clear();
    _QyBin.clear();
    _bankIdList.clear();

    _MessageTag = "QzProjection::";

}
//////////////////////////////////////////////////////////
void QzProjection::
SetMapRange( double qx_min, double qx_max, double qx_bin,
             double qy_min, double qy_max, double qy_bin ){

    if (qx_min>qx_max){
        double tmp = qx_min;
        qx_min = qx_max;
        qx_max = tmp;
    }
    if (qy_min>qy_max){
        double tmp = qy_min;
        qy_min = qy_max;
        qy_max = tmp;
    }

    _QxRange = MakeBinsZeroCenter( qx_min, qx_max, qx_bin );
    _QyRange = MakeBinsZeroCenter( qy_min, qy_max, qy_bin );
    _QxBinWidth = qx_bin;
    _QyBinWidth = qy_bin;

    _QxBin.clear();
    _QyBin.clear();
    for (double qx=_QxRange.first; qx<(_QxRange.second+_QxBinWidth/2.0); qx+=_QxBinWidth)
        _QxBin.push_back( qx );
    for (double qy=_QyRange.first; qy<(_QyRange.second+_QyBinWidth/2.0); qy+=_QyBinWidth)
        _QyBin.push_back( qy );

    _IQ.clear();
    _Error.clear();
    _Count.clear();
    std::vector<double> tmp_qy( (_QyBin.size()-1), 0.0 );
    _IQ.resize( (_QxBin.size()-1), tmp_qy );
    _Error.resize( (_QxBin.size()-1), tmp_qy );
    _Count.resize( (_QxBin.size()-1), tmp_qy );

    std::cout << _MessageTag+"SetMapRange==="<< std::endl;
    std::cout << _MessageTag+"QxRange="<< _QxRange.first << "," << _QxRange.second << std::endl;
    std::cout << _MessageTag+"QyRange="<< _QyRange.first << "," << _QyRange.second << std::endl;
}
//////////////////////////////////////////////////////////
void QzProjection::
SetQzRange( double qz_min, double qz_max ){
    if (qz_min>qz_max){
        double tmp = qz_min;
        qz_min = qz_max;
        qz_max = tmp;
    }
    _QzRange.first = qz_min;
    _QzRange.second = qz_max;
}
//////////////////////////////////////////////////////////
void QzProjection::
SetLambRange( double lamb_min, double lamb_max ){
    if (lamb_min>lamb_max){
        double tmp = lamb_min;
        lamb_min = lamb_max;
        lamb_max = tmp;
    }
    _LambRange.first = lamb_min;
    _LambRange.second = lamb_max;
    std::cout <<"############### Lambda Range="<<_LambRange.first<<","<<_LambRange.second<<std::endl;
}
//////////////////////////////////////////////////////////
bool QzProjection::
AddBankId( UInt4 bankId ){
    if ((bankId > 15) || (bankId == 14)) return false;
    _bankIdList.push_back(bankId);
    return true;
}
//////////////////////////////////////////////////////////
std::pair<double,double> QzProjection::
MakeBinsZeroCenter(double s_val, double e_val, double step ){

    std::pair<double,double> ret;
    if (s_val>=0.0){
        double v = -step/2.0;
        while(true){
            if ((v+step)>=s_val){
                ret.first = v;
                break;
            }
            v += step;
        }

        while(true){
            if (v>e_val) {
                ret.second=v;
                break;
            }
            v += step;
        }
    }else{
        double v = -step/2.0;
        while(true){
            if (v<=s_val){
                ret.first = v;
                break;
            }
            v -= step;
        }
        v = -step/2.0;
        while(true){
            if (v>e_val){
                ret.second = v;
                break;
            }
            v += step;
        }
    }
    return ret;
}
//////////////////////////////////////////////////////////
void QzProjection::
Execute(){
    ElementContainerMatrix* ecm = Put();

    for (UInt4 i=0; i<_IQ.size(); i++){
        for (UInt4 j=0; j<_IQ[i].size(); j++){
            _IQ[i][j] = 0.0;
            _Error[i][j] = 0.0;
            _Count[i][j] = 0.0;
        }
    }

    std::cout <<"############### Lambda Range="<<_LambRange.first<<","<<_LambRange.second<<std::endl;
    for (UInt4 i=0; i<ecm->PutSize(); i++){
        HeaderBase* h_eca = ecm->PutPointer(i)->PutHeaderPointer();
        if (h_eca->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==1) continue;
        bool isSkip = false;
        if (_bankIdList.empty()){
        }else{
            if (h_eca->CheckKey(UTSUSEMI_KEY_HEAD_BANKID)==1){
                isSkip = true;
                UInt4 bankId = h_eca->PutInt4(UTSUSEMI_KEY_HEAD_BANKID);
                for (UInt4 ind=0; ind<_bankIdList.size(); ind++){
                    if (bankId == _bankIdList[ind]){
                        isSkip = false;
                        break;
                    }
                }
            }
        }
        if (isSkip) continue;
        for (UInt4 j=0; j<ecm->PutPointer(i)->PutSize(); j++){
            ElementContainer* ec = ecm->PutPointer(i)->PutPointer(j);
            if (ec->PutHeaderPointer()->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==1) continue;
            std::vector<double> iq_int = ec->PutY();
            std::vector<double> iq_err = ec->PutE();
            /*
            std::vector<double> qx = ec->Put("Qx");
            std::vector<double> qy = ec->Put("Qy");
            std::vector<double> qz = ec->Put("Qz");

            for (UInt4 k=0; k<iq_int.size(); k++){
                if ( ( _QxRange.first<=qx[k])&&( qx[k]<=_QxRange.second ) ){
                    if ( ( _QyRange.first<=qy[k] )&&( qy[k]<=_QyRange.second ) ){
                        if ( ( _QzRange.first<=qz[k] )&&( qz[k]<=_QzRange.second ) ){
                            UInt4 indx = floor( (qx[k]-_QxRange.first)/ _QxBinWidth );
                            UInt4 indy = floor( (qy[k]-_QyRange.first)/ _QyBinWidth );
                            //std::cout << "Hits indx,indy=" << indx << "," << indy << std::endl;
                            _IQ[indx][indy] += iq_int[k];
                            _Error[indx][indy] += iq_err[k]*iq_err[k];
                            _Count[indx][indy] += 1.0;

                        }else{
                            //std::cout << "QzRange.first, QzRange.second, qz=" << _QzRange.first << "," << _QzRange.second << "," << qz[k] << std::endl;
                        }
                    }
                }
            }
            */  //[inamura 140314]
            std::vector<double> qxh = ec->Put("Qx");
            std::vector<double> qyh = ec->Put("Qy");
            std::vector<double> qzh = ec->Put("Qz");
            std::vector<double> lmh = ec->Put("Lamb");

            for (UInt4 k=0; k<iq_int.size(); k++){
                double qx = (qxh[k+1]+qxh[k])/2.0;
                double qy = (qyh[k+1]+qyh[k])/2.0;
                double qz = (qzh[k+1]+qzh[k])/2.0;
                double lmd = lmh[k];//(lmh[k+1]+lmh[k])/2.0;
                if ( (_LambRange.first>lmd)||(_LambRange.second<lmd) ) continue;
                if ( ( _QxRange.first<=qx )&&( qx<=_QxRange.second ) ){
                    if ( ( _QyRange.first<=qy )&&( qy<=_QyRange.second ) ){
                        if ( ( _QzRange.first<=qz )&&( qz<=_QzRange.second ) ){
                            UInt4 indx = floor( (qx - _QxRange.first)/ _QxBinWidth );
                            UInt4 indy = floor( (qy - _QyRange.first)/ _QyBinWidth );
                            //std::cout << "Hits indx,indy=" << indx << "," << indy << std::endl;
                            if (iq_err[k]<0.0){
                            }else{
                                _IQ[indx][indy] += iq_int[k];
                                _Error[indx][indy] += iq_err[k]*iq_err[k];
                                _Count[indx][indy] += 1.0;
                            }
                        }else{
                            //std::cout << "QzRange.first, QzRange.second, qz=" << _QzRange.first << "," << _QzRange.second << "," << qz[k] << std::endl;
                        }
                    }
                }
            }

        }
    }
    for (UInt4 i=0; i<_IQ.size(); i++){
        for (UInt4 j=0; j<_IQ[i].size(); j++){
            if (_Count[i][j]!=0.0){
                _IQ[i][j] = _IQ[i][j]/_Count[i][j];
                _Error[i][j] = sqrt( _Error[i][j] )/_Count[i][j];
            }
        }
    }

}
//////////////////////////////////////////////////////////
PyObject* QzProjection::
PutListOfIntAlongQx( UInt4 indx ){
    std::vector<Double> ret;
    if ( indx>=PutNumOfQxBin() )
        return __gCppToPython.VectorDoubleToList( ret );
    return __gCppToPython.VectorDoubleToList( _IQ[indx] );
}
//////////////////////////////////////////////////////////
PyObject* QzProjection::
PutListOfErrAlongQx( UInt4 indx ){
    std::vector<Double> ret;
    if ( indx>=PutNumOfQxBin() )
        return __gCppToPython.VectorDoubleToList( ret );
    return __gCppToPython.VectorDoubleToList( _Error[indx] );
}
//////////////////////////////////////////////////////////
void QzProjection::
SetResultAsECM( ElementContainerMatrix* ecm ){
    ecm->Allocate( _QxBin.size()-1 );

    ecm->InputHeader( Put()->PutHeader() );

    std::vector<Double> dammy(2,0.0);
    for (UInt4 i=0; i<_QxBin.size()-1; i++){
        ElementContainerArray* eca = new ElementContainerArray();
        eca->Allocate( _QyBin.size()-1 );
        for (UInt4 j=0; j<_QyBin.size()-1; j++){
            ElementContainer* ec = new ElementContainer();
            std::vector<Double> qz, Intensity, Error;
            qz.push_back( _QzRange.first );
            qz.push_back( _QzRange.second );
            Intensity.push_back( _IQ[i][j] );
            Error.push_back( _Error[i][j] );
            ec->Add( "Qz", qz, "1/Ang" );
            ec->Add( "Intensity", Intensity, "Counts" );
            ec->Add( "Error", Error, "Counts" );
            ec->SetKeys( "Qz", "Intensity", "Error" );
            ec->AddToHeader( "TotalCounts", Intensity );
            ec->AddToHeader( "PixelPolarAngle", dammy );
            ec->AddToHeader( "PixelAzimAngle", dammy );
            std::vector<Double> q_posi(3,0.0);
            q_posi[0] = (_QxBin[i]+_QxBin[i+1])/2.0;
            q_posi[1] = (_QyBin[i]+_QyBin[i+1])/2.0;
            q_posi[2] = (_QzRange.first+_QzRange.second)/2.0;
            HeaderBase* hh=ec->PutHeaderPointer();
            hh->Add( "PixelPosition", q_posi );

            eca->Set( j, ec );
        }
        ecm->Set( i, eca );
    }
}
//////////////////////////////////////////////////////////
void QzProjection::
SetResultAsECA( ElementContainerArray* eca ){
    eca->Allocate( _QxBin.size()-1 );
    for (UInt4 i=0; i<_QxBin.size()-1; i++){
        ElementContainer* ec = new ElementContainer();
        std::vector<Double> qy_int(_QyBin.size()-1,0.0);
        std::vector<Double> qy_err(_QyBin.size()-1,0.0);
        for (UInt4 j=0; j<_QyBin.size()-1; j++){
            qy_int[j] = _IQ[i][j];
            qy_err[j] = _Error[i][j];
        }
        ec->Add( "Qy", _QyBin, "1/Ang" );
        ec->Add( "Intensity", qy_int, "Counts" );
        ec->Add( "Error", qy_err, "Counts" );
        ec->SetKeys( "Qy", "Intensity", "Error" );

        std::vector<Double> x_range(2,0.0);
        x_range[0] = _QxBin[i];
        x_range[1] = _QxBin[i+1];
        ec->AddToHeader("XRANGE", x_range );

        eca->Set( i, ec );
    }
    eca->AddToHeader( "Qx", _QxBin );
    eca->AddToHeader( "isHistogram",0 ); //[inamura 170118]
}
