#ifndef UTSUSEMISQECALC
#define UTSUSEMISQECALC

#include "UtsusemiHeader.hh"
#include "CppToPython.hh"
#include "StringTools.hh"
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_blas.h>
#include "HistogramBinToPoint.hh"
#include "UtsusemiUnitConverter.hh"
#include "UtsusemiSqeCalcXtalParams.hh"

//////////////////////
//  UtsusemiSqeCalc
/////////////////////

//! Class for supporting functions to treat Q value
/*!
 *  This includes some methods to replace functions in VisualCont2 (VisualContCommon.py)
 *
 *
 */


class UtsusemiSqeCalc :
  public ManipulatorBase< ElementContainerMatrix >
{
private:
    void _Initialize();
        //!< Initializes some parameter
        /*!<
         *   @param None
         *   @return None
         */
    std::vector<gsl_vector*> _MakeEmpty3DMatrix();
    void _Delete3DMatrix( std::vector<gsl_vector*> *A );
    bool _Calc3DMatrixProduct( std::vector<gsl_vector*> *A, std::vector<gsl_vector*> *B, std::vector<gsl_vector*> *C );
    bool _Calc3DReversedMatrix( std::vector<gsl_vector*> *A, std::vector<gsl_vector*> *C );
    bool _CalcRotateUVWMatrix( UInt4 rot_axis, Double angle, std::vector<gsl_vector*> *A );
    void _CalcReciprocalLatticeMatrix( std::vector<Double> latticeConst, std::vector<gsl_vector*> *M );
    void _CalcUVWMatrix( std::vector<Double> Uvec, std::vector<Double> Vvec, std::vector<gsl_vector*> *M, std::vector<gsl_vector*> *N );
    void _CalcReciproLattice2( std::vector<Double> latticeConst, std::vector<Double> Uvec, std::vector<Double> Vvec, std::vector<Double> rotSteps, std::vector< gsl_vector* > *ret );

    void _CalcCross3d( gsl_vector* v1, gsl_vector* v2, gsl_vector* ret );
        //!< Calculates the cross between two vectors of gsl_vector.
        /*!< Before calling this, returned gsl_vector must be created.
         *   @param v1    first std::vector
         *   @param v2    second std::vector
         *   @param ret   result
         *   @return None
         */
    void _CalcReciproLattice( std::vector<Double> latticeConst, std::vector<Double> Uvec, std::vector<Double> Vvec, Double phi, std::vector< gsl_vector* > *ret );
        //!< Calculates reciprocal lattice from parameters
        /*!<
         *   @param latticeConst  lattice constants [a,b,c,alpha,beta,gamma]
         *   @param Uvec          U std::vector (parallel to incident beam)
         *   @param Vvec          V std::vector
         *   @param phi           angle around W axis
         *   @param ret           result
         *   @return None
         */

    void _Make4DVect( gsl_vector* vec, std::vector< gsl_vector*> latticeVec, gsl_vector* ret );
        //!< Makes 4 dimension std::vector on Q-E space from 3-D std::vector on Q-space
        /*!<
         *   @param vec           target std::vector on 3-D std::vector on Q-space
         *   @param latticeVec    Reciprocal std::vector infor (returned from _CalcReciproLattice)
         *   @param ret           result
         *   @return None
         */
    StringTools *stool;
    std::vector< std::vector<Double> > datArray, errArray; /**< Vectors of sliced data*/
    std::vector<Double> XArray, YArray;               /**< Vectors of XY axes for sliced data*/
    Double MASKVALUE;                            /**< value for masking*/
    bool isDEBUG;                                /**< flag for DEBUG mode*/
    std::string MessageTag;
    UtsusemiUnitConverter* UCC;
    bool isDirectGeometry;
    UInt4 _NumOfMulTh;
    UtsusemiSqeCalcXtalParams* _XP;
    bool isSetXtalParams;
    bool _ConvPyListToDoubleVect( PyObject *ax, std::vector<double> *ret, UInt4 s );

public:
    UtsusemiSqeCalc();
        //!< Constructor
        /*!<
         */
    UtsusemiSqeCalc( bool isDebug );
        //!< Constructor
        /*!<
         *   @param isDebug   when debug mode, set true
         */
    UtsusemiSqeCalc( std::string instType );
        //!< Constructor
        /*!<
         *   @param instType   Direct:direct geometry, Inverted:inverted geometry
         */
    ~UtsusemiSqeCalc();
        //!< Destructor
        /*!<
         */

    void SetInstGeometry( std::string instType="Direct" );
        //!< Sets the instrument geometry
        /*!<
         *   @param instType   Direct:direct geometry, Inverted:inverted geometry
         */

    std::vector<Double> CalcReciproLattice( std::vector<Double> latticeConst, std::vector<Double> Uvec, std::vector<Double> Vvec, Double phi);
        //!< Calculates reciprocal lattice from parameters
        /*!<
         *   @param latticeConst   Lattice Constants a,b,c,alpha,beta,gamma
         *   @param Uvec           U std::vector
         *   @param Vvec           V std::vector
         *   @param phi            angle around W axis
         *   @return Reciprocal Lattice std::vector
         */

    std::vector<Double> Make4DVect( std::vector<Double> vaxis, std::vector<Double> lc );
        //!< Makes 4 dimension std::vector on Q-E space from 3-D std::vector on Q-space
        /*!<
         *   @param vaxis         view axes
         *   @param lc            Reciprocal std::vector infor (returned from CalcReciproLattice)
         *   @return None
         */

    std::vector<Double> Projection( std::vector<Double> vaxis);
        //!< Calculates projction of ElementContainerMatrix
        /*!<
         *   @param vaxis          view axes
         *   @return Reciprocal Lattice std::vector
         */
    std::vector<Double> Projection_QxQyQz( std::vector<Double> vaxis);
        //!< Calculates projction of ElementContainerMatrix
        /*!<
         *   @param vaxis          view axes
         *   @return Reciprocal Lattice std::vector
         */
    Int4 Slice( std::vector<Double> Ax1range,std::vector<Double> Ax2range,std::vector<Double> Ax3range,std::vector<Double> Ax4range,
                std::vector<std::string> AxesType, std::vector<Double> Folding, bool isAverageMode=true );
        //!< Slice
        /*!<
         *   @param Ax1range    first view axis std::vector
         *   @param Ax2range    second view axis std::vector
         *   @param Ax3range    third view axis std::vector
         *   @param Ax4range    forth view axis std::vector
         *   @param AxesType    set how to treat each axis. can use only "X","Y" or "T".
         *   @param Folding     [0]to[3] are for along-axis-folding 0..off, 1..on, [4]to[6] are parameters for diagonal folding, [4] is type 0..no, 1..1st-3rd quadrant, 2..2nd-4th quadrant, [5]and[6] are axes-id to be used.
         *   @param isAverageMode
         *   @retval -1  Something wrong
         *   @retval  0  No trouble
         */
    std::vector<Double> PutXArray(){return XArray;}
        //!< Puts XArray (X-axis values) calculated at Slice method.
        /*!<
         *   @return  < XArray >
         */
    std::vector<Double> PutYArray(){return YArray;}
        //!< Puts YArray (Y-axis values) calculated at Slice method.
        /*!<
         *   @return  < YArray >
         */
    std::vector<Double> PutDArray();
        //!< Puts DArray (Intensity values) calculated at Slice method.
        /*!<
         *   @return  < DArray >
         */
    std::vector<Double> PutEArray();
        //!< Puts EArray (Error values) calculated at Slice method.
        /*!<
         *   @return  < EArray >
         */
    std::vector<Double> GetAxLimit();
        //!< returns Q1,Q2,Q3,hw ranges
        /*!<
         *   @return  < Q1 min, Q1 max, Q2 min, Q2 max, Q3 min, Q3 max, Q4 min, Q4 max >
         */
    PyObject* PutQRange(){ return __gCppToPython.VectorDoubleToList( GetAxLimit() );}
        //!< Puts axes ranges of D4Mat
        /*!<
         *   @param None
         *   @return python list object [ax1_min, ax1_max, ax2_min, ... , ax4_max]
         */
    Int4 ConvertToD4Mat(std::string filepath, std::vector< Double> ax1,std::vector< Double> ax2,
                        std::vector< Double> ax3,std::vector< Double> ax4, std::vector<Double> foldings, bool isAverageMode=true, bool useBinCorr=true);
        //!< Convert and save data using format which D4MatSlicer can load.
        /*!<
         *   @param filepath    Path/to/file
         *   @param ax1         first view axis information < min, max, step >
         *   @param ax2         second view axis information < min, max, step >
         *   @param ax3         third view axis information < min, max, step >
         *   @param ax4         forth view axis information < min, max, step >
         *   @param foldings    folding 0..off, 1..on
         *   @param isAverageMode  true: average calculation of intensity, false: summation
         *   @param useBinCorr true: do bin width correction
         *   @retval -1  Something wrong
         *   @retval  0  No trouble
         */
    Int4 ConvertToD4Mat(std::string filepath, bool isAverageMode=true, bool useBinCorr=true);
        //!< Convert and save data using format which D4MatSlicer can load.
        /*!<
         *   @param filepath    Path/to/file
         *   @param isAverageMode  true: average calculation of intensity, false: summation
         *   @param useBinCorr true: do bin width correction
         *   @retval -1  Something wrong
         *   @retval  0  No trouble
         */
    void Test();
    std::vector<Double> _LastConv3DMatrixElements;
    std::vector<Double> QConvFromReciprocalToRealSpace( std::vector<Double> Qvec, std::vector<Double> LC, std::vector<Double> Uv, std::vector<Double> Vv, std::vector<Double> RS, std::vector<Double> VA );
        //!< Convert list of Qx,Qy,Qz in Qvec to real space using sample information
        /*!<
         *   @param Qvec  list of Qx,Qy,Qz : Qx0,Qy0,Qz0,Qx1,Qy1,Qz1,Qx2,...,Qxn,Qyn,Qzn
         *   @param LC    Latice constants
         *   @param Uv    U std::vector
         *   @param Vv    V std::vector
         *   @param RS    Rotation info
         *   @param VA    View axes info
         *   @return list of x,y,z in real space, its size is same as Qvec
         */
    std::vector<Double> MakeProjectionMatrix( std::vector<Double> latticeConst, std::vector<Double> Uvec, std::vector<Double> Vvec, std::vector<Double> rotateSteps, std::vector<Double> viewAxes );
        //!< Makes the 4d Matrix for projection from (Qa,Qb,Qc,hw) -> (Vx,Vy,Vz,Vx)
        /*!<    Vx = Qz*A[0]  + Qx*A[1]  + Qy*A[2]  + hw*A[3];
         *      Vy = Qz*A[4]  + Qx*A[5]  + Qy*A[6]  + hw*A[7];
         *      Vz = Qz*A[8]  + Qx*A[9]  + Qy*A[10] + hw*A[11];
         *      Vw = Qz*A[12] + Qx*A[13] + Qy*A[14] + hw*A[15];

         *   @param laticeConst (std::vector<double>) a,b,c,alpha,beta,gamma
         *   @param Uvec (std::vector<double>) U std::vector
         *   @param Vvec (std::vector<double>) V std::vector
         *   @param rotateSteps (std::vector<double>) axis1, rot1[, axis2, rot2[, axis3, rot3] ]
         *                                       axisX is the number of rotation axis ("X":0,"Y":1,"Z":2) and the rotation value
         *   @param viewAxes (std::vector<Double>) View Axes
         *   @return std::vector<Double> Matrix A with size of 16
         */
    bool SetSlicedElementContainerArray( ElementContainerArray* _eca, std::string _bin_key="X", std::string _xval_key="XVAL", std::string _xrange_key=UTSUSEMI_KEY_HEAD_XRANGE );

    bool LoadXtalParam( std::string file );
    bool SaveXtalParam( std::string file );
    bool SetLatticeConstants( PyObject* LC );
    bool SetUVvector( PyObject* UV, PyObject* VV );
    bool SetRotationSteps( std::string steps );
    bool Projection( PyObject* arg_ax1, PyObject* arg_ax2, PyObject* arg_ax3, PyObject* arg_ax4 );
    bool Projection();
    bool Slice( PyObject* Ax1, PyObject* Ax2, PyObject* Ax3, PyObject* Ax4, PyObject* DiagFolding, bool isAverageMode=true );
    bool Slice(bool isAverageMode=true);
    static const Int4 ROTATE_AXIS_X;
    static const Int4 ROTATE_AXIS_Y;
    static const Int4 ROTATE_AXIS_Z;
    void SetDebugMode( bool _isDebugMode=true ){ isDEBUG=_isDebugMode; }
};
#endif
