#ifndef MLFARRAYSLICER
#define MLFARRAYSLICER

#include "MlfHeader.hh"
#include "OperatorBase.hh"

//////////////////////////////////
// MlfArraySlicer
//////////////////////////////////

//! Clip and integrate data
/*!
 *  Clip specified region from ElementContainerArray
 *  and integrate them to a ElementContainer,
 *  while exclude masked point.
 *
 *  cd = Manyo.MlfArraySlicer( tea ) <- ElementContainerArray
 *  ecm = TPC.Execute(0.05) <-- give Q step
 *
 */
class MlfArraySlicer:
    public OperatorBase< ElementContainerArray, ElementContainerArray >
{
private:
    bool _debugMode;
    bool _invalid;           // Given data is invalid flag
    Double _ei;              // Incident Energy. in case og TOF, this value is 0.0.
    int _numMask;            // Number of Masked regions
    std::string _runNo;           // Run No. in Header
    std::vector<std::string> _keys;    // preserve original keys
    std::vector<std::string> _units;   // preserve original units
    std::string _unit_X;          // Unit Xaxis if geven MLF_KEY_HEAD_XUNIT("Xunit")
    std::vector< std::pair<std::string,UInt4> > _givenAxesKeys;// keys given by SetAxes and index of type
                                                // 0: std::vector<Double> in EC,
                                                // 1: Int4 in EC Header, 2: Double in EC Header
                                                // 3: std::vector<Int4> (size of 2) in EC Header
                                                // 4: vecotr<Double>(size of 2) in EC Header
                                                // 5: std::vector<Int> (size same as ECs)in ECA Header
                                                // 6: std::vector<Double> (size same as ECs)in ECA Header
                                                // 7: std::vector<Int> (size same as ECs+1)in ECA Header
                                                // 8: std::vector<Double> (size same as ECs+1)in ECA Header


    std::vector< std::vector<Double> > _regions;    // Assembly of clip region, each region will create an ElementContainer

    // 2D Map data
    std::vector< std::vector<Double> > _anisoBin2d;    // X-axis points (TOF or Q values for Energy Data)
    std::vector< std::vector<Double> > _ints2d;        // Intencity of Neutron
    std::vector< std::vector<Double> > _errs2d;        // Errors
    std::vector<Double> _isoBin;                  // Y-axis points (Indices for TOF, or Energy), same for all Histogram
                                             //
    std::vector<Double> _Angles;                  // Polar Angles(deg) in header


    void _makeHeader(Double ,Double, bool, ElementContainer& , bool);    //
    void _initData(Double ei);     //
    void _MakeRectMap();            // Make 2D Map data for TOF
    void _MakePowderMap(Double ei);     // Make 2D Map data for Q data
    void _MakeRectYbinCutData(std::vector<Double> , bool , ElementContainer&);
    void _MakeRectXbinCutData(std::vector<Double> , bool , ElementContainer&);
    void _MakeRectDiagCutData(std::vector<Double>, Double, Double, bool, ElementContainer& );
                                                                        // Make a histogram of free cut on RectMap
    void _MakeQbinCutData(std::vector<Double>, bool, ElementContainer&, double);// Make a histogram which xbin is Q
    void _MakeEbinCutData(std::vector<Double>, bool, ElementContainer&);        // Make a histogram which xbin is Energy
    void _Rebin(UInt4, std::vector<Double>&, std::vector<Double>&,std::vector<Double>&);    // Rebinning of aniso Axis
    void _AddAngle(HeaderBase* hh, Int4 index0, Int4 index1);           // Set Polar Angle to Header
    double _EtoK2( double E );
    void _initialize();
    std::string _MessageTag;
    double _qBin;                 // Q bin width for _MakeQbinCutData
    std::pair<double,double> _qRange;  // Q range for _MakeQbinCutData
    bool _isInelaPowder;   // If given data is the inelastic powder with scan rocus

public:
    MlfArraySlicer();
        //!< Constructor
        /*!<
         */
    ~MlfArraySlicer();
        //!< Destructor
        /*!<
         */
    MlfArraySlicer( ElementContainerMatrix* ecm, std::string keyOfX="", std::string keyOfY="" );
        //!< Constructor
        /*!<
         *   @param eca   Target ElementContainerMatrix (TOF or Energy/Q data)
         *   @param keyOfX   key used as X axis (same as SetAxes)
         *   @param keyOfY   key used as Y axis (same as SetAxes)
         */
    MlfArraySlicer( ElementContainerArray* eca, std::string keyOfX="", std::string keyOfY="" );
        //!< Constructor
        /*!<
         *   @param eca   Target ElementContainerArray (TOF or Energy/Q data)
         *   @param keyOfX   key used as X axis (same as SetAxes)
         *   @param keyOfY   key used as Y axis (same as SetAxes)
         */
    bool SetAxes(std::string keyOfX="", std::string keyOfY="", bool doInitData=true);
        //!< Set Axes keys
        /*!< This sets keys to be used as X-axis values or Y-axis std::vector.
         *   keyX must be included in the Header of ECA or the Header of EC as Int4, Double,
         *   std::vector<Int4> or vectro<Double> which size is same as the number of ECs or plus 1.
         *   keyY must be one of the vectors in EC.
         *   If not set or invalid keys, X axis is made from  "XRANGE" std::vector in the Header of EC
         *   and Y axis from EC.PutX()
         *
         *   @param keyOfX      key used as X axis
         *   @param keyOfY      key used as Y axis
         *   @param doInitData  whether executing initData or not
         *   @retval true
         *   @retval false
         */
    void SetMask(Double x0, Double y0, Double x1, Double y1);
        //!< Add mask region
        /*!<
         *   @param x0   minimam Tof or Energy value
         *   @param y0   minimam pixel ID or Q value
         *   @param x1   maximam Tof or Energy value
         *   @param y1   maximam pixel ID or Q value
         */
    bool IsInvalid(){return _invalid;};
        //!< Return if valid data
        /*!<
         *   @param None
         *   @returnValue   true or false
         */
    void ClearMask(void);
        //!< Clear std::vector of Mask region
        /*!<
         *   Clear Mask region std::vector
         *   @param None
         */
    void SetClipRegion(Double x0, Double y0, Double x1, Double y1, bool checkY=true);
        //!< Add mask region
        /*!<
         *   @param x0   minimam Tof or Energy value
         *   @param y0   minimam pixel ID or Q value
         *   @param x1   maximam Tof or Energy value
         *   @param y1   maximam pixel ID or Q value
         *   @param checkY  true is to check y0,y1 are in sequence
         */
    void ClearClipRegion(void);
        //!< Clear std::vector of clip region
        /*!<
         *   Clear Mask region std::vector
         *   @param None
         */
    ElementContainerArray ExecInteg(bool average, int axis, Double width=0.0, Double bin=0.0);
    bool ExecInteg(ElementContainerArray* _eca, bool average, int axis, Double width=0.0, Double bin=0.0);
        //!< Integrate (or calcurate average) each region
        /*!<
         *   X axis will be TOF or Energy
         *   @param mode  Set if true: average or false:integration
         *   @param axis  Set axis 0: TOF or Energy 1:index  or Q 2:Free cutting
         *   @param width Set integ width on free cutting
         *   @param bin   Set bin of free cutting line
         */
    ElementContainerArray CutAlongX( bool average=true ){ return ExecInteg( average, 1 ); }
    ElementContainerArray CutAlongY( bool average=true ){ return ExecInteg( average, 0 ); }
    ElementContainerArray CutDiagonal( Double width, Double bin, bool average=true ){ return ExecInteg( average, 2, width, bin ); }
    ElementContainer CutAlongX( Double ymin, Double ymax, bool average=true );
    ElementContainer CutAlongY( Double xmin, Double xmax, bool average=true );
    ElementContainer CutDiagonal( Double x0, Double y0, Double x1, Double y1, Double width, Double bin, bool average=true );
    bool CutAlongX( ElementContainerArray* _eca, bool average=true ){ return ExecInteg( _eca, average, 1 ); }
    bool CutAlongY( ElementContainerArray* _eca, bool average=true ){ return ExecInteg( _eca, average, 0 ); }
    bool CutDiagonal( ElementContainerArray* _eca, Double width, Double bin, bool average=true ){ return ExecInteg( _eca, average, 2, width, bin ); }
    bool CutAlongX( ElementContainer* _ec, Double ymin, Double ymax, bool average=true );
    bool CutAlongY( ElementContainer* _ec, Double xmin, Double xmax, bool average=true );
    bool CutDiagonal( ElementContainer* _ec, Double x0, Double y0, Double x1, Double y1, Double width, Double bin, bool average=true );
    void SetQbin( Double bin, Double qmin=0.0, Double qmax=0.0 );
        //!< Set Q bin width and Q range to make Q binning cut
        /*!<
         *   SetQbin( 0.02 )          : bin=0.02, and Q range is depend on slicing area
         *   SetQbin( 0.02, -1, -1 )  : bin=0.02, and Q range uses maximun and minimum of given data
         *   SetQbin( 0.01, 0.0, 8.0 ): bin=0.01, and Q range is 0.0 to 8.0
         *
         *   @param bin   Set bin of Q cutting line
         *   @param qmin  Set minimum value of Q range
         *   @param qmax  Set maximum value of Q range
         */
    void SetHeaderToEC(ElementContainer& ec_res, Double y0, Double y1, bool average=true,  bool reverse=false){ _makeHeader(y0, y1, average, ec_res,reverse); }
};
#endif
