import numpy

import os,time

from vis.UtilPlot import *
import vis.VisContMParams as VCP

import Manyo as mm
import Manyo.Utsusemi as mu
import threading

class D4Mat(object):
    """
    This is python lapping for commands treating D4Matrix on Manyo-lib
    Required Manyo-lib revision is 2250 or later.
    [inamura 160227] : bugfix of wrong path treatment on Windows Environment
    """
    def __init__(self):
        """
        Constructor
        """
        self.SlicedECM = None
        self.D4Mat = mu.UtsusemiD4Matrix()
        self.CommentHead = "D4Mat >>>"
        self.progress = 0
        self.progress_max = 0
        self.AddedFileList = []
        
    def new(self, A1range,A2range,A3range,A4range,titles,paramfilepath):
        """
        makes new matrix

        @param A1range (list) [A1min, A1max, dA1]
        @param A2range (list) [A2min, A2max, dA2]
        @param A3range (list) [A3min, A3max, dA3]
        @param A4range (list) [A4min, A4max, dA4]
        @param titles  (list of string) [ AX1_title, AX2_title, AX3_title, AX4_title ]
        @param datadir (string)
        @param paramfile (string) 
        @return None
        """
        
        if (type(A1range)!=type([])) or (len(A1range)!=3):
            print self.CommentHead + "Invalid parameter about Q1 range. "
            raise
        if (type(A2range)!=type([])) or (len(A2range)!=3):
            print self.CommentHead + "Invalid parameter about Q2 range. "
            raise
        if (type(A3range)!=type([])) or (len(A3range)!=3):
            print self.CommentHead + "Invalid parameter about Q3 range. "
            raise
        if (type(A4range)!=type([])) or (len(A4range)!=3):
            print self.CommentHead + "Invalid parameter about hw range. "
            raise
        if (type(titles)!=type([]) or (len(titles)!=4)):
            print self.CommentHead + "Invalid parameter about titles. "
            raise
        if (type(paramfilepath)!=type("")):
            print self.CommentHead + "Invalid parameter about param file. "
            raise
            
        a1v = mm.ListToDoubleVector( A1range )
        a2v = mm.ListToDoubleVector( A2range )
        a3v = mm.ListToDoubleVector( A3range )
        a4v = mm.ListToDoubleVector( A4range )
        tlv = mm.MakeStringVector()
        for title in titles:
            tlv.append( str(title) )
        ##[inamura 160227]-->  # for Windows Environment
        #datadir = os.path.dirname( paramfilepath ) + "/"
        #paramfile = paramfilepath.replace( datadir, "" )
        datadir = os.path.dirname( paramfilepath )
        paramfile = os.path.basename( paramfilepath )
        ##<--[inamura 160227]
        self.D4Mat.AllocateNewMat( a1v, a2v, a3v, a4v, tlv, datadir, paramfile )
        
        if not self.D4Mat.isStatusOK():
            print self.CommnetHead + "Fail to allocate New matrix.. "
            raise
        
        
    def open(self,paramfilepath):
        """
        Opens existed Matrix data
        
        @param paramfilepath (string) full path to parameter xml file of D4Matrix
        @retval -1  Error happened
        @retval  0  No trouble
        """
        if not os.path.exists(paramfilepath):
            print self.CommentHead + "Cannot find file " + paramfilepath
            return -1
                        
        ##[inamura 160227]-->  # for Windows Environment
        #datadir = os.path.dirname( paramfilepath ) + "/"
        #filename = paramfilepath.replace( datadir, "" )
        datadir = os.path.dirname( paramfilepath )
        filename = os.path.basename( paramfilepath )
        ##<--[inamura 160227]
        print self.CommentHead + "Data dir = ",datadir
        print self.CommentHead + "param_file =",filename
        self.D4Mat.OpenMat( datadir,filename )

        if not self.D4Mat.isStatusOK():
            print self.CommentHead + "Fail to open Matrix.. "
            return -1

        
        return 0


    def close(self):
        """
        Closes opened Matrix data
        
        @param None
        @retval None
        """
        
        self.D4Mat.CloseMat()

    def rebin(self,paramfile1,paramfile2):
        """
        Rebins a matrix to the other matrix

        @param paramfile1    full path to param xml file of original matrix
        @param paramfile2    full path to param xml file of matrix to be rebined
        @retval  -1   Error happened
        @retval   0   No trouble
        """
        if not os.path.exists(paramfile1):
            print self.CommentHead + "Cannot find file " + paramfile1
            return -1
        if not os.path.exists(paramfile2):
            print self.CommentHead + "Cannot find file " + paramfile2
            return -1
                        
        ##[inamura 160227]-->  # for Windows Environment
        #datadir1 = os.path.dirname( paramfile1 ) + "/"
        #filename1 = paramfile1.replace( datadir1, "" )
        #datadir2 = os.path.dirname( paramfile2 ) + "/"
        #filename2 = paramfile2.replace( datadir2, "" )
        datadir1 = os.path.dirname( paramfile1 )
        filename1 = os.path.basename( paramfile1 )
        datadir2 = os.path.dirname( paramfile2 )
        filename2 = os.path.basename( paramfile2 )
        ##<--[inamura 160227]

        print self.CommentHead + "Rebin from:"
        print self.CommentHead + "      Data dir = ",datadir1
        print self.CommentHead + "      param_file =",filename1
        print self.CommentHead + "Rebin to:"
        print self.CommentHead + "      Data dir = ",datadir2
        print self.CommentHead + "      param_file =",filename2

        self.D4Mat.CloseMat()
        self.D4Mat.AddMatToMat( str(datadir1), str(filename1), str(datadir2), str(filename2) )

        if not self.D4Mat.isStatusOK():
            print self.CommentHead + "Fail to rebin Matrix.. "
            raise
        
        return 0
    
    def isOpened(self):
        """
        Returns True if any matrix data has been opened.
        @param None
        @retval True   matrix data has been opened.
        @retval False  not opened.
        """
        return self.D4Mat.isMatOpened()

    def putOpenedPath(self):
        """
        Returns path to parameter xml file of opened D4Matrix

        @param None
        @retval tupple of string ( directory, xml_file_name )
        """
        ret = self.D4Mat.PutOpenedDataPath()
        if ret.size()<2:
            return None

        return ( ret[0], ret[1] )
    
        
    def setTitles(self,titles,isSaved=False):
        """
        Sets other titles

        @param titles ( list of string ) [AX1 title,AX2 title,AX3 title,AX4 title]
        @param isSaved  (bool) If True, rewrite parameter xml file
        @return None
        """
        if ((type(titles)!=type([])) or (len(titles)!=4)):
            print self.CommentHead + " First Argument must be a list of 4 strings. "
            raise
        if (type(isSaved)!=type(True)):
            print self.CommentHead + " Second Argument must be bool. "
            raise
        
        tv = mm.MakeStringVector()
        for title in titles:
            tv.append( str(title) )
        self.D4Mat.SetAxTitles( tv, isSaved )
        
        ##for i,title in enumerate(titles):
        ##    self.D4Mat.SetAxTitle( i, str(title) )

        
    def addFromDirectry(self,filedir):
        """
        Adds data from files by given directory

        @param filedir (string) path to directory stored text file
        @return List of file path to be loaded
        """
        if not os.path.exists( filedir ):
            print self.CommnetHead + "Cannot find such directory, "+filedir
            raise
        
        filelist = os.listdir(filedir)
        filelist.sort()
        
        num_files = len(filelist)

        self.AddedFileList = []
        self.progress = 0
        self.progress_max = num_files
        print "self.progress_max=",self.progress_max
        for filename in filelist:
            filepath =  os.path.join( filedir, filename )
            print self.CommentHead+"filepath = "+filepath+( " :%d/%d" % (self.progress+1, num_files) ) ##[inamura 170116]
            if filepath!=None:
                time.sleep(0.2)
                self.AddedFileList.append( self.addFromFile(filepath) )
            self.progress +=1
        
        self.progress +=1
        
        ##[inamura 170116]-->
        print self.CommentHead+"--------------------------------------------------"
        print self.CommentHead+" Finished : Load vbin files"
        print self.CommentHead+" in %s"%(filedir)
        print self.CommentHead+"--------------------------------------------------"
        ##<--[inamura 170116]
        return self.AddedFileList

    def addFromFile(self,filename):
        """
        Adds data from a file

        @param filename (string) path to data file
        @return file path to be loaded
        """
        st = time.time()
        try:
            if filename[-5:]==".vbin":
                self.D4Mat.AddToMatFromBin( str( filename ) )
            elif filename[-4:]==".txt":
                self.D4Mat.AddToMatFromText( str( filename ) )
            else:
                print self.CommentHead + " File name is invalid. "
                raise
            
            if not self.D4Mat.isStatusOK():
                print self.CommnetHead + "Fail to add data from file.. "
                raise

        except:
            print self.CommentHead+"Cannot read file :",filename
            filename = None
        print self.CommentHead+"time of cost = ",time.time()-st
        return filename
            

    def subtractText( self,filename ):
        """
        Subtracts data of text file from Matrix

        @param filename (string) path to data of text file
        @retval -1  Error happened
        @retval  0  No trouble
        """

        try:
            self.D4Mat.SubtractFromMatByText( str( filename ) )
            if not self.D4Mat.isStatusOK():
                print self.CommnetHead + "Fail to subtract text file data from D4Mat.. "
                return -1
        except:
            print self.CommentHead + "Fail to open text file.."
            return -1
        
        return 0
            

    def sliceMat(self, A1range, A2range, A3range, A4range, type_axes, foldings ):
        """
        Slices Matrix

        @param A1range    (list of float) range of AX1
        @param A2range    (list of float) range of AX2
        @param A3range    (list of float) range of AX3
        @param A4range    (list of float) range of AX4
        @param type_axes  (list of string) type of axes 'X','Y' or 'T'
        @param foldings   (list of double )
        @return None
        """
        
        if (type(A1range)!=type([])) or (len(A1range)!=2):
            print self.CommnetHead + "Invalid parameter about Q1 range. "
            raise
        if (type(A2range)!=type([])) or (len(A2range)!=2):
            print self.CommnetHead + "Invalid parameter about Q2 range. "
            raise
        if (type(A3range)!=type([])) or (len(A3range)!=2):
            print self.CommnetHead + "Invalid parameter about Q3 range. "
            raise
        if (type(A4range)!=type([])) or (len(A4range)!=2):
            print self.CommnetHead + "Invalid parameter about hw range. "
            raise
        if (type(type_axes)!=type([])) or (len(type_axes)!=4):
            print self.CommnetHead + "Invalid type_axes. "
            raise
        
        a1v = mm.ListToDoubleVector( A1range )
        a2v = mm.ListToDoubleVector( A2range )
        a3v = mm.ListToDoubleVector( A3range )
        a4v = mm.ListToDoubleVector( A4range )
        
        
        cnt_Ax = {'X':0,'Y':0,'T':0}
        def_axes = mm.MakeStringVector()
        isValid = True
        for Axtype in type_axes:
            if Axtype=='X':
                cnt_Ax['X'] += 1
            elif Axtype=='Y':
                cnt_Ax['Y'] += 1
            elif Axtype=='T':
                cnt_Ax['T'] += 1
            else:
                break;
            def_axes.append(Axtype)

        if (cnt_Ax['X']!=1) or (cnt_Ax['Y']!=1) or (cnt_Ax['T']!=2):
            print self.CommentHead + "Invalid argument type_axes."
            isValid = False
            
        fld = mm.ListToDoubleVector( foldings ) ##[inamura 160616]
        if isValid:
            self.D4Mat.SliceMat( a1v, a2v, a3v, a4v, def_axes, fld )
            if not self.D4Mat.isStatusOK():
                print self.CommentHead + "Fail to slice data.. "
                raise
            self.SlicedECM = self.D4Mat.PutSlicedECA()
        else:
            raise

    #[inamura 170227]-->
    def sliceMat3d(self, A1range, A2range, A3range, A4range, type_axes, foldings, name_axes ):
        """
        Slices Matrix 3D

        @param A1range    (list of float) range of AX1
        @param A2range    (list of float) range of AX2
        @param A3range    (list of float) range of AX3
        @param A4range    (list of float) range of AX4
        @param type_axes  (list of string) type of axes 'X','Y','Z' or 'T'
        @param foldings   (list of int )
        @return None
        """
        
        if (type(A1range)!=type([])) or (len(A1range)!=2):
            print self.CommnetHead + "Invalid parameter about Q1 range. "
            raise
        if (type(A2range)!=type([])) or (len(A2range)!=2):
            print self.CommnetHead + "Invalid parameter about Q2 range. "
            raise
        if (type(A3range)!=type([])) or (len(A3range)!=2):
            print self.CommnetHead + "Invalid parameter about Q3 range. "
            raise
        if (type(A4range)!=type([])) or (len(A4range)!=2):
            print self.CommnetHead + "Invalid parameter about hw range. "
            raise
        if (type(type_axes)!=type([])) or (len(type_axes)!=4):
            print self.CommnetHead + "Invalid type_axes. "
            raise
        
        a1v = mm.ListToDoubleVector( A1range )
        a2v = mm.ListToDoubleVector( A2range )
        a3v = mm.ListToDoubleVector( A3range )
        a4v = mm.ListToDoubleVector( A4range )
        
        cnt_Ax = {'X':0,'Y':0,'Z':0,'T':0}
        def_axes = mm.MakeStringVector()
        isValid = True
        for Axtype in type_axes:
            if Axtype=='X':
                cnt_Ax['X'] += 1
            elif Axtype=='Y':
                cnt_Ax['Y'] += 1
            elif Axtype=='Z':
                cnt_Ax['Z'] += 1
            elif Axtype=='T':
                cnt_Ax['T'] += 1
            else:
                break;
            def_axes.append(Axtype)

        if (cnt_Ax['X']!=1) or (cnt_Ax['Y']!=1) or (cnt_Ax['Y']!=1) or (cnt_Ax['T']!=1):
            print self.CommentHead + "Invalid argument type_axes."
            isValid = False
            
        fld = mm.ListToDoubleVector( foldings )
        keys = mm.MakeStringVector(3)
        for i in range(3):
            keys[i] = str( name_axes[i] )
        
        if isValid:
            try:
                del self.SlicedECM
            except:
                pass
            self.SlicedECM = mm.ElementContainerMatrix()
            self.D4Mat.SliceMat3D( self.SlicedECM, a1v, a2v, a3v, a4v, def_axes, fld, keys )
            if not self.D4Mat.isStatusOK():
                print self.CommnetHead + "Fail to slice data.. "
                raise
        else:
            raise
        #<--[inamura 170227]

    def loadVisContParams(self,filename=""):
        """
        Pick up information of Axis from VisCont parameter file

        @param filename   path to XML parameter file produced by VisualCont2
        @return  tupple of list ( list_of_ranges_for_axes, list_of_titles_for_axes )
        """
        if not os.path.exists(filename):
            ss = self.CommentHead+(" Cannot find XML file(%s)" % (filename))
            raise "FileIOError",ss

        vcp = VCP.VisContMParams(filename)

        axrange_list = []
        title_list = []
        for id in range(4):
            (type_str, r_axis, folding) = vcp.paxis_info[str(id)]
            ##[inamura 160622]-->
            """
            if type_str in ["x","y"]:
                r_vec = self.D4Mat.CalcRangeAsBinCenterZero( r_axis[0], r_axis[1], r_axis[2] )
                r_axis[0] = r_vec[0]
                r_axis[1] = r_vec[1]
            """
            if type_str in ["x","y"]:
                pass
            else:
                r_step = r_axis[1] - r_axis[0]
                if len(r_axis)==2:
                    r_axis.append( r_step )
                elif len(r_axis)==3:
                    r_axis[2] = r_step
                else:
                    raise
            ##<--[inamura 160622]
            axrange_list.append(r_axis)
            title_list.append( vcp.proj_axes[str(id)][4] )
            
        return (axrange_list,title_list)

    def makeNewMatFromVisContParams(self, VisContParamFile="", D4MatParamFile=""):
        """
        Makes new D4Matrix data file using parameters fo VisContParam

        @param VisContParamFile   path to XML parameter file produced by VisualCont2
        @param D4MatParamFile     path to XML parameter file for D4Matrix
        @return None
        """
        (range_list,title_list) = self.loadVisContParams( VisContParamFile )

        self.new(range_list[0],range_list[1],range_list[2],range_list[3],title_list,D4MatParamFile )

