#ifndef UTSUSEMID4MATONMEM
#define UTSUSEMID4MATONMEM

#include "Header.hh"
#include "HeaderBase.hh"
#include "ElementContainer.hh"
#include "ElementContainerArray.hh"
#include "ElementContainerMatrix.hh"
#include "UtsusemiSqeCalc.hh"
#include "UtsusemiCalcContainers.hh"
#include "WriteSerializationFile.hh"
#include "ReadSerializationFile.hh"
#include "StringTools.hh"
#include "UtsusemiHeader.hh"
#include "BoostXmlParser.hh"
#include "UtsusemiEventDataConverterNeunet.hh"
#include "UtsusemiEventDataMonitorNeunet.hh"
#include "PhiCalculation.hh"
#include "KiKfCorrection.hh"
#include "UtsusemiSetMask.hh"
#include "UtsusemiUnitConverter.hh"
#include "CppToPython.hh"
#include <omp.h>

//////////////////////////////////
// UtsusemiD4matOnMem
/////////////////////////////////

//! D4Mat on memory
/*! 
 *
 */

class UtsusemiD4MatOnMem
{
private:
    
    void _Initialize();
    void _Clear();
    string MessageTag;
    Double MASKVALUE;
    ElementContainerArray* _D4mat;
    HeaderBase* _HH;
    UInt4 _NumOfMulTh;
    StringTools * _stools;
    
public:
    UtsusemiD4MatOnMem();
        //!< Constructor
        /*!< 
         *   @return None
         */
    ~UtsusemiD4MatOnMem();
        //!< Destructor
        /*!< 
         *   @return None
         */
    Int4 SetCrystalParameters( ElementContainerMatrix* ecm, vector<Double> LatticeConsts, vector<Double> Uvec, vector<Double> Vvec, vector<Double> Rvec, Double phi );
    bool SetCrystalParameters( ElementContainerMatrix* ecm, PyObject* LatticeConsts, PyObject* Uvec, PyObject* Vvec, PyObject* Rvec, Double phi );
        //!< Sets crystal parameters
        /*!< This sets crystal parameters to ElementContainerMatrix and _D4mat header.
         *   @param ecm (ElementContainerMatrix*) target ElementContainerMatrix
         *   @param LatticeConsts (vector<Double>) Lattice constants <a, b, c, alpha, beta, gamma>
         *   @param Uvec (vector<Double>) U vector (ki parallel vector in coordinate system inside sample)
         *   @param Vvec (vector<Double>) V vector (ki perpendicular in coordinate system inside sample)
         *   @param Rvec (vector<Double>) steps of rotate angle 
         *   @param phi (Double) rotate angle around Y-axis
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    Int4 SetCrystalParametersToHeader( HeaderBase* head,vector<Double> LatticeConsts, vector<Double> Uvec, vector<Double> Vvec, vector<Double> Rvec );
        //!< Sets crystal parameters
        /*!< This sets crystal parameters to HeaderBase.
         *   @param head (HeaderBase*) target HeaderBase
         *   @param LatticeConsts (vector<Double>) Lattice constants <a, b, c, alpha, beta, gamma>
         *   @param Uvec (vector<Double>) U vector (ki parallel vector in coordinate system inside sample)
         *   @param Vvec (vector<Double>) V vector (ki perpendicular in coordinate system inside sample)
         *   @param Rvec (vector<Double>) Rotation Steps
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    Int4 SetD4MatParameters( Int4 runNo, vector<Double> dataRedParam,vector<Double> LatticeConsts, vector<Double> Uvec, vector<Double> Vvec, vector<Double> Rvec);
    bool SetD4MatParameters( Int4 runNo, PyObject* dataRedParam, PyObject* LatticeConsts, PyObject* Uvec, PyObject* Vvec, PyObject* Rvec);
        //!< Sets D4Mat2 parameters into Header
        /*!< This sets D4Mat2 parameters to HeaderBase.
         *   @param runNo (Int4) runNo
         *   @param dataRedParam (vector<Double>) time_slice_start, time_slice_end, Ei, dHW, hw_min, hw_max, start_deg, end_deg, step_deg
         *   @param LatticeConsts (vector<Double>) Lattice constants <a, b, c, alpha, beta, gamma>
         *   @param Uvec (vector<Double>) U vector (ki parallel vector in coordinate system inside sample)
         *   @param Vvec (vector<Double>) V vector (ki perpendicular in coordinate system inside sample)
         *   @param Rvec (vector<Double>) Rotation Steps
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    Int4 PutD4MatRunNo();
        //!< Puts RunNo from D4Mat2 parameters in Header
        /*!< This return run number from D4Mat2 parameters in HeaderBase.
         *   @param None
         *   @return runNo (Int4)
         */
    vector<Double> PutD4MatDataRedParams();
        //!< Puts Data Reduction Parameters from Header
        /*!< This return data reduction parameters of D4Mat2 from HeaderBase.
         *   @param None
         *   @return vector<Double> 
         */
    vector<Double> PutD4MatLatticeConsts();
        //!< Puts Lattice Constants parameters from Header
        /*!< This return lattice constants parameters of D4Mat2 from HeaderBase.
         *   @param None
         *   @return vector<Double>
         */
    vector<Double> PutD4MatUVvects();
        //!< Puts U-vector and V-vector parameters from Header
        /*!< This return U and V vectors of D4Mat2 from HeaderBase.
         *   @param None
         *   @return vector<Double>
         */
    Int4 ImportEcm( vector<Double> viewAxis, Double arg1, ElementContainerMatrix* ecm1, string ope,
                    Double arg2, ElementContainerMatrix* ecm2, string label );
    Int4 ImportEcm( vector<Double> viewAxis, ElementContainerMatrix* ecm, string label );
        //!< Imports ElementContainerMatrix into D4mat
        /*!< This loads a ElementContainerMatrix into D4mat as ElementContainer(s with given calc operation).
         *   ImportEcm( vector<Double> viewAxis, Double arg1, ElementContainerMatrix* ecm1, string ope,
         *              Double arg2, ElementContainerMatrix* ecm2, string label );
         *   ImportEcm( vector<Double> viewAxis, ElementContainerMatrix* ecm, string label );
         *
         *   @param viewAxis (vector<Double>) view axes
         *   @param arg1 (Double) argument multiplied to ecm1
         *   @param ecm1 (ElementContainerMatrix*) target ElementContainerMatrix
         *   @param ope (string) operator between ecm1 and ecm2
         *   @param arg2 (Double) argument multiplied to ecm1
         *   @param ecm2 (ElementContainerMatrix*) target ElementContainerMatrix
         *   @param label (string) label of target ElementContainerMatrix
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    Int4 ImportEcms( UtsusemiEventDataConverterNeunet* EDC,
		     const vector<UInt4> CaseId, const vector<Double> Phi,
		     const vector<Double> LC, const vector<Double> UV, const vector<Double> VV,
		     const vector<Double> VA, const vector<Double> RV, const string maskfile, const Double dOmega );
    bool ImportEcms( UtsusemiEventDataConverterNeunet* EDC,
		     PyObject* CaseId, PyObject* Phi,
		     PyObject* LC, PyObject* UV, PyObject* VV,
		     PyObject* VA, PyObject* RV, const string maskfile, const Double dOmega );
    Int4 ImportEcmsPsuedOnLine( UtsusemiEventDataMonitorNeunet* EDC,
                                const vector<UInt4> CaseId, const vector<Double> Phi,
                                const vector<Double> LC, const vector<Double> UV, const vector<Double> VV,
                                const vector<Double> VA, const vector<Double> RV, const string maskfile, const Double dOmega, const bool isRefresh=false );
    void AllocateD4MatPseudOnLine( UInt4 num_of_cases );
    Int4 RemoveEcm( UInt4 runNo );
    Int4 RemoveEcm( string label );
        //!< Removes ElementContainer 
        /*!< This removes one ElementContainer by given runNo or label
         *   RemoveEcm( UInt4 runNo );
         *   RemoveEcm( string label );
         *   @param head (HeaderBase*) target HeaderBase
         *   @param LatticeConsts (vector<Double>) Lattice constants <a, b, c, alpha, beta, gamma>
         *   @param Uvec (vector<Double>) U vector (ki parallel vector in coordinate system inside sample)
         *   @param Vvec (vector<Double>) V vector (ki perpendicular in coordinate system inside sample)
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    void ClearAll();
        //!< Clears D4Mat
        /*!< This resets D4Mat
         *   @param None
         *   @return None
         */
    ElementContainerArray Slice2d( vector<Double> ax1range, vector<Double> ax2range, vector<Double> ax3range, vector<Double> ax4range, vector<string> type, vector<Double> folding );
    ElementContainerArray Slice2d( PyObject* ax1range, PyObject* ax2range, PyObject* ax3range, PyObject* ax4range, PyObject* type, PyObject* folding );
        //!< Slices D4Mat by given parameters
        /*!< This slices D4Mat by given parametes and makes ElementContainerArray as result
         *
         *   @param ax1range (vector<Double>) range for axis 1
         *   @param ax2range (vector<Double>) range for axis 2
         *   @param ax3range (vector<Double>) range for axis 3
         *   @param ax4range (vector<Double>) range for axis 4
         *   @param type     (vector<string>) types for each axis "X","Y","T"
         *   @param folding  (vector<Double>) folding information
         *   @return ElementContainerArray    results of sliced data
         */
    ElementContainerMatrix Slice3d( vector<Double> ax1range, vector<Double> ax2range, vector<Double> ax3range, vector<Double> ax4range, vector<string> type, vector<Double> folding );
    ElementContainerMatrix Slice3d( PyObject* ax1range, PyObject* ax2range, PyObject* ax3range, PyObject* ax4range, PyObject* axistype, PyObject* folding );
        //!< Slices D4Mat by given parameters
        /*!< This slices D4Mat by given parametes and makes ElementContainerArray as result
         *
         *   @param ax1range (vector<Double>) range for axis 1
         *   @param ax2range (vector<Double>) range for axis 2
         *   @param ax3range (vector<Double>) range for axis 3
         *   @param ax4range (vector<Double>) range for axis 4
         *   @param type     (vector<string>) types for each axis "X","Y","Z","T"
         *   @param folding  (vector<Double>) folding information
         *   @return ElementContainerMatrix    results of sliced data
         */
    Int4 WriteData( string filename );
        //!< Saves D4Mat data using Boost Serialization
        /*!< 
         *   @param filename (string)
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    Int4 ReadData( string filename );
        //!< Loads D4Mat data using Boost Serialization
        /*!< 
         *   @param filename (string)
         *   @retval 0   no trouble
         *   @retval -1  trouble happened.
         */
    ElementContainerArray* PutD4matPointer(){ return _D4mat; }
        //!< Puts D4Mat data pointer
        /*!< 
         *   @param None
         *   @return ElementContainerArray*)
         */
    void ResetD4mat();
        //!< Clear D4Mat data
        /*!< 
         *   @param None
         *   @return None
         */
    vector<Double> GetAxLimit();
        //!< Puts axes ranges of D4Mat
        /*!< 
         *   @param None
         *   @return vector<Double>
         */
    void AllocateD4MatOnDisk(vector<Double> a1range, vector<Double> a2range, vector<Double> a3range, vector<Double> a4range,
                             vector<string> titles, string data_dir, string paramfile );
    bool AllocateD4MatOnDisk(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
         *   @return None
         */
    Int4 SaveParamXmlD4MatOnDisk( string d4mat_datadir, string d4mat_param_file, 
                                  vector<string> name_of_blocks, vector<UInt4> index_of_blocks,
                                  vector< vector<Double> > ranges, vector<string> axtitles,
                                  vector<string> pathbinfiles );
    Int4 SaveParamXmlD4MatOnDisk( string d4mat_datadir, string d4mat_param_file,
                                  vector<string> name_of_blocks, vector<UInt4> index_of_blocks,
                                  vector<Double> ax1range, vector<Double> ax2range,
                                  vector<Double> ax3range, vector<Double> ax4range, vector<string> axtitles,
                                  vector<string> pathbinfiles );
    vector<string> _vbinFileComponents;
    
    Int4 ImportEcmStepByStep(ElementContainerMatrix* ecm, double ang, string label );
    bool ProjectionStepByStep( vector<Double> viewAxis );
    bool ProjectionStepByStep( PyObject* viewAxis );
    bool ExportEcmFromStepByStep(ElementContainerMatrix* ecm, UInt4 index);
    bool isDirectGeometry;
};
#endif
    
