#ifndef D4MATRIXFUNCTIONS
#define D4MATRIXFUNCTIONS

#include "Header.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"
#include "CppToPython.hh"
#include "StringTools.hh"
#include "BoostXmlParser.hh"
#include <iomanip>
#include "UtsusemiHeader.hh"
#include "UtsusemiSqeCalc.hh"
#include "UtsusemiUnitConverter.hh"
#include "UtsusemiNeunetEventDecoderBase.hh"
#include "UtsusemiGetNeunetHistogram.hh"

//////////////////////
//  D4Matrix
/////////////////////

//! Class for treating 4-dimensional data 
/*! 
 * This can import and conbine several data of single crystal sample
 * with dirrent conditions produced from VisualCont2.
 * 
 */

class D4Matrix

{
private:
    string MessageTag;
    Double MAX_SIZE_OF_ONE_BLOCK;
    bool isDebugMode;
    string d4mat_param_file;
    string d4mat_data_dir;
    StringTools *stools;
    bool isFilesOpened;
    bool _isGoodResult;

    void _Initialize();
        //!< Initializes for this class
        /*!< Sets initial value about MAX_SIZE_OF_ONE_BLOCK and some flags.
         *   @return None
         */
    Int4 _OpenFiles();
        //!< Opens allocated binary files
        /*!< Make file pointers for files described in parameter file
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    Int4 _CalcIndex( Double a1, Double a2, Double a3, Double a4, UInt4 *target, UInt8 *ind );
        //!< Calculates index(pointer) in binary files.
        /*!< 
         *   @param a1      range of a1-axis
         *   @param a2      range of a2-axis
         *   @param a3      range of a3-axis
         *   @param a4      range of a4-axis
         *   @param target  index of list for FILE I/O pointer including given a4 value
         *   @param ind     index of pointer on binary file calculated by a1, a2, a3 and a4.
         *   @return None
         */

    vector< vector<Double> > datArray, errArray; /**< Vectors of sliced data*/
    vector<Double> XArray, YArray;               /**< Vectors of XY axes for sliced data*/
    Int4 _AddToMatFromText( string filename, bool isAdd );
        //!< Adds or subtract TEXT data to D4Matrix files
        /*!< Reads TEXT data produced by VisualCont2 and add/subtract these values to/from D4Matrix files 
         *   @param paramfilepath   full path to parameter
         *   @param isAdd           true: Add to, false: Sbutract from
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    Int4 _AddToMatFromBin( string filename, bool isAdd );
        //!< Adds or subtract BINARY data to D4Matrix files
        /*!< Reads binary data converted from TEXT data of VisualCont2 
         *   and add/subtract these values to/from D4Matrix files 
         *   @param paramfilepath   full path to parameter
         *   @param isAdd           true: Add to, false: Sbutract from
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    Int4 _ReadParamXml( string datapath, string paramfilepath );
        //!< Reads parameters from XML file
        /*!< 
         *   @param paramfilepath The path to XML file of parameters
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    void _ClearAllParams();
    bool _CalcRangeInfo( UInt4 dim, vector<Double> &a1r, vector<Double> &a2r, vector<Double> &a3r, vector<Double> &a4,
                         vector<string> &def_axes, vector<UInt4> &def_ind, vector< vector<Double> > &arlist, vector<UInt4> &numlist );

    // For VirtualD4Matrix
    bool _isVirtual;
    vector< vector<float>* >* _VirtualD4Mat;
    vector< pair<double,double>* > _VirtualAngleInfo;
    void _ClearVirtualMat();
    Int4 _CalcIndexVirtual( Double a1, Double a2, Double a3, UInt8 *ind );
    
public:
    D4Matrix();
        //!< Constructor
        /*!<
         */
    D4Matrix( bool isDebugMode );
        //!< Constructor
        /*!<
         *   @param isDebugMode  shows some comments and parameters on executing.
         */
    ~D4Matrix();
        //!< Destructor
        /*!<
         */
    void SetMaxSizeOfBlock( Double _size );
        //!< Sets maximum file size for one block of allocated D4Matrix binary file
        /*!< All file size can not be exceeded this size
         *   @param _size (Double) maximum file size
         *   @return None
         */
    void AllocateNewMat( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range,
                         vector<string> titles, string data_dir, string paramfile );
    bool AllocateNewMat( PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range,
                         PyObject* titles, string data_dir, string paramfile );
        //!< Makes new D4Matrix files on disk
        /*!< Each range consists of three values, start, end and width.
         *   @param a1range         first axis range
         *   @param a2range         second axis range
         *   @param a3range         third axis range
         *   @param a4range         fourth axis range
         *   @param titles          titles for axes
         *   @param data_dir        path to data dir (including xml file)
         *   @param paramfile       file name of parameter xml file
         *   @retval False Failed.
         *   @retval True  Succeeded.
         */
    bool OpenMat( string datapath, string paramfile );
        //!< Open D4Matrix files existing on disk
        /*!< Read parameter file and open file pointers given by parameters.
         *   @param datapath        path to data dir
         *   @param paramfile       file name of parameter xml file
         *   @retval False Failed.
         *   @retval True  Succeeded.
         */
    void CloseMat();
        //!< Closes opened D4Matrix files 
        /*!< Close all file pointers opened by _OpenDat method.
         * 
         *   @return None
         */
    bool isMatOpened(){ return isFilesOpened; }
        //!< Returns whether D4Matrix files are opened or not
        /*!< 
         *   @retval true   D4Matrix files are opened.
         *   @retval false  Not opened.
         */
    bool isStatusOK(){ return _isGoodResult; }
        //!< Returns status of results to be executed
        /*!< 
         *   @retval true   OK.
         *   @retval false  Bad.
         */
    void AddToMatFromText( string filename );
        //!< Adds TEXT data to D4Matrix files
        /*!< Read TEXT data produced by VisualCont2 and add these values to D4Matrix files 
         *   @param  filename   full path to parameter
         *   @return None
         */
    Int4 AddToMatFromPyList( string title, PyObject *ax1, PyObject *ax2, PyObject *ax3, PyObject *ax4, PyObject *ii, PyObject *ee );
        //!< Adds python List object data to D4Matrix files
        /*!< Adds Sliced data produced by VisualCont2 to D4Matrix files 
         *   @param title      string which indicates this data
         *   @param ax1        Python list object for AX1
         *   @param ax2        Python list object for AX2
         *   @param ax3        Python list object for AX3
         *   @param ax4        Python list object for AX4
         *   @param ii         Python list object for Intensity
         *   @param ee         Python list object for Error
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    void SubtractFromMatByText( string filename );
        //!< Subtracts TEXT data from D4Matrix files
        /*!< Read TEXT data produced by VisualCont2 and subtract these values from D4Matrix files 
         *   @param filename   full path to parameter
         *   @return None
         */
    bool AddToMatFromBin( string filename );
        //!< Adds BINARY data to D4Matrix files
        /*!< Read binary data converted from TEXT data of VisualCont2 and add these values to D4Matrix files 
         *   @param filename   full path to parameter
         */
    bool AddToMatFromBinFolder( string folderpath, string _ext=".vbin" );
    void SubtractFromMatByBin( string filename );
        //!< Subtracts BINARY data from D4Matrix files
        /*!< Read binary data converted from TEXT data of VisualCont2 and subtract these values from D4Matrix files 
         *   @param filename   full path to parameter
         *   @return None
         */
    void SliceMat( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Double> foldings );
    bool SliceMat( PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range, PyObject* def_axes, PyObject* foldings );
    void SliceMatOld( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Int4> foldings );
        //!< Slices D4Matrix by parameters
        /*!< Picks up the region given by parameters from D4Matrix and make vector data for visualization.
         *   Users can set 4 axes ranges and the role of each axis by using string "X", "Y" and "T".
         *   "X" and "Y" mean X and Y axis on visualization. Two axes indicated by "T" are summarized.
         *   Each range must consits of two values, begin and end;
         *   @param a1range (list of float)  first axis range  [min, max]
         *   @param a2range (list of float)  second axis range [min, max]
         *   @param a3range (list of float)  third axis range  [min, max]
         *   @param a4range (list of float)  fourth axis range [min, max]
         *   @param def_axes (list of str)   role of each axis, for example [ "X", "Y", "T", "T" ]
         *   @param foldings (list of float) folding values, [] means [ -1, -1, -1, -1]
         *   @retval False Failed.
         *   @retval True  Succeeded.
         */
    vector<Double> PutSliceResults( UInt4 type, UInt4 Xindex );
        //!< Put Sliced data as vector.
        /*!< Put vector made by SliceMat. This method puts a vector with X index.'type' indicates the type of data.
         *   @param type    indicates data type 0:Intensity, 1:Error, 2:X array, 3:Y array
         *   @param Xindex   indicates index of vectors of Intensity and Error.
         *   @return string vector
         */
    vector<Double> PickUpInten( Double a1, Double a2, Double a3, Double a4 );
        //!< Picks up data at a point on D4Matrix.
        /*!< Picks up data at a point indicated by given parameters and show its intensity and error.
         *   @param a1    A1 axis
         *   @param a2    A2 axis
         *   @param a3    A3 axis
         *   @param a4    A4 axis
         *   @return vector of two values for intensity and error.
         */
    void ReplaceInten( Double a1, Double a2, Double a3, Double a4, Double Inten, Double Error, Double Counts );
        //!< Replaces data at a point on D4Matrix.
        /*!< Replaces data at a point indicated by given parameters with given Intensity, Error and Counts.
         *   @param a1    A1 axis
         *   @param a2    A2 axis
         *   @param a3    A3 axis
         *   @param a4    A4 axis
         *   @param Inten   Value for Intensity to be replaced
         *   @param Error   Value for Error to be replaced
         *   @param Counts  Value for the number of summarized data
         *   @return None
         */
    ElementContainerArray PutSlicedECA();
        //!< Puts Sliced data as ElementContainerArray
        /*!< Sliced data are stored in list of vectors. 
         *   This method constructs ElementContainerArray from these vectors.
         *   ElementContainerArray can be sent to M2Plot directly.
         *   @return ElementContainerArray
         */
    vector<string> PutFileComponents(){ return file_components; }
        //!< Puts list for file component of D4Matrix
        /*!< 
         *   @return file_components
         */
    vector<Double> PutAxRange( UInt4 i_ax );
        //!< Puts axis range 
        /*!< Puts axis range in range_list by given index i_ax as vector
         *   @param i_ax index of axis
         *   @return range_list[ i_ax ]
         */
    string PutAxTitle( UInt4 i_ax );
        //!< Puts axis title
        /*!< 
         *   @param i_ax   index of axis title
         *   @return  ax_titles[ i_ax ]
         */
    void SetAxTitle( UInt4 i_ax, string title );
        //!< Sets axis title
        /*!< 
         *   @param i_ax   index of axis title
         *   @param title  axis title
         *   @return None
         */
    void SetAxTitles( vector<string> titles, bool isSaved );
    void SetAxTitles( PyObject* titles, bool isSaved );
        //!< Sets axis titles and save parames xml file
        /*!< 
         *   @param titles  string vector or list for axis titles
         *   @param isSaved flag for save file after set of titles
         *   @return None
         */
    void SavePyListToBin( string filepath, PyObject *ax1, PyObject *ax2, PyObject *ax3, PyObject *ax4, PyObject *ii, PyObject *ee );
        //!< Saves List object on python to binary file
        /*!< Saves given list object on python to binary file as the format to be loaded by AddToMatFromBin.
         *   @param filepath   file path of save binary file
         *   @param ax1        Python list object for AX1
         *   @param ax2        Python list object for AX2
         *   @param ax3        Python list object for AX3
         *   @param ax4        Python list object for AX4
         *   @param ii         Python list object for Intensity
         *   @param ee        Python list object for Error   
         *   @return None
         */
    void AddMatToMat( string dir_from, string file_from, string dir_to, string file_to );
        //!< Adds data of saved Matrix files to other Matrix data files
        /*!< This is mainly used for rebinning of matrix
         *   @param dir_from      directory path to original data 
         *   @param file_from     file name to original data 
         *   @param dir_to        directory path to new data
         *   @param file_to       file name to new data
         *   @return None
         */
    vector<string> PutOpenedDataPath();
        //!< Puts opened data path
        /*!< This pushes d4mat_data_dir and d4mat_param_file
         *   
         *   @return string vector including d4mat_data_dir and d4mat_param_file
         */
    
    Int4 GetPartOfD4Mat( UInt4 fs_index, float data[], UInt4 top_posi, UInt4 get_size);
    Int4 SaveParamXml( string datapath, string paramfilepath );
        //!< Saves parameters to XML file
        /*!< 
         *   @param paramfilepath The path to XML file of parameters
         *   @retval 0              No Error
         *   @retval -1             Error
         */
    Int4 SliceMat3D( ElementContainerMatrix* _ecm, vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Double> foldings, vector<string> key_axes );
    bool SliceMat3D( ElementContainerMatrix* _ecm, PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range, PyObject* def_axes, PyObject* foldings, PyObject* key_axes );
        //!< Slices D4Matrix for 3D
        /*!< Picks up the region given by parameters from D4Matrix and make vector data for visualization.
         *   Users can set 4 axes ranges and the role of each axis by using string "X", "Y", "Z" and "T".
         *   "X","Y" and "Z" mean X, Y and Z axis on visualization. The other axis indicated by "T" are summarized.
         *   Each range must consits of two values, begin and end;
         *   SliceMat3D( dat, a1range, a2range, a3range, a4range, def_axes, foldings, key_axes );
         *   @param data
         *   @param a1range (list of float)  first axis range  [min, max]
         *   @param a2range (list of float)  second axis range [min, max]
         *   @param a3range (list of float)  third axis range  [min, max]
         *   @param a4range (list of float)  fourth axis range [min, max]
         *   @param def_axes (list of str)   role of each axis, for example [ "X", "Y", "T", "T" ]
         *   @param foldings (list of float) folding values, [] means [ -1, -1, -1, -1]
         *   @param key_axes (list of str)   Key name for each range [ <KeyX>,<KeyY>,<KeyZ> ]
         *   @retval False Failed.
         *   @retval True  Succeeded.
         */
    Int4 OutputText3D( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Double> foldings, string filename, bool isIgnoreMaskVal=false, string maskValStr=""  );
    bool OutputText3D( PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range, PyObject* def_axes, PyObject* foldings, string filename, bool isIgnoreMaskVal=false, string maskValStr=""  );
        //!< Slices as 3D axes data of D4Matrix to save as text file
        /*!< 
         *   OutputText3D( a1range, a2range, a3range, a4range, def_axes, foldings, filename, isIgnoreMaskVal, maskValStr );
         *   @param a1range (list of float)  first axis range  [min, max]
         *   @param a2range (list of float)  second axis range [min, max]
         *   @param a3range (list of float)  third axis range  [min, max]
         *   @param a4range (list of float)  fourth axis range [min, max]
         *   @param def_axes (list of str)   role of each axis, for example [ "X", "Y", "T", "T" ]
         *   @param foldings (list of float) folding values, [] means [ -1, -1, -1, -1]
         *   @param isIgnoreMaskVal (bool)   ignore mask values ( does not save mask value ) or not
         *   @param maskValStr (str)         if isIgnoreMaskVal==False, this string is used as the mask value in text file.
         *   @retval True  Succeeded.
         *   @retval False Failed.
         */
    Int4 DumpD4MatToFile( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Double> foldings, string filename, bool isText=true );
    bool DumpD4MatToFile( PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range, PyObject* def_axes, PyObject* foldings, string filename, bool isText=true );
        //!< Saves D4Matrix to text or binary file
        /*!< 
         *   DumpD4MatToFile( a1range, a2range, a3range, a4range, def_axes, foldings, filename, isText );
         *   @param a1range (list of float)  first axis range  [min, max]
         *   @param a2range (list of float)  second axis range [min, max]
         *   @param a3range (list of float)  third axis range  [min, max]
         *   @param a4range (list of float)  fourth axis range [min, max]
         *   @param def_axes (list of str)   role of each axis, for example [ "X", "Y", "T", "T" ]
         *   @param foldings (list of float) folding values, [] means [ -1, -1, -1, -1]
         *   @param isText (bool) If True, save data as text file. 
         *   @retval True  Succeeded.
         *   @retval False Failed.
         */
    Int4 OutputText3DOld( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Int4> foldings, string filename, bool isIgnoreMaskVal=false, string maskValStr=""  );
    Int4 DumpD4MatToFileOld( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Int4> foldings, string filename, bool isText=true );
    vector<FILE*> fs_list;
    vector< vector<Double> > range_list;
    vector<UInt4> NumBin;
    vector<string> name_of_blocks;
    vector<UInt4> index_of_blocks;
    vector<string> file_components;
    vector<UInt4> size_of_Ax;
    vector<string> ax_titles;
    vector<Double> _hwInfoForVirtualMatrix;
    virtual void _CalcVirtualAngleInfo( string wfile, string dfile );
    virtual bool SetRunNoForVirtualMatrix( UInt4 runNo, PyObject* hwInfo );
    void AllocateVirtualMat( vector<Double> latticeConst, vector<Double> Uvec, vector<Double> Vvec, vector<Double> rotateSteps, 
                             vector<Double> viewAxes, vector<Double> hwInfo, vector<Double> phiSteps,
                             vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> titles );
    bool AllocateVirtualMat( PyObject* latticeConst, PyObject* Uvec, PyObject* Vvec, PyObject* rotateSteps,
                             PyObject* viewAxes, PyObject* phiSteps,
                             PyObject* a1range, PyObject* a2range, PyObject* a3range, PyObject* a4range, PyObject* titles );
        //!< Makes Virtual Matrix in memory
        /*!< 
         *   AllocateVirtualMat( latticeConst, Uvec, Vvec, rotateSteps, viewAxes, phiSteps , a1range, a2range, a3range, a4range, titles )
         *   @param latticeConst (list of float) Lattice Constant [a,b,c,alpha,beta,gamma]
         *   @param Uvec         (list of float) U-vector
         *   @param Vvec         (list of float) V-vector
         *   @param rotateSteps  (list of float) Steps for rotation [ 1, -5 ] means -5 degree rotation aroung Y axis
         *   @param viewAxes     (list of float) List of matrix for axes projection 
         *   @param phiStep      (list of float) List of angles for sample orientations 
         *   @param a1range (list of float)  first axis range  [min, max]
         *   @param a2range (list of float)  second axis range [min, max]
         *   @param a3range (list of float)  third axis range  [min, max]
         *   @param a4range (list of float)  fourth axis range [min, max]
         *   @param titles  (list of str)    Titles
         *   @retval True succeeded.
         *   @retval False failed.
         */
    vector<Double> EstimateRangeOfVirtualMat( vector<Double> latticeConst, vector<Double> Uvec, vector<Double> Vvec, vector<Double> rotateSteps, 
                                             vector<Double> viewAxes, vector<Double> hwInfo, vector<Double> phiSteps );
    PyObject* EstimateRangeOfVirtualMat( PyObject* latticeConst, PyObject* Uvec, PyObject* Vvec, PyObject* rotateSteps,
                                             PyObject* viewAxes, PyObject* phiSteps );
        //!< Estimates range of Virtual Matrix 
        /*!< 
         *   EstimateRangeOfVirtualMat( latticeConst, Uvec, Vvec, rotateSteps, viewAxes, phiSteps )
         *   @param latticeConst (list of float) Lattice Constant [a,b,c,alpha,beta,gamma]
         *   @param Uvec         (list of float) U-vector
         *   @param Vvec         (list of float) V-vector
         *   @param rotateSteps  (list of float) Steps for rotation [ 1, -5 ] means -5 degree rotation aroung Y axis
         *   @param viewAxes     (list of float) List of matrix for axes projection 
         *   @param phiStep      (list of float) List of angles for sample orientations 
         *   @return ranges for all axes (list of float)
         */
    void SliceVirtualMat( vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range, vector<string> def_axes, vector<Double> foldings );
    bool isVirtualMode(){ return _isVirtual; }
    static const Int4 ROTATE_AXIS_X;
    static const Int4 ROTATE_AXIS_Y;
    static const Int4 ROTATE_AXIS_Z;
};

#endif
