#include "UtsusemiPlaneSlicer.hh"


//////////////// Constructor /////////////////////
UtsusemiPlaneSlicer::
UtsusemiPlaneSlicer() :
_valid(false), _datType(-1),_sliced(false), _plane_Xkey("X"),_plane_Ykey("Y"),_line_Xkey("X"),_line_Ykey("Y")  
{
    _Initialize();
}


///////////// Constructor with Matrix data //////////////////////
UtsusemiPlaneSlicer::
UtsusemiPlaneSlicer( ElementContainerMatrix *ecm ) :
_valid(false), _datType(0),_sliced(false), _plane_Xkey("X"),_plane_Ykey("Y"),_line_Xkey("X"),_line_Ykey("Y")  
{
    _ecm = ecm;
    //cout << "@@@ The data is an ElementContainerMatrix." << endl;
    _Initialize();
}

///////////// Constructor with Array data //////////////////////
UtsusemiPlaneSlicer::
UtsusemiPlaneSlicer( ElementContainerArray *eca ) :
_valid(false), _datType(1),_sliced(false),_plane_Xkey("X"),_plane_Ykey("Y"),_line_Xkey("X"),_line_Ykey("Y")
{
    _eca = eca;
    //cout << "@@@ The data is an ElementContainerArray." << endl;
    _Initialize();
}

////////////// Destructor ///////////////////////
UtsusemiPlaneSlicer::
~UtsusemiPlaneSlicer()
{
    if (_sliced ){
        delete _plane;
    }
}

///////////// Initialize /////////////////
void UtsusemiPlaneSlicer::
_Initialize(){
    _data_Keys.resize(3,"");
    pair<Int4,Int4> tmp;
    tmp.first=-1;
    tmp.second=-1;
    _data_Keys_Info.resize(3,tmp);
    _isAverage = true;
    _st = new StringTools();
}
///////////// Specify keys for 3 axes //////////////////////
bool UtsusemiPlaneSlicer::
SetAxes( string keyX, string keyY, string keyZ )
{
    _valid = false;
    ElementContainer* ec;
    HeaderBase* hh_eca;
    HeaderBase* hh_ec;
    if (_datType == 0){
        // Matrix
        ec = _ecm->PutPointer(0)->PutPointer(0);
        hh_eca = _ecm->PutPointer(0)->PutHeaderPointer();
        hh_ec = ec->PutHeaderPointer();
    }
    else if (_datType == 1){
        // Array
        ec = _eca->PutPointer(0);
    }
    else if (_datType == 2){
        // Matrix in DetectMap type
        ec = _ecm->PutPointer(0)->PutPointer(0);
        hh_eca = _ecm->PutPointer(0)->PutHeaderPointer();
        hh_ec = ec->PutHeaderPointer();
    }
    else{
        // No data
        return false;
    }
    // Check if all keys exist
    _data_Keys[0] = keyX;
    if (keyX!=""){
        Int4 tmp_xi = ec->PutIndexNumber(keyX);
        if (tmp_xi >=0){
            _xi = (UInt4)tmp_xi;
            _data_Keys_Info[0].first = 0;
            _data_Keys_Info[0].second = 0;
        }else{
            if (hh_eca->CheckKey( keyX )==1){
                _data_Keys_Info[0].first = 1;
                _data_Keys_Info[0].second = hh_eca->PutKeyLocation( keyX );
            }else{
                if (hh_ec->CheckKey( keyX )==1){
                    _data_Keys_Info[0].first = 2;
                    _data_Keys_Info[0].second = hh_ec->PutKeyLocation( keyX );
                }else{
                    _data_Keys[0] = "";
                    _data_Keys_Info[0].second = -1;
                    UtsusemiWarning( "UtsusemiPlaneSlicer::SetAxes > KeyX("+keyX+") is not exist. Use index No");
                }
            }
        }
    }

    _data_Keys[1] = keyY;
    if (keyY!=""){
        //cout << "keyY="+keyY<<endl;
        Int4 tmp_yi = ec->PutIndexNumber(keyY);
        if (tmp_yi >=0){
            _yi = (UInt4)tmp_yi;
            _data_Keys_Info[1].first = 0;
            _data_Keys_Info[1].second = 0;
        }else{
            if (hh_eca->CheckKey( keyY )==1){
                _data_Keys_Info[1].first = 1;
                _data_Keys_Info[1].second = hh_eca->PutKeyLocation( keyY );
            }else{
                if (hh_ec->CheckKey( keyY )==1){
                    _data_Keys_Info[1].first = 2;
                    _data_Keys_Info[1].second = hh_ec->PutKeyLocation( keyY );
                }else{
                    _data_Keys[1] = "";
                    _data_Keys_Info[1].second = -1;
                    UtsusemiWarning( "UtsusemiPlaneSlicer::SetAxes > KeyY("+keyY+") is not exist. Use index No");
                }
            }
        }
    }

    _data_Keys[2] = keyZ;
    if (keyZ!=""){
        Int4 tmp_zi = ec->PutIndexNumber(keyZ);
        if (tmp_zi >=0){
            _zi = (UInt4)tmp_zi;
            _data_Keys_Info[2].first = 0;
            _data_Keys_Info[2].second = 0;
        }else{
            if (hh_eca->CheckKey( keyZ )==1){
                _data_Keys_Info[2].first = 1;
                _data_Keys_Info[2].second = hh_eca->PutKeyLocation( keyZ );
            }else{
                if (hh_ec->CheckKey( keyZ )==1){
                    _data_Keys_Info[2].first = 2;
                    _data_Keys_Info[2].second = hh_ec->PutKeyLocation( keyZ );
                }else{
                    _data_Keys[2] = "";
                    _data_Keys_Info[2].second = -1;
                    UtsusemiWarning( "UtsusemiPlaneSlicer::SetAxes > KeyZ("+keyZ+") is not exist. Use index No");
                }
            }
        }
    }
    /*
    cout << "_data_Keys[0] = " << _data_Keys[0] <<endl;
    cout << "_data_Keys_Info[0].first=" << _data_Keys_Info[0].first << endl;
    cout << "_data_Keys_Info[0].second=" << _data_Keys_Info[0].second << endl;
    cout << "_data_Keys[1] = " << _data_Keys[1] <<endl;
    cout << "_data_Keys_Info[1].first=" << _data_Keys_Info[1].first << endl;
    cout << "_data_Keys_Info[1].second=" << _data_Keys_Info[1].second << endl;
    cout << "_data_Keys[2] = " << _data_Keys[2] <<endl;
    cout << "_data_Keys_Info[2].first=" << _data_Keys_Info[2].first << endl;
    cout << "_data_Keys_Info[2].second=" << _data_Keys_Info[2].second << endl;
    */
    // KeyX is vector in EC, KeyY is vector in EC, KeyZ is vector in EC
    if ((_data_Keys_Info[0].first==0)&&(_data_Keys_Info[1].first==0)&&(_data_Keys_Info[2].first==0)){
        _valid = true;
    // KeyX is vector in EC, KeyY and KeyZ are in Header
    }else if ((_data_Keys_Info[0].first==0)&&(_data_Keys_Info[1].first!=0)&&(_data_Keys_Info[2].first!=0)){
        // one of KeyY or KeyZ is in Header of EC, the other is in Header of ECA
        if (_data_Keys_Info[1].first!=_data_Keys_Info[2].first){
            _datType = 2;
            _valid = true;
        }
        if ((_data_Keys_Info[1].first==1)&&(_data_Keys[2]=="")) _data_Keys_Info[2].first=2;
        else if ((_data_Keys_Info[1].first==2)&&(_data_Keys[2]=="")) _data_Keys_Info[2].first=1;
        else if ((_data_Keys_Info[2].first==1)&&(_data_Keys[1]=="")) _data_Keys_Info[1].first=2;
        else if ((_data_Keys_Info[2].first==2)&&(_data_Keys[1]=="")) _data_Keys_Info[1].first=1;
    }else if ((_data_Keys_Info[0].first!=0)&&(_data_Keys_Info[1].first==0)&&(_data_Keys_Info[2].first!=0)){
        if (_data_Keys_Info[0].first!=_data_Keys_Info[2].first){
            _datType = 2;
            _valid = true;
        }
        if ((_data_Keys_Info[0].first==1)&&(_data_Keys[2]=="")) _data_Keys_Info[2].first=2;
        else if ((_data_Keys_Info[0].first==2)&&(_data_Keys[2]=="")) _data_Keys_Info[2].first=1;
        else if ((_data_Keys_Info[2].first==1)&&(_data_Keys[0]=="")) _data_Keys_Info[0].first=2;
        else if ((_data_Keys_Info[2].first==2)&&(_data_Keys[0]=="")) _data_Keys_Info[0].first=1;
    // KeyX ans KeyY are in Header , KeyZ is vector
    }else if ((_data_Keys_Info[0].first!=0)&&(_data_Keys_Info[1].first!=0)&&(_data_Keys_Info[2].first==0)){
        // KeyX=KeyY=""
        if ((_data_Keys_Info[0].first==-1)&&(_data_Keys_Info[1].first==-1)){
            // In this pattern, KeyX is index of ECA, KeyY is index of EC
            _data_Keys_Info[0].first = 1;
            _data_Keys_Info[1].first = 2;
            _datType = 2;
            _valid = true;
        }
        if (_data_Keys_Info[0].first!=_data_Keys_Info[1].first){
            _datType = 2;
            _valid = true;
        }
        if ((_data_Keys_Info[0].first==1)&&(_data_Keys[1]=="")) _data_Keys_Info[1].first=2;
        else if ((_data_Keys_Info[0].first==2)&&(_data_Keys[1]=="")) _data_Keys_Info[1].first=1;
        else if ((_data_Keys_Info[1].first==1)&&(_data_Keys[0]=="")) _data_Keys_Info[0].first=2;
        else if ((_data_Keys_Info[1].first==2)&&(_data_Keys[0]=="")) _data_Keys_Info[0].first=1;
    }else{
        UtsusemiError( "UtsusemiPlaneSlicer::SetAxes > Failed.");
    }
    
    return _valid;
}

//// Slice a plane ////
ElementContainerArray* UtsusemiPlaneSlicer::
GetPlane(vector<Double> org, vector<Double> x, vector<Double> y, Double xbin, Double ybin, 
        vector<Double> xrange, vector<Double> yrange, Double thickness)
{
    ElementContainerArray* eca = new ElementContainerArray();
    bool ret = GetPlane( eca, org, x, y, xbin, ybin, xrange, yrange, thickness );
    if (ret){
    }else{
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > Failed." );
    }
    return eca;
}
bool UtsusemiPlaneSlicer::
GetPlane(ElementContainerArray* ret_eca, vector<Double> org, vector<Double> x, vector<Double> y, Double xbin, Double ybin, vector<Double> xrange, vector<Double> yrange, Double thickness){
    // Is there any plane ?
    if (_sliced ){
        delete _plane;
        _sliced = false;
    }
    // Check if valid data 
    if (!_valid){
        if (_datType < 0){
            UtsusemiError("UtsusemiPlaneSlicer::GetPlane > There is no data to slice.");
        }
        else{
            UtsusemiError("UtsusemiPlaneSlicer::GetPlane > Keys have not set yet.");
        }
        // Return empty array
        return false;
    }
    // Check X Range
    if (xrange[0] > xrange[1]){
        Double tmpx = xrange[0];
        xrange[0] = xrange[1];
        xrange[1] = tmpx;
    }

    // Check Y Range
    if (yrange[0] > yrange[1]){
        Double tmpy = yrange[0];
        yrange[0] = yrange[1];
        yrange[1] = tmpy;
    }

    // Calc constants of plane equation
    _const_a = (x[1]-org[1])*(y[2]-org[2]) - (y[1]-org[1])*(x[2]-org[2]);
    _const_b = (x[2]-org[2])*(y[0]-org[0]) - (y[2]-org[2])*(x[0]-org[0]);
    _const_c = (x[0]-org[0])*(y[1]-org[1]) - (y[0]-org[0])*(x[1]-org[1]);
    _const_d = (_const_a * org[0] + _const_b * org[1] + _const_c * org[2]) * -1.0;
    //cout << "const: " <<  _const_a << " : " <<  _const_b << " : " << _const_b << " : " << _const_b << endl;

    _thick2 = thickness / 2.0;

    // calc size of vector X 
    Double xlength = sqrt((x[0]-org[0])*(x[0]-org[0]) + (x[1]-org[1])*(x[1]-org[1]) + (x[2]-org[2])*(x[2]-org[2]));

    // calc size of vector Y 
    Double ylength = sqrt((y[0]-org[0])*(y[0]-org[0]) + (y[1]-org[1])*(y[1]-org[1]) + (y[2]-org[2])*(y[2]-org[2]));

    //cout << "Length: " <<  xlength << " : " <<  ylength << endl;

    // Make unit vector of X
    vector<Double> ux = vector<Double>(3);
    ux[0] = (x[0]-org[0]) / xlength;
    ux[1] = (x[1]-org[1]) / xlength;
    ux[2] = (x[2]-org[2]) / xlength;
    //  Make unit vector of Y
    vector<Double> uy = vector<Double>(3);
    uy[0] = (y[0]-org[0]) / ylength;
    uy[1] = (y[1]-org[1]) / ylength;
    uy[2] = (y[2]-org[2]) / ylength;

    // calculate Dot product of both unit vectors 
    Double Dot = ux[0]*uy[0] + ux[1]*uy[1] +  ux[2]*uy[2];

    //cout << "Dot: " <<  Dot << endl;
        
    // Too small angle
    if ((1.0 - fabs(Dot)) < DBL_MIN * 1000.0){
        UtsusemiError("UtsusemiPlaneSlicer::GetPlane > 3 points are on a same line.");
        return false;
    }
    // Make x bin
    vector<Double> xdat;
    
    _MakeBin(xdat, xrange, xbin);
    Double xstart = xdat[0] - xbin/2.0;

    // Make y bin
    vector<Double> ydat;
    
    _MakeBin(ydat, yrange, ybin);
    Double ystart = ydat[0] - ybin/2.0;

    // Create an ElementContainerArray Header
    HeaderBase hh_plane;
    // Set xbin data to array header 
    hh_plane.Add(_plane_Xkey, xdat);
    // Create an ElementContainerArray to contain results
    if (ret_eca!=NULL) ret_eca->InputHeader( hh_plane );

    UInt4 numX = xdat.size(); 
    UInt4 numY = ydat.size();
    
    // Prepare integration counter and buffer
    vector<vector<int> > counter = vector<vector<int> >(numX, vector<int>(numY, 0));
    vector<vector<Double> > intSum = vector<vector<Double> >(numX, vector<Double>(numY, 0.0));
    vector<vector<Double> > errSum = vector<vector<Double> >(numX, vector<Double>(numY, 0.0));

    if ((_datType == 0)||(_datType==2)){
        _SlicePlaneFromMatrix(counter, intSum, errSum, org, ux, uy, xbin, ybin, xstart, ystart); 
    }
    else{
        _SlicePlaneFromArray(_eca,counter, intSum, errSum, org, ux, uy, xbin, ybin, xstart, ystart);
    }

    // Devide by counter
    Double xval = xstart;
    for (UInt4 i = 0; i < numX; i++){
        // Create an ElementContainer
        HeaderBase hh_ec;
        vector<Double> xrange(2,0.0);
        xrange[0]=xval;
        xrange[1]=xval+xbin;
        xval += xbin;
        hh_ec.Add(UTSUSEMI_KEY_HEAD_XRANGE, xrange );
        hh_ec.Add(_plane_Xkey, xdat[i]);
        ElementContainer* ec = new ElementContainer(hh_ec);
        vector<Double> ecInt = vector<Double>(numY, 0.0);
        vector<Double> ecErr = vector<Double>(numY, 0.0);

        for (UInt4 j = 0; j < numY; j++){
            // Exist counter?
            if (counter[i][j] > 0){
                if (_isAverage){
                    ecInt[j] = intSum[i][j]/counter[i][j];
                    ecErr[j] = sqrt(errSum[i][j])/counter[i][j];
                }else{
                    ecInt[j] = intSum[i][j];
                    ecErr[j] = sqrt(errSum[i][j]);
                }
            }else{
                ecErr[j] = -1.0;
            }
                
        }
        ec->Add(_plane_Ykey, ydat);
        ec->Add(UTSUSEMI_KEY_INTENSITY, ecInt);
        ec->Add(UTSUSEMI_KEY_ERROR, ecErr);
        ec->SetKeys(_plane_Ykey,UTSUSEMI_KEY_INTENSITY,UTSUSEMI_KEY_ERROR);
        // Add ec to array(Plane)
        ret_eca->Add(*ec);
    }
    _sliced = true;
    _plane = new ElementContainerArray( (*ret_eca) );
    return true;
}
bool UtsusemiPlaneSlicer::
GetPlane(ElementContainerArray* eca, PyObject *org, PyObject *x, PyObject *y, Double xbin, Double ybin, PyObject *xrange, PyObject *yrange, Double thickness){
    vector<Double> org_v = __gCppToPython.ListToDoubleVector( org );
    vector<Double> x_v = __gCppToPython.ListToDoubleVector( x );
    vector<Double> y_v = __gCppToPython.ListToDoubleVector( y );
    vector<Double> xrange_v = __gCppToPython.ListToDoubleVector( xrange );
    vector<Double> yrange_v = __gCppToPython.ListToDoubleVector( yrange );
    bool ret = true;
    if (org_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > org cannot be converted." );
        ret = false;
    }
    if (x_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > x cannot be converted." );
        ret = false;
    }
    if (y_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > y cannot be converted." );
        ret = false;
    }
    if (xrange_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > xrange cannot be converted." );
        ret = false;
    }
    if (yrange_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > yrange cannot be converted." );
        ret = false;
    }

    if (ret) return GetPlane( eca, org_v, x_v, y_v, xbin, ybin, xrange_v, yrange_v, thickness );
    else return false;
    
}

//// Make bins vector////
void UtsusemiPlaneSlicer::_MakeBin(vector<Double>& bins, vector<Double> binRange, Double bin)
{
    Double start = binRange[0];
    Double end = binRange[1];
    if (binRange[0] > binRange[1]){
        start = binRange[1];
        end = binRange[0];
    }

    int num = (int)(fabs(start)/ bin);
    
    // Start point must be divisible by bin. 
    Double startN =  num * bin;

    // In case of minus
    if (start < 0.0){
        startN = 0.0 - startN;
        if (startN > start){
            startN -= bin;
        }
    }
    while (startN < end){
        bins.push_back(startN);
        startN += bin;
    }
    // Add the last point 
    bins.push_back(startN);
}

//// Slice Plane from matrix////
void UtsusemiPlaneSlicer::
_SlicePlaneFromMatrix(vector<vector<int> > &counter, vector<vector<Double> > &intSum, vector<vector<Double> > &errSum,
                        vector<Double> org, vector<Double> x, vector<Double> y, Double xbin, Double ybin,
                        Double xstart, Double ystart)
{

    for (UInt4 i=0; i < _ecm->PutSize(); i++){
        
        ElementContainerArray* eca = _ecm->PutPointer(i);
        HeaderBase* h_a = eca->PutHeaderPointer();
        // Masked array?
        if ((h_a->CheckKey(UTSUSEMI_KEY_HEAD_MASKED)==1)&&(h_a->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==1)){
            continue;
        }
        
        _SlicePlaneFromArray(eca,counter, intSum, errSum, org, x, y, xbin, ybin, xstart, ystart,i); 
    }
    
}
//// Slice Plane from array////
void UtsusemiPlaneSlicer::
_SlicePlaneFromArray(ElementContainerArray* eca, vector<vector<int> > &counter, vector<vector<Double> > &intSum, vector<vector<Double> > &errSum,
                        vector<Double> org, vector<Double> ux, vector<Double>uy, Double xbin, Double ybin, Double xstart, Double ystart, UInt4 eca_ind)
{
    Double sqd = _const_a*_const_a + _const_b*_const_b + _const_c*_const_c;  // sqd never be 0.0.
    Double sqrd = sqrt(sqd);
    
    UInt4 xlimit = intSum.size()-1;
    UInt4 ylimit = intSum[0].size()-1;
    
    #ifdef MULTH
    omp_set_num_threads( MULTH );
    #endif

    #ifdef _OPENMP
    #pragma omp parallel for

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

    #else
    // Pararell for
    for (UInt4 i=0; i < eca->PutSize(); i++){
    #endif  // #ifdef _OPENMP
        //cout << "Index: " << i << endl;

        ElementContainer* ec = eca->PutPointer(i);
        HeaderBase* h_ec = ec->PutHeaderPointer();
        // Masked ec?
        if ((h_ec->CheckKey(UTSUSEMI_KEY_HEAD_MASKED)==1)&&(h_ec->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==1)){
            continue;
        }
        vector<Double> err = ec->PutE();
        vector<Double> intY = ec->PutY();
        vector<Double>* vx;
        vector<Double>* vy;
        vector<Double>* vz;
        vector<Double>* vv;

        vector<bool> isDelete(3,true);
        UInt4 v_index = 0; // X-axis
        if (_data_Keys_Info[v_index].first==0){
            if ( (*ec)(_xi)->size() == intY.size()+1 ){
                vv = new vector<Double>( intY.size() );
                for (UInt4 j=0; j<intY.size(); j++) vv->at(j) = ( (*ec)(_xi)->at(j)+(*ec)(_xi)->at(j+1))/2.0;
            }else{
                vv = (*ec)(_xi);
                isDelete[v_index]=false;
            }
        }else if (_data_Keys_Info[v_index].first==1){ // x-val is in ECA Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)eca_ind );
            else if (_data_Keys_Info[v_index].second<3)
                vv = new vector<Double>( intY.size(), eca->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = eca->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = eca->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }else{  // x-val is in EC Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)i );
            else if (_data_Keys_Info[v_index].second<3)
                vv = new vector<Double>( intY.size(), ec->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = ec->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = ec->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }
        vx = vv;
        
        v_index = 1; // Y-axis
        if (_data_Keys_Info[v_index].first==0){
            if ( (*ec)(_yi)->size() == intY.size()+1 ){
                vv = new vector<Double>( intY.size() );
                for (UInt4 j=0; j<intY.size(); j++) vv->at(j) = ( (*ec)(_yi)->at(j)+(*ec)(_yi)->at(j+1) )/2.0;
            }else{
                vv = (*ec)(_yi);
                isDelete[v_index]=false;
            }
        }else if (_data_Keys_Info[v_index].first==1){  // y-val is in ECA Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)eca_ind );
            else if (_data_Keys_Info[v_index].second<3)
                vv = new vector<Double>( intY.size(), eca->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = eca->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = eca->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }else{  // y-val is in EC Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)i );
            else if (_data_Keys_Info[v_index].second==2)
                vv = new vector<Double>( intY.size(), ec->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = ec->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = ec->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }
        vy = vv;

        v_index = 2; // Z-axis
        if (_data_Keys_Info[v_index].first==0){
            if ( (*ec)(_zi)->size() == intY.size()+1 ){
                vv = new vector<Double>( intY.size() );
                for (UInt4 j=0; j<intY.size(); j++) vv->at(j) = ( (*ec)(_zi)->at(j)+(*ec)(_zi)->at(j+1) )/2.0;
            }else{
                vv = (*ec)(_zi);
                isDelete[v_index]=false;
            }
        }else if (_data_Keys_Info[v_index].first==1){  // z-val is in ECA Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)eca_ind );
            else if (_data_Keys_Info[v_index].second<3)
                vv = new vector<Double>( intY.size(), eca->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = eca->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = eca->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }else{  // z-val is in EC Header
            if (_data_Keys_Info[v_index].second<0)
                vv = new vector<Double>( intY.size(), (Double)i );
            else if (_data_Keys_Info[v_index].second<3)
                vv = new vector<Double>( intY.size(), ec->PutHeaderPointer()->PutDouble( _data_Keys[v_index] ) );
            else if (_data_Keys_Info[v_index].second==4){
                vector<Int4> t1 = ec->PutHeaderPointer()->PutInt4Vector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (Double)((t1[0]+t1[1]))/2.0 );
            }
            else{
                vector<Double> t1 = ec->PutHeaderPointer()->PutDoubleVector( _data_Keys[v_index] );
                vv = new vector<Double>( intY.size(), (t1[0]+t1[1])/2.0 );
            }
        }
        vz = vv;

        //cout << "------ size all=" << vx->size() << "," << vy->size() << "," << vz->size() << endl;
        if (vx->size() != vy->size() || vx->size() != vz->size())
        {
            continue;
        }
        for (UInt4 j=0; j < err.size(); j++){
            // Masked point ?
            if (err[j] < 0.0){
                continue;
            }
            
            // Calc distance from the plane
            Double point = _const_a * (*vx)[j] +  _const_b * (*vy)[j] + _const_c * (*vz)[j] + _const_d;
            Double dist = fabs(point)/ sqrd;
            
            // Check if the point is withen a thickness of the plane
            if (dist <= _thick2)
            {
                // Calc projection point on the plane
                Double t = point / sqd;
                Double hx = (*vx)[j] - _const_a * t;
                Double hy = (*vy)[j] - _const_b * t;
                Double hz = (*vz)[j] - _const_c * t;

                // Shift to org
                hx -= org[0];
                hy -= org[1];
                hz -= org[2];
                
                // Calc projected vector length on X-axis
                Double xx = hx * ux[0] + hy * ux[1] + hz * ux[2];
                // calc index of xbin
                UInt4 xindex = (UInt4)(xx/xbin);
                //cout <<j << ":" <<  xindex << "/" <<  xlimit << endl;
                if (xindex < 0 || xindex > xlimit){
                    // Out of xrange
                    continue;
                }
                
                // Calc projected vector length
                Double yy = hx * uy[0] + hy * uy[1] + hz * uy[2];
                // calc index of ybin
                UInt4 yindex = (UInt4)(yy/ybin);
                //cout <<j << ":" <<  yindex << "/" <<  ylimit << endl;
                if (yindex < 0 || yindex > ylimit){
                    // out of y range
                    continue;
                }
                //cout <<j << ":" <<  xindex << " : " <<  yindex << " : " << intY[j] << endl;
                #ifdef _OPENMP
                #pragma omp critical  
                { 
                #endif
                    // Critical section for paralell
                    intSum[xindex][yindex] += intY[j];
                    errSum[xindex][yindex] += err[j]*err[j];
                    counter[xindex][yindex] += 1;
                #ifdef _OPENMP
                }
                #endif
            }
        }
        if (isDelete[0]) delete vx;
        if (isDelete[1]) delete vy;
        if (isDelete[2]) delete vz;
            
    }

}

//// Slice Plane as DetectMap////
bool UtsusemiPlaneSlicer::
GetPlaneAsDetectMap( ElementContainerArray* _eca, string keyX, string keyY, string keyZ, vector<Double> zrange ){
    _valid = false;

    ElementContainer* ec = _ecm->PutPointer(0)->PutPointer(0);
    HeaderBase* hh_eca = _ecm->PutPointer(0)->PutHeaderPointer();
    HeaderBase* hh_ec = ec->PutHeaderPointer();

    if (ec->CheckKey(keyZ)!=1){
        UtsusemiError("UtsusemiPlaneSlicer::GetPlaneAsDetectMap > No keyZ in data ("+keyZ+")");
        return false;
    }
    
    if (zrange.size()<2){
        vector<Double> _v = ec->Put(keyZ);
        zrange.resize(2);
        zrange[0] = _v.front();
        zrange[1] = _v.back();
    }
    
    bool ret = SetAxes( keyX, keyY, keyZ );
    if ((ret)&&(_data_Keys_Info[0].first==1)&&(_data_Keys_Info[1].first==2)){
    }else{
        return false;
    }

    // Make xaxis and yaxis vector
    vector<Double> xaxis;
    vector<Double> yaxis;
    for (UInt4 i=0; i<(_ecm->PutSize()); i++){
        for (UInt4 j=0; j<(_ecm->PutPointer(i)->PutSize()); j++){
            Double xd = -1.;
            Double yd = -1.;
            
            if (_data_Keys[0]==""){
                xd = (Double)i;
            }else if (_data_Keys_Info[0].second<3){
                xd = _ecm->PutPointer(i)->PutHeaderPointer()->PutDouble( _data_Keys[0] );
            }else if (_data_Keys_Info[0].second==4){
                vector<Int4> vx = _ecm->PutPointer(i)->PutHeaderPointer()->PutInt4Vector( _data_Keys[0] );
                xd = (Double)(vx[0]+vx[1])/2.0;
            }else if (_data_Keys_Info[0].second==5){
                vector<Double> vx = _ecm->PutPointer(i)->PutHeaderPointer()->PutDoubleVector( _data_Keys[0] );
                xd = (Double)(vx[0]+vx[1])/2.0;
            }
            
            if (_data_Keys[1]==""){
                yd = (Double)j;
            }else if (_data_Keys_Info[1].second==1){
                yd = _ecm->PutPointer(i)->PutHeaderPointer()->PutDouble( _data_Keys[1] );
            }else if (_data_Keys_Info[1].second==2){
                yd = _ecm->PutPointer(i)->PutPointer(j)->PutHeaderPointer()->PutDouble( _data_Keys[1] );
            }else if (_data_Keys_Info[1].second==4){
                vector<Int4> vy = _ecm->PutPointer(i)->PutHeaderPointer()->PutInt4Vector( _data_Keys[1] );
                yd = (Double)(vy[0]+vy[1])/2.0;
            }else if (_data_Keys_Info[1].second==5){
                vector<Double> vy = _ecm->PutPointer(i)->PutHeaderPointer()->PutDoubleVector( _data_Keys[1] );
                yd = (Double)(vy[0]+vy[1])/2.0;
            }

            xaxis.push_back(xd);
            yaxis.push_back(yd);
        }
    }

    sort( xaxis.begin(), xaxis.end() );
    sort( yaxis.begin(), yaxis.end() );
    unique( xaxis.begin(), xaxis.end() );
    unique( yaxis.begin(), yaxis.end() );

    // Make Slice parameters
    Double z_val = (zrange[0]+zrange[1])/2.0;
    Double thickness = fabs( zrange[1]-zrange[0] );
    vector<Double> pv_org(3,0.0);
    vector<Double> pv_x(3,0.0);
    vector<Double> pv_y(3,0.0);

    pv_org[0] = xaxis.front();
    pv_org[1] = yaxis.front();
    pv_org[2] = z_val;
    pv_x[0] = xaxis.back();
    pv_x[1] = yaxis.front();
    pv_x[2] = z_val;
    pv_y[0] = xaxis.front();
    pv_y[1] = yaxis.back();
    pv_y[2] = z_val;

    Double xbin = xaxis[1]-xaxis[0];
    Double ybin = yaxis[1]-yaxis[0];

    vector<Double> xrange(2,0.0);
    vector<Double> yrange(2,0.0);
    xrange[0] = xaxis.front();
    xrange[1] = xaxis.back();
    yrange[0] = yaxis.front();
    yrange[1] = yaxis.back();

    if (_data_Keys[0]=="")
        _plane_Xkey = "index";
    else
        _plane_Xkey = _data_Keys[0];
    
    if (_data_Keys[1]=="")
        _plane_Ykey = "index";
    else
        _plane_Ykey = _data_Keys[1];


    ret = GetPlane( _eca, pv_org, pv_x, pv_y, xbin, ybin, xrange, yrange, thickness );

    if (ret){
        // Make Conversion map from x,y on sliced plane to i,j of _ecm
        // initialize
        _ConvMat.clear();
        for (UInt4 x=0; x<(_eca->PutSize() ); x++){
            vector< pair<Int4,Int4> > tmp;
            vector<Double> zv = _eca->PutPointer(0)->PutY();
            for (UInt4 y=0; y<(zv.size()); y++){
                pair<Int4,Int4> tmp2;
                tmp2.first = -1;
                tmp2.second= -1;
                tmp.push_back(tmp2);
            }
            _ConvMat.push_back(tmp);
        }
        // Set Conversion map
        for (UInt4 i=0; i<(_ecm->PutSize()); i++){
            for (UInt4 j=0; j<(_ecm->PutPointer(i)->PutSize()); j++){
                Double xd = -1.;
                Double yd = -1.;
                
                if (_data_Keys[0]==""){
                    xd = (Double)i;
                }else if (_data_Keys_Info[0].second<3){
                    xd = _ecm->PutPointer(i)->PutHeaderPointer()->PutDouble( _data_Keys[0] );
                }else if (_data_Keys_Info[0].second==4){
                    vector<Int4> vx = _ecm->PutPointer(i)->PutHeaderPointer()->PutInt4Vector( _data_Keys[0] );
                    xd = (Double)(vx[0]+vx[1])/2.0;
                }else if (_data_Keys_Info[0].second==5){
                    vector<Double> vx = _ecm->PutPointer(i)->PutHeaderPointer()->PutDoubleVector( _data_Keys[0] );
                    xd = (Double)(vx[0]+vx[1])/2.0;
                }
                
                if (_data_Keys[1]==""){
                    yd = (Double)j;
                }else if (_data_Keys_Info[1].second==1){
                    yd = _ecm->PutPointer(i)->PutHeaderPointer()->PutDouble( _data_Keys[1] );
                }else if (_data_Keys_Info[1].second==2){
                    yd = _ecm->PutPointer(i)->PutPointer(j)->PutHeaderPointer()->PutDouble( _data_Keys[1] );
                }else if (_data_Keys_Info[1].second==4){
                    vector<Int4> vy = _ecm->PutPointer(i)->PutHeaderPointer()->PutInt4Vector( _data_Keys[1] );
                    yd = (Double)(vy[0]+vy[1])/2.0;
                }else if (_data_Keys_Info[1].second==5){
                    vector<Double> vy = _ecm->PutPointer(i)->PutHeaderPointer()->PutDoubleVector( _data_Keys[1] );
                    yd = (Double)(vy[0]+vy[1])/2.0;
                }

                Int4 indX = -1;
                Int4 indY = -1;
                for (UInt4 k=0; k<xaxis.size(); k++){
                    if (fabs(xaxis[k]-xd)<1e-30){
                        indX = (Int4)k;
                        break;
                    }
                }
                for (UInt4 k=0; k<yaxis.size(); k++){
                    if (fabs(yaxis[k]-yd)<1e-30){
                        indY = (Int4)k;
                        break;
                    }
                }
                if ((indX<0.)||(indY<0.)){
                    continue;
                }else if ((UInt4)(indX)>=(_ConvMat.size())){
                    string msg = "UtsusemiPlaneSlicer::GetPlaneAsDetectMap > indX Over _ConvMat.size() (";
                    msg += _st->UInt4ToString(indX)+"/"+_st->UInt4ToString(_ConvMat.size());
                    UtsusemiError(msg);
                }else if ((UInt4)(indY)>=(_ConvMat[0].size())){
                    string msg = "UtsusemiPlaneSlicer::GetPlaneAsDetectMap > indY Over _ConvMat[0].size() (";
                    msg += _st->UInt4ToString(indY)+"/"+_st->UInt4ToString(_ConvMat[0].size());
                    UtsusemiError(msg);
                }else{
                    _ConvMat[ (UInt4)indX ][ (UInt4)indY ].first = (Int4)i;
                    _ConvMat[ (UInt4)indX ][ (UInt4)indY ].second = (Int4)j;
                }
            }
        }
    }

    return ret;
}
bool UtsusemiPlaneSlicer::
GetPlaneAsDetectMap( ElementContainerArray* _eca, string keyX, string keyY, string keyZ, PyObject *zrange ){
    vector<Double> z_v = __gCppToPython.ListToDoubleVector( zrange );
    if (z_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlaneAsDetectMap > zrange cannot be converted." );
        return false;
    }else{
        return GetPlaneAsDetectMap( _eca, keyX, keyY, keyZ, z_v );
    }
}
////////// Pick Up index of EC from sliced ECA position ////////////
vector<UInt4> UtsusemiPlaneSlicer::
PutDetectMapIndex( UInt4 xind, UInt4 yind ){
    vector<UInt4> ret;
    if ( (xind>=_ConvMat.size()) || (yind>=_ConvMat[0].size()) ){
        UtsusemiError("UtsusemiPlaneSlicer::PutDetectorMapIndex > Failed Convert from [x][y] to (i,j) : index over");
        return ret;
    }
    if ( (_ConvMat[xind][yind].first<0)||(_ConvMat[xind][yind].second<0) ){
        UtsusemiError("UtsusemiPlaneSlicer::PutDetectorMapIndex > There is no container such (xind,yind)="+_st->UInt4ToString(xind)+","+_st->UInt4ToString(yind));
        return ret;
    }
    ret.push_back((UInt4)(_ConvMat[xind][yind].first));
    ret.push_back((UInt4)(_ConvMat[xind][yind].second));
    return ret;
}

//// Set Plane ////
void UtsusemiPlaneSlicer::
SetPlane(ElementContainerArray* plane)
{
    if (_sliced )
    {
        delete _plane;
    }
    _sliced = false;
    _valid = true;
    _plane = plane;
}

//////////////// Get Line //////////////////
ElementContainer UtsusemiPlaneSlicer::
GetLine( vector<Double> start, vector<Double> end, Double bin, Double width )
{
    
    ElementContainer ec0;
    
    if (!_valid)
    {
        UtsusemiError("UtsusemiPlaneSlicer::GetLine > There is no valid plane.");
        return ec0;        
    }
    // Get Xdata from plane's header
    HeaderBase* h_plane = _plane->PutHeaderPointer();
    vector<Double> xdat = h_plane->PutDoubleVector(0);

    // Make header
    HeaderBase hh_ec;
    hh_ec.Add("STARTPOINT", start);
    hh_ec.Add("ENDPOINT", end);
    ec0.InputHeader(hh_ec);

    // calc size of vector 
    Double xx = end[0] - start[0];
    Double yy = end[1] - start[1];
    Double vlength = sqrt(xx * xx+ yy * yy);
    //cout << "vlength : " <<  vlength << endl;
    // Make unit vector
    Double ux = xx / vlength;
    Double uy = yy / vlength;

    Double width2 = width / 2.0;
    
    // Make  bin
    vector<Double> x_vec;
    Double vbin = 0.0;
    while ( vbin < vlength)
    {
        x_vec.push_back(vbin);
        vbin += bin;
    }
    // add the last point
    x_vec.push_back(vbin);

    UInt4 numBin = x_vec.size();
    //cout << "numBin : " <<  numBin << endl;
    // prepare 
    vector<int> counter = vector<int>(numBin, 0);
    vector<Double> y_vec = vector<Double>(numBin, 0.0);
    vector<Double> e_vec = vector<Double>(numBin, 0.0);

    
    
    for (UInt4 i=0; i < _plane->PutSize(); i++){
        ElementContainer* ec = _plane->PutPointer(i);
        HeaderBase* h_ec = ec->PutHeaderPointer();
        // Masked ec?
        if ((h_ec->CheckKey(UTSUSEMI_KEY_HEAD_MASKED)==1)&&(h_ec->PutInt4(UTSUSEMI_KEY_HEAD_MASKED)==1)){
            continue;
        }
        vector<Double> err = ec->PutE();
        vector<Double> intY = ec->PutY();
        // xdata of ec means ydata for plane
        vector<Double> ydat = ec->PutX();

        UInt4 index;

        for (UInt4 j=0; j < err.size(); j++){
            // Masked point ?
            if ((err[j] < 0.0)||(intY[j]==UTSUSEMIMASKVALUE64)){
                continue;
            }
            // shift the origine
            Double xp = xdat[i] - start[0];
            Double yp = ydat[j] - start[1];
            // Calc distance from the line
            Double distance = fabs(xx * yp - yy * xp) / vlength ;
            

            // Check if in width?
            if (distance > width2){
                continue;
            }
            //cout << "distance : " << i << ":" << j << ":" << distance << endl;
            // Calc Dot 
            Double dot = xp  * ux + yp * uy;
            if (dot >= 0.0){
                index = (UInt4)((dot + bin/2.0) / bin);
                if ( index >= numBin){
                    continue;
                }
            }
            else if (fabs(dot) < bin/2.0){
                index = 0;
            }
            else{
                continue;
            }
            //cout << i << " : " << j <<":"<< intY[j] << endl;
            y_vec[index] += intY[j];
            counter[index] += 1;
            e_vec[index] += err[j]*err[j];
        }
    }
    for (UInt4 i = 0; i < numBin; i++){
        // Exist counter? 
        if ((counter[i] > 0)&&(y_vec[i]<UTSUSEMIMASKVALUE64)){
            y_vec[i] /= counter[i];
            e_vec[i] = sqrt(e_vec[i])/counter[i];
        }
    }

    ec0.Add(_line_Xkey,x_vec);
    ec0.Add(_line_Ykey,y_vec);
    ec0.Add(UTSUSEMI_KEY_ERROR,e_vec);
    ec0.SetKeys(_line_Xkey,_line_Ykey,UTSUSEMI_KEY_ERROR);

    return ec0;
}
ElementContainer UtsusemiPlaneSlicer::
GetLine( PyObject *start, PyObject *end, Double bin, Double width ){
    vector<Double> s_v = __gCppToPython.ListToDoubleVector( start );
    vector<Double> e_v = __gCppToPython.ListToDoubleVector( end );
    ElementContainer ret;
    if (s_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetLine > start cannot be converted." );
        return ret;
    }
    if (e_v.empty()){
        UtsusemiError( "UtsusemiPlaneSlicer::GetPlane > end cannot be converted." );
        return ret;
    }

    return GetLine( s_v, e_v, bin, width );
}
