#ifndef UTSUSEMI_PLANESLICER
#define UTSUSEMI_PLANESLICER

#include <math.h>
#include <omp.h>
#include <float.h>

#include "UtsusemiHeader.hh"
#include "HistogramBinToPoint.hh"
#include "DoubleBinArrange.hh"
#include "StringTools.hh"
#include "CppToPython.hh"

//////////////////////////////////
// UtsusemiPlaneSlicer
//////////////////////////////////

//! Slice a plane and get a line on the plane 
/*! 
 *  Slice a plane specified by 3 points( org, x, y) from a data.
 *  and get a line on the plane.
 *  The data must be ElementContainerArray or ElementContainerMatrix. 
 *  
 * ups = Utsusemi.UtsusemiPlaneSlicer( data ) <-  ElementContainerArray or ElementContainerMatrix.
 * ups.SetAxes("Qa","Qb","Qc") <- Qa,Qb,Qc must be existed as a key of vectors in EC or HeaderBase in EC,ECA,ECM
 * ups.SetPlaneKeys("SlicedX","SlicedY")
 * sliced_eca = Manyo.ElementContaineArray()
 * ups.GetPlane(sliced_eca,[0,0,0],[1,0,0],[0,1,0],0.1,0.05,[-1.0,10.0],[-0.5,5.0],0.2)
 *  
 *  
 */
class UtsusemiPlaneSlicer   
{
private:
    bool _valid;                    // Valid data flag
    bool _sliced;
    int  _datType;                  //-1: None,  0: Matrix, 1:Array
    ElementContainerMatrix* _ecm;
    ElementContainerArray* _eca;

    vector<string> _data_Keys;      // Axes Keys given by SetAxes
    vector< pair<Int4,Int4> > _data_Keys_Info;
                                    // <i,j> i=0: in vector, i=1: in ECA Header, i=2: in EC Header
                                    //       j=0: using index, j: value type in header (PutKeyLocation)
    bool _isAverage;                // Averaging or Summation on slicing
    UInt4 _xi;                      // index of key for X-Axis
    UInt4 _yi;                      // index of key for Y-Axis
    UInt4 _zi;                      // index of key for Z-Axis
    ElementContainerArray* _plane;  // Sliced Plane
    string _plane_Xkey;             // X key for plane(ElementContainerArray) 
    string _plane_Ykey;             // X key for plane(ElementContainerArray) 
    string _line_Xkey;              // X key for line(ElementContainer) 
    string _line_Ykey;              // X key for line(ElementContainer)

    Double _const_a, _const_b, _const_c, _const_d;   // constants of plane
    Double _thick2;                 // Harlf thickness
    vector< vector< pair<Int4,Int4> > > _ConvMat; // (x,y) on DetectMap -> (i,j) in _ECM

    StringTools* _st;
    
    void _Initialize();
    void _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);
    void _SlicePlaneFromArray(ElementContainerArray* eca, 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, UInt4 eca_ind=0);
    void _MakeBin(vector<Double>& bins, vector<Double> binRange, Double bin);
        //!< Make bin vector for X axis or Y axis
        /*!<
         */
public:
     UtsusemiPlaneSlicer();
        //!< Constructor
        /*!<
         */
    ~UtsusemiPlaneSlicer();
        //!< Destructor
        /*!<
         */
    UtsusemiPlaneSlicer( ElementContainerMatrix* ecm );
        //!< Constructor
        /*!<
         *   @param ecm   Target ElementContainerMatrix 
         */
    UtsusemiPlaneSlicer( ElementContainerArray* eca );
        //!< Constructor
        /*!<
         *   @param eca   Target ElementContainerArray 
         */
    bool SetAxes(string keyX, string keyY, string keyZ);
        //!< Specify keys for 3 axes
        /*!<
         *   @param keyX  key of X-axis
         *   @param keyY  key of Y-axis
         *   @param keyZ  key of Z-axis
         *   @returnValue true: sucsess or false: faile
         */
    bool IsValid(){return _valid;}
        //!< Return if valid data
        /*!<
         *   @param None
         *   @returnValue   true or false
         */
    void SetPlaneKeys(string xkey, string ykey){_plane_Xkey = xkey;_plane_Ykey = ykey;}
        //!< Set x and y keys for sliced plane 
        /*!<
         *   set string keys
         *   @param xkey  key of X data
         *   @param ykey  key of Y data
         */

    ElementContainerArray* GetPlane(vector<Double> org, vector<Double> x, vector<Double> y, Double xbin, Double ybin,
        vector<Double> xrange, vector<Double> yrange, Double thickness);
        //!< Get sliced plane ( Obsoleted )
        /*!<
         *   @param org  org point(x,y,z) of plane
         *   @param x    a point(x,y,z) on x axis of plane
         *   @param y    a point(x,y,z) on y axis of plane
         *   @param xbin bin value for x axis
         *   @param ybin bin value for y axis
         *   @param xrange (x0,x1)
         *   @param yrange (y0,y1)
         *   @param thickness thickness of plane
         *   @returnValue ElementContainerArray (Sliced plane)
         */
    bool GetPlane(ElementContainerArray* eca, vector<Double> org, vector<Double> x, vector<Double> y, Double xbin, Double ybin, vector<Double> xrange, vector<Double> yrange, Double thickness);
    bool GetPlane(ElementContainerArray* eca, PyObject *org, PyObject *x, PyObject *y, Double xbin, Double ybin, PyObject *xrange, PyObject *yrange, Double thickness);
        //!< Get sliced plane
        /*!<
         *   @param eca  ElementContainerArray to be filled by slice results
         *   @param org  org point(x,y,z) of plane
         *   @param x    a point(x,y,z) on x axis of plane
         *   @param y    a point(x,y,z) on y axis of plane
         *   @param xbin bin value for x axis
         *   @param ybin bin value for y axis
         *   @param xrange (x0,x1)
         *   @param yrange (y0,y1)
         *   @param thickness thickness of plane
         *   @retval true : succeeded
         *   @retval false: failed
         */
    bool GetPlaneAsDetectMap( ElementContainerArray* ret_eca, string keyX, string keyY, string keyZ, vector<Double> zrange );
    bool GetPlaneAsDetectMap( ElementContainerArray* ret_eca, string keyX, string keyY, string keyZ, PyObject *zrange );
        //!< Get sliced plane as DetectMap data
        /*!< This includes SetAxes(string keyX, string keyY, string keyZ).
         *
         *   @param ret_eca  ElementContainerArray to be filled by slice results
         *   @param keyX  key of X-axis (usually "DETID") detector direction
         *   @param keyY  key of Y-axis (usually "" )     pixel direction
         *   @param keyZ  key of Z-axis (usually vector key) "TOF" direction
         *   @param zrange (z0,z1)
         *   @retval true : succeeded
         *   @retval false: failed
         */
    vector<UInt4> PutDetectMapIndex( UInt4 xind, UInt4 yind );
        //!< Get index of original ElementContainer for sliced plane as DetectMap data
        /*!< 
         *   @param xind index of x for sliced plane
         *   @param yind index of y for sliced plane
         *   @returnValue vector<UInt4> : if failed to convert, size=0;
         */

    void SetLineKeys(string xkey, string ykey){_line_Xkey = xkey;_line_Ykey = ykey;}
        //!< Set x and y keys for line 
        /*!<
         *   set string keys
         *   @param xkey  key of X data
         *   @param ykey  key of Y data
         */
    ElementContainer GetLine(vector<Double> start, vector<Double> end, Double bin, Double width);
    ElementContainer GetLine(PyObject *start, PyObject *end, Double bin, Double width);
        //!< Clip and get a line data
        /*!<
         *   X axis will be TOF or Energy
         *   @param start   start point(x,y) of line
         *   @param end     end point(x,y) of line
         *   @param bin     bin value for x axis
         *   @param width   width of line
         *   @returnValue ElementContainer 
         */
    void SetPlane(ElementContainerArray* plane);
        //!< Set a plane 
        /*!<
         *   set plane data
         *   @param plane  a plane
         */
    void SetAverageMode(){ _isAverage=true; }
    void SetSummationMode(){ _isAverage=false; }
};
#endif
