#!/usr/bin/python
# -*- coding: utf-8 -*-

import os
import Manyo as mm
import Manyo.Utsusemi as mu
import ana.Reduction.AnaEnviron as AE
import ana.Reduction.EditWiringFile as ED

import ana.Reduction.EventDataToEcm as EtoE

try:
    ver_ED = float( ED.__version__ )
    if (ver_ED<0.6):
        raise "VersionError","EditDetectorInfo or EventDataToHist is not valid."
except:
    raise "VersionError","EditDetectorInfo or EventDataToHist is not valid."

__version__ = '0.61'

class Histogram(object):
    """
    Load EventData and convert to histogram.
    ver 0.62[141209] add TofMask
    ver 0.61[140630] add LLD
    ver 0.6 [130304] update for Monitor
    ver 0.5 [130213] upgrade
    ver 0.4 [110717] replace EventDataToHist to EventDataToEcm
    ver 0.3 [110104] add control tofDoubleFrameBoundary
    ver 0.2 [101108] can use direct conversion from event data to energy transfer
    ver 0.1 before 101108 original version
    """
    __version__ = '0.5'
    def __init__(self,runNo=[],
                 ConvType=-1,
                 Params=[],
                 DetRange=[],
                 pixelNo=-1,
                 TimeRange=[],
                 envFile="environ_ana.xml",
                 frameInfo=[0,0.0],
                 DetType="PSD",
                 LLD=[],
                 TofMask="",
                 withPulseHeight=False,
                 isDebug=False):
        """
        Constructor and initialization
        
        @param runNo    ([]) Run Number 0:Dummy data
        @param ConvType (int) TOF Conversion Type
        @param Params ([])
        @param DetRange ([])  [first PSD ID, final PSD ID]
        @param pixelNo  (int) the number of pixels on one PSD
        @param TimeRange ([]) [startTime, endTime] [seconds]
        @param envFile  (string) Environ file for event data to histogram
        @param frameInfo ([int,double]) FrameNo and Boundary TOF
        @param DetType  (string) PSD or N2MON
        @param LLD ([]) LLD and HLD for DetRange
        @param TofMask (string) 
        @param withPulseHeight (bool)
        @param isDebug  (bool)
        """
        self.isParallel = False
        
        self.runNo = None
        if type(runNo)==type([]):
            self.runNo = runNo
        elif type(runNo)==type(0):
            self.runNo = [ runNo ]
        else:
            msg = "Histogram >> runNo is invalid."
            raise UserWarning, msg
        
        self.ConvType = ConvType
        #self.isPulseHeight = False
        
        isBadParams = False
        if ((self.ConvType==2) or (self.ConvType==3) or (self.ConvType==12) or (self.ConvType==13) or (self.ConvType==21) or (self.ConvType==25)):
            if len(Params)<3:
                isBadParams = True
        elif (self.ConvType==20):
            if len(Params)<5:
                isBadParams = True
        elif (self.ConvType==22):
            if len(Params)<6:
                isBadParams = True
        else:
            msg = "Invalid ConvType (%d)"%(self.ConvType)
            raise UserWarning, msg
        """
        if (self.ConvType==50):  ## convert to pulse height
            if len(Params)<3:
                isBadParams = True
            else:
                self.isPulseHeight = True
                self.ConvType=2
        """
        if isBadParams:
            msg = "Num of Parameters is invalid for type=%d ("
            for p in Params:
                msg += " %f" % (p)
            msg +=")"
            raise UserWarning, msg

        self.Params = Params
        self.startDet = DetRange[0]
        self.endDet = DetRange[1]
        self.numOfPixelOnDet = pixelNo
        if len(TimeRange)>=2:
            self.SliceEventStart = TimeRange[0]
            self.SliceEventEnd = TimeRange[1]
        else:
            self.SliceEventStart = -1.0
            self.SliceEventEnd = -1.0
        self.envFile = ""
        if envFile.find(",")==-1:
            self.envFile = envFile
        else:
            self.envFile = "environ_ana.xml"
        self.frameInfo = frameInfo
        self.DetType = DetType
        self.LLD = LLD
        self.TofMask = TofMask
        #self.isPulseHeight = isPulseHeight
        self.withPulseHeight = withPulseHeight
        self.isDebug = isDebug
        #self.isDebug =True 
        
        ##self.L1 = EtoE.Def_L1*1000.0 #[mm]
        self.L1 = -1.0
        self.T0shift = 0.0
        self.instCode = os.environ["UTSUSEMI_SYS_NAME"]
        self.CommentHead = "Histogram >>>"
        
        self.DetectorInfo = None
        ana_env = AE.AnaEnviron(self.envFile)
        if ana_env.Status==-1:
            print self.CommentHead+"environ file ("+self.envFile+") is not existed."
            self.envFile = ""
            self.anaEnv = None
            self.Status = -1
            return
        else:
            self.anaEnv = ana_env
            ind = envFile.find(",")
            if self.isDebug:
                print "---================ Check envFile=",envFile,"ind=",ind
            if ind==-1:
                if self.anaEnv.Info["OrgWiringFile"]==None:
                    lastPeriod = 0
                    lastPeriodFile=[]
                    for period in self.anaEnv.Info["FileHistory"]:
                        if (self.runNo[0] >= period[0]) and (self.runNo[0] <= period[1]):
                            self.anaEnv.Info["OrgWiringFile"] = period[2]
                            self.anaEnv.Info["PositioningFile"] = period[3]
                            break
                        if period[1]>=lastPeriod:
                            lastPeriod = period[1]
                            lastPeriodFile=[ period[2], period[3] ]
                    if self.anaEnv.Info["OrgWiringFile"]==None:
                        if len(lastPeriodFile)==0:
                            msg = "Run No is not suitable to choose WiringInfo from environ_ana.xml"
                            raise UserWarning,msg
                        else:
                            self.anaEnv.Info["OrgWiringFile"] = lastPeriodFile[0]
                            self.anaEnv.Info["PositioningFile"] = lastPeriodFile[1]
            else:
                if self.isDebug:
                    print "=================== Changed by envFile"
                self.anaEnv.Info["OrgWiringFile"] = envFile[:ind]
                if envFile[(ind+1):]!="":
                    self.anaEnv.Info["PositioningFile"] = envFile[(ind+1):]
            self.Status = 0
            #self.DetectorInfo = mu.UtsusemiDetectorInfoReader3HePsd( self.anaEnv.Info["PositioningFile"] )
            path_dfile = ""
            if os.path.exists( self.anaEnv.Info["PositioningFile"] ):
                path_dfile = self.anaEnv.Info["PositioningFile"]
            else:
                path_dfile = os.path.join(os.environ["UTSUSEMI_USR_DIR"],"ana","xml",self.anaEnv.Info["PositioningFile"])
                if not os.path.exists( path_dfile ):
                    self.Status = -1
                    print "Cannot find DetectorInfo file "+self.anaEnv.Info["PositioningFile"]
                    return
                
            self.DetectorInfo = mu.UtsusemiDetectorInfoReader3HePsd( path_dfile )
            self.L1 = self.DetectorInfo.PutInstL1()
            

    def MakeWiringFile(self):
        if self.anaEnv== None:
            print self.CommentHead+"Set file name of environment file (loadEnvFile)."
            return
        
        path_org_wf = os.path.join(os.environ["UTSUSEMI_USR_DIR"],"ana","xml",self.anaEnv.Info["OrgWiringFile"])
        if not os.path.exists(path_org_wf):
            print self.CommentHead+"ERROR: There is no original wiring file,",path_org_wf
            return -1
        
        return self._MakeWiringFile(path_org_wf,self.anaEnv.Info["SingleMode"])
            

    def _MakeWiringFile(self,path_org_wf,p=None):
        print self.CommentHead+"Read Original WiringInfo ("+path_org_wf+")"
        ed = ED.EditWiringInfo(path_org_wf)
        daq_list=ed.InstInfo.PutContainerIdList()

        for daq_r in daq_list:
            ed.SuspendDaq(daq_r)
        
        new_module_list =[]
        if (self.startDet>=0) and (self.endDet<=ed.MaxDetId):
            for i in range(int(self.startDet),int(self.endDet+1)):
                (flag,daq_i,mod_i) = ed.DetInfoList[i]
                if self.isDebug:
                    print "flag,daq_i,mod_i",flag,daq_i,mod_i
                if (flag!=0):
                    for j in ed.InstInfo.PutContainer(daq_i).PutContainer(mod_i).PutContainerIdList():
                        info = ed.InstInfo.PutContainer(daq_i).PutContainer(mod_i).PutContainer(j) 
                        info.SetStatus(1)
                    if not (daq_i,mod_i) in new_module_list:
                        new_module_list.append( (daq_i,mod_i) )
        else:
            for (daq,mod) in p.Info["modulelist"]:
                if daq==-1:
                    for daq_r in daq_list:
                        ed.ResumeDaq(daq_r)
                    break
                else:
                    ed.ResumeModules(daq,mod)

        ed.SelectByDetType( self.DetType )
        
        if new_module_list !=[]:
            p.Info["modulelist"]=new_module_list
            
        if self.frameInfo[0]!=0:
            ed.set_FrameInfo( self.frameInfo[0],self.frameInfo[1] )
            if self.isDebug:
                print self.CommentHead+("Set FrameNo %d Boundary TOF %f" % (self.frameInfo[0],self.frameInfo[1] ))
        
        if len(self.LLD)==2:
            ed.set_LLD( self.LLD[0], self.LLD[1] )
            
        if (self.TofMask!=""):
            ed.set_TofMask( 0, self.TofMask )
            
        if self.numOfPixelOnDet!=-1:
            ed.ReSetNumOfPixel( 0,ed.MaxDetId, -1, self.numOfPixelOnDet )
            if self.isDebug:
                print self.CommentHead+"Change the number of pixels on one PSD to ",self.numOfPixelOnDet

        if self.ConvType!=-1:
            ptnId = ed.change_TofBinPattern(type=self.ConvType,cont_in=self.Params)
            maskId = -1
            if (self.TofMask!=""):
                maskId = 0
            ed.setall_TofBinInfo(ptnId,0,maskId)
            print self.CommentHead+"ConvType=",self.ConvType,", Params = ",self.Params
        
        p.Info["wiringfile"]="%s_%3s%06d_%06d_%s" % (os.environ["USER"],self.instCode,self.runNo[0],os.getpid(),p.Info["wiringfile"])
        
        if os.environ.has_key("UTSUSEMI_USR_PRIV_HOME"):
            ss = os.path.join(os.environ["UTSUSEMI_USR_PRIV_HOME"],"ana","xml",p.Info["wiringfile"])
        elif os.environ.has_key("HOME"):
            ss = os.path.join(os.environ["HOME"],"ana","xml",p.Info["wiringfile"])
        else:
            ss = p.Info["wiringfile"]
        ed.SaveToXml( ss )
        comm = "chmod go+w "+ss
        print self.CommentHead+"Do command = ",comm
        try:
            os.system(comm)
        except:
            print self.CommentHead+" Command missed."

        return 0


    def GetHistogram(self,daq=-1):
        if self.anaEnv==None:
            print self.CommentHead+" There is no information."
            return -1

        if self.instCode != self.anaEnv.Info["InstCode"]:
            print self.CommentHead+" Instrument code is not correct."
            return -1
        
        p=self.anaEnv.Info["SingleMode"]
        
        DAT = EtoE.GetHistogram(eventtype=self.DetType,
                                runnumber=self.runNo,
                                WiringInfo_xml=p.Info["wiringfile"],
                                DetectorInfo_xml=self.anaEnv.Info["PositioningFile"],
                                starttime=self.SliceEventStart,
                                endtime=self.SliceEventEnd,
                                withPH=self.withPulseHeight
                                )
        """
        DAT = EtoE.GetHistogram( self.DetType, self.runNo,
                                 p.Info["wiringfile"],
                                 self.anaEnv.Info["PositioningFile"],
                                 self.SliceEventStart,self.SliceEventEnd )
        """
        if not self.isDebug:
            comm = "rm -rf %s" % (os.path.join(os.environ["UTSUSEMI_USR_PRIV_HOME"],"ana","xml",p.Info["wiringfile"]))
            try:
                os.system(comm)
            except:
                print self.CommentHead+" False to delete %s."%(os.path.join(os.environ["UTSUSEMI_USR_PRIV_HOME"],"ana","xml",p.Info["wiringfile"]))
                
        return DAT


###############################################
### Sample for utilization of Histogram class
###############################################
def GetHistDTconst(runNo=-1,startTOF=-1,endTOF=-1,binTOF=-1,startPSD=-1,endPSD=-1,pixelNo=-1,bTOF=0.0,detType="PSD"):
    """
    Load EventData and convert to histogram.
    GetHistogram(runNo=-1,paraFlag=0,startTOF=-1,endTOF=-1,binTOF=-1,startPSD=-1,endPSD=-1,pixelNo=-1,bTOF=0.0)
    @param runNo    (int) Run Number
    @param startTOF (double) top TOF value to be selected
    @param endTOF   (double) end TOF value to be selected
    @param binTOF   (double) bin width of TOF
    @param startPSD (int) first PSD ID
    @param endPSD   (int) final PSD ID
    @param pixelNo  (int) the number of pixels on one PSD
    @param bTOF     (double) boundary tof for double frame mode
    @param detType  (string) PSD or N2MON
    @retval ElementContainerMatrix
    """
    fInfo = [0,0.0]
    if bTOF!=0.0:
        fInfo[0] = int( bTOF/40000.0 )
        fInfo[1] = bTOF - float(fInfo[0])*40000.0

    params = [startTOF, endTOF, binTOF ]
    drange = [startPSD, endPSD]
    trange = [-1.0, -1.0]
    h=Historam(runNo=runNo,
               ConvType=2,
               Params=params,
               DetRange=drange,
               pixelNo=pixelNo,
               TimeRange=trange,
               frameInfo=fInfo,
               DetType=detType,
               )
    
    if h.Status==0:
        h.MakeWiringFile()
        return h.GetHistogram()

def GetHistogramSimple( runNo=-1,wiringFile="WiringFile_org.xml",detectCompFile="DetectorPosi2.xml", startTOF=-1,endTOF=-1,binTOF=-1,startPSD=-1,endPSD=-1,pixelNo=-1,bTOF=0.0 ):
    """
    Load EventData and confert to histogram using given WiringFile and DetectorCompFile.
    GetHistogramSimple( runNo=-1,wiringFile="WiringFile_org.xml",detectCompFile="DetectorPosi2.xml", 
                        startTOF=-1,endTOF=-1,binTOF=-1,startPSD=-1,endPSD=-1,pixelNo=-1,bTOF=0.0
                      )
    @param runNo    (int) Run Number
    @param wiringFile (string) Wiring File name
    @param detectCompFile (string) Detector Components file name
    @param startTOF (double) top TOF value to be selected
    @param endTOF   (double) end TOF value to be selected
    @param binTOF   (double) bin width of TOF
    @param startPSD (int) first PSD ID
    @param endPSD   (int) final PSD ID
    @param pixelNo  (int) the number of pixels on one PSD
    @param bTOF (double) boundary tof for double frame mode
    @retval ElementContainerMatrix
    """
    in_envFile = wiringFile+","+detectCompFile
    in_envFile = in_envFile.replace(" ","")

    fInfo = [0,0.0]

    if bTOF!=0.0:
        fInfo[0] = int( bTOF/40000.0 )
        fInfo[1] = bTOF - float(fInfo[0])*40000.0

    params = [startTOF, endTOF, binTOF ]
    drange = [startPSD, endPSD]
    trange = [-1.0, -1.0]
    h=Histogram(runNo=runNo,
                ConvType=-1,
                Params=params,
                DetRange=drange,
                pixelNo=pixelNo,
                TimeRange=trange,
                envFile=in_envFile,
                frameInfo=fInfo,
                DetType="PSD",
                withPulseHeight=False,
                isDebug=False
                )
    
    if h.Status==0:
        h.MakeWiringFile()
        return h.GetHistogram()

def GetHistogramByTime(runNo=-1,startTOF=-1,endTOF=-1,binTOF=-1,startPSD=-1,endPSD=-1,pixelNo=-1,startTime=-1.0,endTime=-1.0,bTOF=0.0):
    """
    Load EventData and convert to histogram.
    Histogram(runNo,paraFlag,startTOF=-1,endTOF=-1,binTOF=-1,pixelNo=-1,startPSD=-1,endPSD=-1,envFile)
    @param runNo    (int) Run Number
    @param startTOF (double) top TOF value to be selected
    @param endTOF   (double) end TOF value to be selected
    @param binTOF   (double) bin width of TOF
    @param startPSD (int) first PSD ID
    @param endPSD   (int) final PSD ID
    @param pixelNo  (int) the number of pixels on one PSD
    @param startTime (float)
    @param endTime   (float)
    @param bTOF (double) boundary tof for double frame mode
    @retval ElementContainerMatrix
    """
    fInfo = [0,0.0]
    if bTOF!=0.0:
        fInfo[0] = int( bTOF/40000.0 )
        fInfo[1] = bTOF - float(fInfo[0])*40000.0

    params = [startTOF, endTOF, binTOF ]
    drange = [startPSD, endPSD]
    trange = [startTime, endTime]
    h=Histogram(runNo=runNo,
                ConvType=2,
                Params=params,
                DetRange=drange,
                pixelNo=pixelNo,
                TimeRange=trange,
                frameInfo=fInfo,
                DetType="PSD",
                withPulseHeight=False,
                isDebug=False
                )
    
    if h.Status==0:
        h.MakeWiringFile()
        return h.GetHistogram()
    



def GetHistogramOfConv( runNo=-1,
                        ConvType=-1,
                        Params=[],
                        detectorRange=[-1,-1],
                        numOfPixel=-1,
                        timeRange=[-1.0,-1.0],
                        frameInfo=[0,0.0],
                        DetType="PSD"
                        ):
                        
    """
    Load EventData and convert to histogram
    GetHistogramOfConv( runNo=-1,
                        ConvType=-1,
                        Params=[],
                        detectorRange=[-1,-1],
                        numOfPixel=-1,
                        timeRange=[-1.0,-1.0],
                        frameInfo=[0,0.0],
                        DetType="PSD"
                        )
    @param runNo         (int)   run number
    @param ConvType      (int)   Conversion Type 
    @param Params        (list)  parameters required by ConvType
    @param detectorRange (list)  range of detectors [startId, endId]
    @param numOfPixel    (int)   the number of pixels of a PSD
    @param timeRange     (list)  time range [start Time[sec], end Time[sec] ]
    @param frameInfo     (list)  frame info
    @param DetType       (string) "PSD" or "N2MON"
    @retval ElementContainerMatrix
    """
    h=Histogram(runNo=runNo,
                ConvType=ConvType,
                Params=Params,
                DetRange=detectorRange,
                pixelNo=numOfPixel,
                TimeRange=timeRange,
                frameInfo=frameInfo,
                DetType=DetType,
                withPulseHeight=False,
                isDebug=False
                )

    if h.Status==0:
        h.MakeWiringFile()
        return h.GetHistogram()

def GetPulseHeight(runNo=-1,
                   detType="PSD",
                   phHist=[0,10000,10],
                   detectorRange=[-1,-1],
                   timeRange=[-1.0,-1.0]
                   ):
    
    """
    Load EventData and make PulseHeight hist
    GetPulseHeight(runNo=-1,
                   detType="PSD",
                   phHist=[0,10000,10],
                   detectorRange=[-1,-1],
                   timeRange=[-1.0,-1.0]
                   )
    @param runNo
    @param detType
    @param phHist
    @param detectorRange
    @param timeRange
    @retval ElementContainerMatrix
    """
    h=Histogram(runNo=runNo,
                ConvType=2,
                Params=phHist,
                DetRange=detectorRange,
                pixelNo=1,
                TimeRange=timeRange,
                DetType=detType,
                withPulseHeight=True,
                isDebug=False
                )
    
    if h.Status==0:
        h.MakeWiringFile()
        return h.GetHistogram()
