from __future__ import print_function

import Manyo.SAS as ms
import Manyo as mm
import Manyo.Utsusemi as mu
import os
import math
"""
Base Functions for SAS
QSpace.py
- ConvertQSpace
- QzProjection
- SectorAverageByAngles
- MakeRegionOfSectAve
"""
from utsusemi.SAS.ana.Reduction.SASUtils import SASBankDic
########################################
def ConvertQSpace( dat, InVec="0.0 0.0 1.0" ):
    """
    Convert Data to Q space EventData to produce histogram
    ConvertQSpace(dat=DAT, InVec="0.0 0.0 1.0")

    @param  dat    (ElementContainerMatrix)
    @param  InVec  (string) vector of the incident beam
    @retval None
    """
    in_vec_st = InVec.split(" ")
    if (len(in_vec_st)!=3):
        raise UserWarning("InVec parameter is invalid.")
    vx = float( in_vec_st[0] )
    vy = float( in_vec_st[1] )
    vz = float( in_vec_st[2] )

    CT = ms.ConvertToQspace( dat )
    CT.SetIncidentBeamVector( vx, vy, vz )
    CT.Execute()
    del CT

########################################
def ConvertQSpaceRPMTCenterShift(dat, DET, PIX):
    """
    Convert Data to Qspace on RPMT using shifted center position.
    The center position is at center point of the pixel given by DET and PIX.
    If DET or PIX is minus, for example -1, then this function calculates with the original position.

    ConvertQSpaceRPMTCenterShift(dat, DET, PIX)

    @param dat (ElementContainerMatrix) Target Data
    @param DET (int) Detect ID, or minus value (-1) to use original position
    @param PIX (int) Pixel No, or minus value (-1) to use original position
    @retval None
    """
    CT = ms.ConvertToQspace(dat)
    if CT.SetIncidentBeamRPMT(DET, PIX):
        CT.Execute()
    del CT

########################################
def ConvertQSpaceWithGravity( dat, params="188.19 0.1562 208.5" ):
    """
    Convert Data to Q space EventData to produce histogram with gravity effect
    ConvertQSpaceWithGravity(dat=DAT, params="188.19 0.1562 208.5")
    py = a+b*(lambda)^2

    params = "188.19 0.1562 208.5" <- a, b, px
    params = "0.1562 1.0 208.5 188.35" <- b, lambda, px, py@lambda

    @param  dat    (ElementContainerMatrix)
    @param  params (string) "a b px" or "b lambda px py"
    @retval None
    """
    param_list_st_sp = params.split(" ")
    param_list_st_cm = params.split(",")
    if len(param_list_st_sp) > len(param_list_st_cm):
        param_list_st = param_list_st_sp[:]
    else:
        param_list_st = param_list_st_cm[:]

    CT = ms.ConvertToQspace( dat )
    if (len(param_list_st)==1):
        modeNo = 5
        runNo_st = dat.PutHeader().PutString(mu.UTSUSEMI_KEY_HEAD_RUNNUMBER)
        runNo_sv = runNo_st.split(",")
        runNo = int(runNo_sv[0])
        CT.SetIncidentBeamWithGravityRPMTDetectInfo(runNo, modeNo)
    elif (len(param_list_st)==3):
        CT.SetIncidentBeamWithGravity( float(param_list_st[0]), float(param_list_st[1]), float(param_list_st[2]) )
    elif (len(param_list_st)==4):
        CT.SetIncidentBeamWithGravity( float(param_list_st[0]), float(param_list_st[1]), float(param_list_st[2]), float(param_list_st[3]) )
    else:
        raise UserWarning("params is invalid.")

    CT.Execute()
    del CT

########################################
def QzProjection( dat, QxRange="-1.0 1.0 0.005", QyRange="-1.0 1.0 0.005", QzRange="-0.5 0.0", LambdaRange="0.7 7.6", Bank="All" ):
    """
    Makes Qx-Qy map data integrated in a Qz range.
    QzProjection( dat=DAT, QxRange="-1.0 1.0 0.005", QyRange="-1.0 1.0 0.005", QzRange="-0.5 0.0" )

    @param  dat   (ElementContainerMatrix) Data after ConvertToQSpace
    @param  QxRange   (string)  "Qx_min Qx_max Qx_bin"
    @param  QyRange   (string)  "Qy_min Qy_max Qy_bin"
    @param  QzRange   (string)  "Qz_max Qz_bin"
    @param LambdaRange(string)  "Lambda_min, Lambda_max"
    @param  Bank      (string)  "All" or "<BankName> <BankName> ...", delimiter can be " ", "," or "-"
    @retval ECA   (ElementContainerArray)
    """

    qx_range_st=QxRange.split(" ")
    qy_range_st=QyRange.split(" ")
    qz_range_st=QzRange.split(" ")
    lmd_range_st=LambdaRange.split(" ")

    qxmin = float( qx_range_st[0] )
    qxmax = float( qx_range_st[1] )
    qxbin = float( qx_range_st[2] )

    qymin = float( qy_range_st[0] )
    qymax = float( qy_range_st[1] )
    qybin = float( qy_range_st[2] )

    qzmin = float( qz_range_st[0] )
    qzmax = float( qz_range_st[1] )

    lmdmin = float( lmd_range_st[0] )
    lmdmax = float( lmd_range_st[1] )

    bank_id_list = []
    if Bank.upper() != "ALL":
        delim_list = [len(Bank.split(" ")), len(Bank.split(",")), len(Bank.split("-"))]
        delim_ind = delim_list.index(max(delim_list))
        delim = " "
        if delim_ind == 1:
            delim = ","
        elif delim_ind == 2:
            delim = "-"
        bank_split_sp = Bank.split(delim)
        for a_bank in bank_split_sp:
            if a_bank=="SM":
                bank_id_list.append(SASBankDic["SMBL14"])
                bank_id_list.append(SASBankDic["SMBL16"])
            elif a_bank in SASBankDic:
                bank_id_list.append(SASBankDic[a_bank])
            else:
                UserWarning("QzProjection >> Invalid Bank name {}".format(a_bank))

    QP = ms.QzProjection( dat )
    QP.SetMapRange( qxmin, qxmax, qxbin, qymin, qymax, qybin )
    QP.SetQzRange( qzmin, qzmax )
    QP.SetLambRange( lmdmin, lmdmax )
    for a_bankId in bank_id_list:
        QP.AddBankId(a_bankId)
    QP.Execute()

    ret_eca = mm.ElementContainerArray()
    QP.SetResultAsECA( ret_eca )

    del QP
    try:
        hh = ret_eca.PutHeaderPointer()
        #hh.AddHeader("YLABEL", "Qy (1/Ang.)")
        #hh.AddHeader("XLABEL", "Qx (1/Ang.)")
        hh.Add("YLABEL", "Qy")
        hh.Add("XLABEL", "Qx")
        hh.Add("Xunit", "1/Ang.")
        hh.Add("Yunit", "1/Ang.")
        hh.Add("M2PP_X_KEY","Qx")
        hh_dat = dat.PutHeaderPointer()
        if hh_dat.CheckKey("TransmittanceParameter"):
            hh.AddHeader("TransmittanceParameter", hh_dat.PutDoubleVector("TransmittanceParameter"))
        if hh_dat.CheckKey("TransmittanceParameterError"):
            hh.AddHeader("TransmittanceParameterError", hh_dat.PutDoubleVector("TransmittanceParameterError"))
        if hh_dat.CheckKey("TransmittanceFitFunc"):
            hh.AddHeader("TransmittanceFitFunc", hh_dat.PutString("TransmittanceFitFunc"))
        if hh_dat.CheckKey("RUNNUMBER"):
            hh.AddHeader("RUNNUMBER", hh_dat.PutString("RUNNUMBER"))
    except:
        pass
    return ret_eca

########################################
def SectorAverageByAngles( dat, Banks="SM16-SM14-SD-SU", Qrange="0.01 2.0 0.001 dQ/Q", LamRange="0.7 0.8", AzimAng="0.0 0.0", PolAng="0.0 0.0", QLamFile="None/QLambdaRange" ):
    """
    Convert Data to Q space EventData to produce histogram
    SectorAverageByAngles( dat=DAT, Banks="SM16-SM14-SD-SU", Qrange="0.01 2.0 0.001 dq/Q", LamRange="0.7 0.8", AzimAng="0.0 0.0", PolAng="0.0 0.0" )

    To give Q binning, default format is "<Qmin> <Qmax> <DeltaQ> <Type>". Another method is given by Text file described list of Q values.
    Format :
        - The line with "#Type=" at the head is parameter for Q binning type { dq, dq/q, None }
        - The line with "#" at the head is ignored

    Example 1 : Free q value list ( Descibe Q binning with a Q value for each line )
        #QbinType{dq,dq/q,None}
        #Type=None
        0.01
        0.02
        0.05
        ...
    Example 2 : delta-Q const   ( Add dQ value after Q value with comma )
        #QbinType{dq,dq/q,None}
        #Type=dq
        0.01,0.01
        0.5,0.02
        1.0,0.04
        ...
    Example 3 : delta-Q/Q const   ( Add dQ/Q value after Q value with comma )
        #QbinType{dq,dq/q,None}
        #Type=dq/q
        0.01,0.01
        0.5,0.02
        1.0,0.04
        ...

    @param  dat  (ElementContainerMatrix)
    @param  Banks    (string)  "SM16-SD-SU" means these banks are unified in one Iq, "SM SD SU" means separated Iq
    @param  Qrange   (string)  Q range and type of Qbin "Qmin Qmax deltaQ type" type must be dQ/Q or dQ. Or Qrange.txt
    @param  LamRange (string)  Lambda range "Lambda_min Lambda_max"
    @param  AzimAng  (string)  "-20.0 20 170 -170"
    @param  PolAng   (string)  "0 90 0 40"
    @param  QLamFile (string)  filename of Q-Lambda range info. If None, no use. (read QLambdaRange_SM.txt for SM bank)
    @retval Iq       (list of ElementContainer)
    """

    Banks = Banks.strip().upper()

    isUnifiedIq = False
    bankId_list = []
    bankName_list = []
    if len(Banks.split(" "))!=1:
        tmp_bank_list_s = Banks.split(" ")
        for bb in tmp_bank_list_s:
            if bb=="SM16":
                bb="SMBL16"
            if bb=="SM14":
                bb="SMBL14"
            if bb=="SM":
                bankId_list.append( SASBankDic["SMBL16"] )
                bankId_list.append( SASBankDic["SMBL14"] )
                bankName_list.append( "SMBL16" )
                bankName_list.append( "SMBL14" )
            elif bb in SASBankDic:
                bankId_list.append( SASBankDic[bb] )
                bankName_list.append( bb )
        if len(bankId_list)==0:
            msg = "Cmm.SectorAverageByAngles > Banks is invalid ("+Banks+")"
            raise UserWarning(msg)
    elif len(Banks.split("-"))!=1:
        tmp_bank_list_s = Banks.split("-")
        for bb in tmp_bank_list_s:
            if bb=="SM16":
                bb="SMBL16"
            if bb=="SM14":
                bb="SMBL14"
            if bb=="SM":
                bankId_list.append( SASBankDic["SMBL16"] )
                bankId_list.append( SASBankDic["SMBL14"] )
                bankName_list.append( "SMBL16" )
                bankName_list.append( "SMBL14" )
            elif bb in SASBankDic:
                bankId_list.append( SASBankDic[bb] )
                bankName_list.append( bb )
        if len(bankId_list)==0:
            msg = "Cmm.SectorAverageByAngles > Banks is invalid ("+Banks+")"
            raise UserWarning(msg)
        isUnifiedIq = True
    else:
        if Banks=="SM16":
            Banks="SMBL16"
        if Banks=="SM14":
            Banks="SMBL14"

        if Banks=="SM":
            bankId_list.append( SASBankDic["SMBL16"] )
            bankId_list.append( SASBankDic["SMBL14"] )
            bankName_list.append( "SMBL16" )
            bankName_list.append( "SMBL14" )
            isUnifiedIq = True
        elif Banks=="ALL":
            bankId_list = list(SASBankDic.values())
            bankName_list = list(SASBankDic.keys())
        elif Banks in SASBankDic:
            bankId_list.append( SASBankDic[Banks] )
            bankName_list.append( Banks )
        else:
            msg = "Cmm.SectorAverageByAngles > Banks is invalid ("+Banks+")"
            raise UserWarning(msg)

    QbinType = 0
    QRlist = []
    QbinInfo = []
    QbinList = []
    qr_list_st = Qrange.split(" ")
    if len(qr_list_st)==1:
        p_file=mu.FindParamFilePath( qr_list_st[0] )
        if p_file=="":
            raise UserWarning(" Qrange is invalid")
        fo = open( p_file, "r" )
        p_conts_list = fo.readlines()
        fo.close()
        if (len(p_conts_list)==0):
            raise UserWarning(" Qrange File is invalid ("+p_file+")")
        ind_lines = 0
        while(True):
            if p_conts_list[ind_lines].find("#Type=")==0:
                qbin_type=p_conts_list[ind_lines][6:].strip()
                if qbin_type.lower()=="dq":
                    QbinType=0
                elif qbin_type.lower()=="dq/q":
                    QbinType=1
            elif p_conts_list[ind_lines].find("#")!=0:
                break
            ind_lines += 1
        split_char = ":"
        tmp_a_line_sp = p_conts_list[ind_lines].split(split_char)
        if len(tmp_a_line_sp)==1:
            tmp_a_line_sp2 = p_conts_list[ind_lines].split(",")
            if len(tmp_a_line_sp2)!=1:
                split_char = ","
                tmp_a_line_sp = tmp_a_line_sp2[:]

        if len(tmp_a_line_sp)==1: ## Free Hist
            QbinList.append( float(p_conts_list[ind_lines].strip()) )
            ind_lines += 1
            while( len(p_conts_list)>ind_lines ):
                if p_conts_list[ind_lines].find("#")!=0:
                    if (p_conts_list[ind_lines].strip())!="":
                        QbinList.append( float(p_conts_list[ind_lines].strip()) )
                ind_lines +=1

        elif len(tmp_a_line_sp)==2: ## Qbin with bin info
            QbinInfo.append( float( tmp_a_line_sp[0].strip() ) )
            QbinInfo.append( float( tmp_a_line_sp[1].strip() ) )
            ind_lines += 1
            while( len(p_conts_list)>ind_lines ):
                if p_conts_list[ind_lines].find("#")!=0:
                    tmp_a_line_sp = p_conts_list[ind_lines].split(split_char)
                    if len(tmp_a_line_sp)>=2:
                        QbinInfo.append( float( tmp_a_line_sp[0].strip() ) )
                        QbinInfo.append( float( tmp_a_line_sp[1].strip() ) )
                    elif len(tmp_a_line_sp)==1: # Last line
                        if tmp_a_line_sp[0].strip()!="":
                            QbinInfo.append( float( tmp_a_line_sp[0].strip() ) )
                            QbinInfo.append( 0.0 )
                            break
                ind_lines += 1
        else:
            raise UserWarning(" Qrange File is invalid.  ("+p_file+")")

    elif len(qr_list_st)==3:
        for i in range(3):
            QRlist.append( float(qr_list_st[i]) )
    elif len(qr_list_st)==4:
        if qr_list_st[3].strip().lower()=="dq/q":
            QbinType=1
        for i in range(3):
            QRlist.append( float(qr_list_st[i]) )
    else:
        raise UserWarning(" Qrange is invalid."+Qrange)
    """
    if len(qr_list_st)!=3 and len(qr_list_st)!=4:
        raise UserWarning, " Qrange is invalid."+Qrange

    qr_list = [ float(qr_list_st[0]), float(qr_list_st[1]), float(qr_list_st[2]) ]
    QbinType = 0
    if len(qr_list_st)==4:
        if (qr_list_st[3].lower()=="dq/q"):
            QbinType = 1
    """

    lam_list_st = LamRange.split(" ")
    lam_list = []
    for lam in lam_list_st:
        if lam!="":
            lam_list.append( float(lam) )
    if len(lam_list)!=2:
        raise UserWarning(" LamRange is invalid.")

    az_list_st = AzimAng.split(" ")
    az_list = []
    for az in az_list_st:
        if az!="":
            az_list.append( float(az) )
    if len(az_list)==2:
        if (az_list[0]==0.0) and (az_list[1]==0.0):
            az_list=[]

    po_list_st = PolAng.split(" ")
    po_list = []
    for po in po_list_st:
        if po!="":
            po_list.append( float(po) )

    v_angles = mm.MakeDoubleVector()
    if len(az_list)!=0:
        if len(az_list)==len(po_list):

            for i in range( 0,len(az_list),2 ):
                v_angles.append( az_list[i] )
                v_angles.append( az_list[i+1] )
                v_angles.append( po_list[i] )
                v_angles.append( po_list[i+1] )
        else:
            raise UserWarning("Angle Info are invalid")

    ## QLamFile check
    QLam_range_bank = []
    if QLamFile[:4].lower()!="none":
        for bank_name in bankName_list:
            qlam_range = []
            qlam_file = "%s_%s.txt" % (QLamFile, bank_name)
            #qlam_file_path = os.path.join( os.environ["UTSUSEMI_USR_DIR"],"ana","xml",qlam_file )
            qlam_file_path = mu.FindParamFilePath( qlam_file )
            if not os.path.exists(qlam_file_path):
                raise UserWarning("file given by QLamFile argument is not existed. name="+qlam_file)
            fo = open( qlam_file_path, "r" )
            rline = fo.readline()
            while( rline!="" ):
                if rline[0]!="#":
                    lamline_st = rline.split(",")
                    if len(lamline_st)==3:
                        qlam_range.append( (float(lamline_st[0]), float(lamline_st[1]), float(lamline_st[2]) ) )
                rline = fo.readline()
            fo.close()
            QLam_range_bank.append( (SASBankDic[bank_name], qlam_range[:]) )

    ## [counts/lambda] -> [counts] instead of Jacobian calc
    #tt = ms.ConvertToPoint(dat)
    #tt.Execute(True)
    #del tt
    ## Executes sector-averaging
    cc=ms.SectorAverageByAngle(dat)
    if (isUnifiedIq):
        bank_v = mm.MakeInt4Vector()
        for bb in bankId_list:
            bank_v.append( bb )
        cc.SetTargetBank( bank_v )

    if len(QLam_range_bank)!=0:
        for (bank_id,QLam_range) in QLam_range_bank:
            for (lam,qmin,qmax) in QLam_range:
                cc.AddQLambdaRangeToIq( bank_id, lam, qmin, qmax )

    if len(QRlist)!=0:
        cc.SetQRange( QbinType, QRlist[0], QRlist[1], QRlist[2] ) ## Set Q range for I(Q)
    elif len(QbinInfo)!=0:
        QbinInfo_v = mm.MakeDoubleVector()
        for a_qbin in QbinInfo:
            QbinInfo_v.append(a_qbin)
        cc.SetQRange( QbinType, QbinInfo_v ) ## Set Q range for I(Q)
    elif len(QbinList)!=0:
        QbinList_v = mm.MakeDoubleVector()
        for a_qbin in QbinList:
            QbinList_v.append(a_qbin)
        cc.SetQRange( QbinList_v ) ## Set Q range for I(Q)

    cc.SetLambdaRange( lam_list[0], lam_list[1] )  ## Set Lambda range
    if v_angles.size()!=0:
        cc.SetSectAngles( v_angles )
    cc.Execute()

    ret = []
    if (isUnifiedIq):
        print( "SectorAverage.py : bankId_list[0]=",bankId_list[0])
        a_ec = cc.PutIq( bankId_list[0] )
        if (a_ec.PutSize()!=0):
            ret.append( a_ec )
    else:
        for i in bankId_list:
            print( "SectorAverage.py : bankId_list[i]=",i)
            a_ec = cc.PutIq( i )
            if (a_ec.PutSize()!=0):
                ret.append( a_ec )
    del cc

    ## [counts]->[counts/lambda] instead of Jacobian calc
    #tt = ms.ConvertToPoint(dat)
    #tt.Execute()
    #del tt

    """
    ss=""
    for i in bankId_list:
        ss+="%02d,"%(i)
    print "@@@@ BankID_list ="+ss
    if (isUnifiedIq):
        print "@@@@ Unified Iq"
    else:
        print "@@@@ Separated Iqs"
    """

    """
    num_of_iq=cc.PutNumOfIq()
    ret = []
    for i in range(num_of_iq):
        ret.append( cc.PutIq(i) ) ## get I(Q)
    """
    """
    ret = []
    if SASBankDic.has_key(Bank):
        if Bank=="SM":
            ret.append( cc.PutIq( SASBankDic["SMBL16"] ) )
            ret.append( cc.PutIq( SASBankDic["SMBL14"] ) )
        else:
            ret.append( cc.PutIq( SASBankDic[Bank] ) )
        del cc

    else:
        del cc
        raise UserWarning, "Bank name is invalid"
    """
    try:
        hh_dat = dat.PutHeaderPointer()
        for ret_ec in ret:
            hh = ret_ec.PutHeaderPointer()
            if hh_dat.CheckKey("TransmittanceParameter"):
                hh.AddHeader("TransmittanceParameter", hh_dat.PutDoubleVector("TransmittanceParameter"))
            if hh_dat.CheckKey("TransmittanceParameterError"):
                hh.AddHeader("TransmittanceParameterError", hh_dat.PutDoubleVector("TransmittanceParameterError"))
            if hh_dat.CheckKey("TransmittanceFitFunc"):
                hh.AddHeader("TransmittanceFitFunc", hh_dat.PutString("TransmittanceFitFunc"))
    except:
        pass
    return ret

########################################
def MakeRegionOfSectAve( dat, Bank="SM", Qrange="0.01 2.0 0.001 dQ/Q", LamRange="0.7 7.6 0.1", AzimAng="0.0 0.0", PolAng="0.0 0.0", ErrTh="1.0 e", OutFile="QLambdaRange", TwoThetaRange="0.0-180.0" ):
    """
    Makes Lamda slice SectorAverage
    MakeRegionOfSectAve( dat=DAT, Bank="SM", Qrange="0.01 2.0 0.001 dQ/Q", LamRange="0.7 7.6 0.1", AzimAng="0.0 0.0", PolAng="0.0 0.0", ErrTh=1.0, OutFile="QLambdaRange" )

    @param  dat  (ElementContainerMatrix)
    @param  Bank     (string)  Target bank.
    @param  Qrange   (string)  Q range and type of Qbin "Qmin Qmax deltaQ type" type must be dQ/Q or dQ
    @param  LamRange (string)  Lambda range "Lambda_min Lambda_max"
    @param  AzimAng  (string)  "-20.0 20 170 -170"
    @param  PolAng   (string)  "0 90 0 40"
    @param  ErrTh    (string)   "sh type" sh : threshold of error bar to decide useless region (ErrTh<=0.0 means no decision), type="e" or "ie"
    @param  OutFile  (string)  output file name for Q-Lambda range for target bunk (filename = QLambdaRange_SM.xml)
    @param  TwoThetaRange (string)
    @retval IQLam    (ElementContainerArray)
    """

    lam_list_st = LamRange.split(" ")
    if len(lam_list_st)!=3:
        raise UserWarning(" Lamrange is invalid.")
    lam_min = abs( float( lam_list_st[0] ) )
    lam_max = abs( float( lam_list_st[1] ) )
    lam_bin = abs( float( lam_list_st[2] ) )

    if (lam_min>lam_max):
        lam_tmp = lam_min
        lam_min=lam_max
        lam_max = lam_tmp

    lam_cur = lam_min - 0.5*lam_bin
    lam_list = []
    while(lam_cur<=lam_max):
        lam_list.append( (lam_cur,(lam_cur+lam_bin)) )
        lam_cur += lam_bin

    hh = mm.HeaderBase()
    xlabel = "Lamb [%4.2f+ i*%4.2f]"%(lam_min,lam_bin)
    hh.Add("XLABEL",xlabel)
    hh.Add("YLABEL","Q [1/Ang]")
    IQlambda = mm.ElementContainerArray(hh)
    print(" Slice along Lamgda")
    for lamRng in lam_list:
        lamRng_st = "%f %f" % lamRng
        print("lamda range= "+lamRng_st)
        Iq_res = SectorAverageByAngles( dat, Bank, Qrange, lamRng_st, AzimAng, PolAng)
        if len(Iq_res)!=1:
            raise UserWarning("Bank argument must be only one bank")
        IQlambda.Add( Iq_res[0] )

    err_th_st = ErrTh.split(" ")
    if len(err_th_st)!=2:
        raise UserWarning("ErrTh argument is invalid")
    err_th = float( err_th_st[0] )
    th_type = err_th_st[1].lower()
    if not th_type in ["e","ie"]:
        raise UserWarning("ErrTh argument is invalid")

    print("MakeRegionOfSectAve >> err_th=",err_th)
    print("MakeRegionOfSectAve >> th_type=",th_type)

    UCV = mu.UtsusemiUnitConverter()

    # Check TwoThetaRange argument
    _TwoThetaRange = []
    two_theta_list = TwoThetaRange.split("-")
    if len(two_theta_list)==2:
        _twotheta_min = float( two_theta_list[0] )
        _twotheta_max = float( two_theta_list[1] )
        if _twotheta_min!=0.0 or _twotheta_max!=180.0:
            _TwoThetaRange = [ _twotheta_min, _twotheta_max ]
            print("MakeRegionOfSectAve >> Two Theta Range = [%g, %g]"%(_TwoThetaRange[0],_TwoThetaRange[1]))
            #err_th=-1.0

    run_number_st = dat.PutHeaderPointer().PutString("RUNNUMBER")

    ss="## Q range limitation for each wave length lambda\n"
    ss+="## Run Number=%s\n" % (run_number_st)
    ss+="## Bank = %s\n" % (Bank)
    ss+="##\n"
    ss+="## lambda[A],Qmin[1/A],Qmax[1/A]\n"
    #print(ss)

    for i in range(len(lam_list)):
        iqlambda = IQlambda(i)
        lamrange = lam_list[i]
        lamb = (lamrange[0]+lamrange[1])/2.0

        ii=iqlambda.PutY()
        #ee=iqlambda.PutE()
        ee=iqlambda.PutP("Error")
        qq=iqlambda.Put("Q")
        ## Set MASK to bin by given threshold
        if (err_th>0.0):
            for j in range(ii.size()):
                if th_type=="e":
                    if (abs(ee[j])>err_th):
                        ee[j]=-1.0*(abs(ee[j]))
                else:
                    if (ii[j]!=0.0):
                        if (abs(ee[j]/ii[j])>err_th):
                            ee[j]=-1.0*(abs(ee[j]))
                    else:
                        ee[j]=-1.0*(abs(ee[j]))
        if (len(_TwoThetaRange)==2):
            for j in range(ii.size()):
                #(*it_kval) = (UU->VtoLambda())/(*it_lamb)*1000.0 * (UU->Vmm_msToK());
                kf = (UCV.VtoLambda())/lamb*1000.0*UCV.Vmm_msToK()
                sin_val = (qq[j]/2.0)/kf
                two_theta = -1.0
                if sin_val<1.0:
                    two_theta_rad = 2.0*math.asin( sin_val )
                    two_theta = two_theta_rad/math.pi*180.0
                if two_theta==-1.0 or two_theta<_TwoThetaRange[0] or two_theta>_TwoThetaRange[1] :
                    ee[j] = -1.0*(abs(ee[j]))

        qmin=-1.0
        qmax=-1.0
        ii_max = ii.size()-1
        for j in range(1,ii.size()):
            ##print "#### %f/%f, %f/%f / %f/%f, %f/%f" %(ee[j-1],ii[j-1],ee[j],ii[j],ee[ii_max-(j-1)],ii[ii_max-(j-1)],ee[ii_max-j],ii[ii_max-j])
            #if (qmin<0.0) and (ee[j-1]<0.0) and (ee[j]>=0.0):
            if (qmin<0.0) and (ee[j-1]<=0.0) and (ee[j]>0.0):
                qmin = qq[j]
            #if (qmax<0.0) and (ee[ii_max-(j-1)]<0.0) and (ee[ii_max-j]>=0.0):
            if (qmax<0.0) and (ee[ii_max-(j-1)]<=0.0) and (ee[ii_max-j]>0.0):
                qmax = qq[ii_max-j]
            if (qmin!=-1.0) and (qmax!=-1.0):
                break
        if (qmin==-1.0):
            print("--Qmin Search")
            for j in range(ii.size()):
                #print "##search Qmin ",qq[j],ii[j],ee[j]
                if ii[j]>0.0:
                    qmin = qq[j]
                    break
        if (qmax==-1.0):
            print("--Qmax Search")
            for j in range(ii.size()):
                #print "##search Qmax : ",qq[ii_max-j],ii[ii_max-j],ee[ii_max-j]
                if ii[ii_max-j]>0.0:
                    qmax = qq[ii_max-j]
                    break



        #print("%f, %f, %f" %(lamb,qmin,qmax))
        ss += "%f, %f, %f\n" % (lamb,qmin,qmax)

    bank_name_list = []
    if Bank=="SM":
        bank_name_list.append("SMBL16")
        bank_name_list.append("SMBL14")
    else:
        bank_name_list.append(Bank)

    for bank_name in bank_name_list:
        fname = "%s_%s.txt"%(OutFile,bank_name)
        #fpath = os.path.join( os.environ["UTSUSEMI_USR_DIR"],"ana","xml", fname )
        fpath = mu.FindParamFilePath( fname )
        if fpath!="":
            mu.UtsusemiWarning("MakeRegionOfSectAve >> Overwrite %s"%(fpath))
        else:
            if mu.UTSUSEMIMANYOVERSION<"4.0.0":
                fpath = os.path.join( os.environ["UTSUSEMI_USR_DIR"],"ana","xml", fname )
            else:
                fpath = os.path.join( os.environ["UTSUSEMI_BASE_DIR"],os.environ["UTSUSEMI_INST_CODE"],"ana","xml", fname )

        fo = open( fpath, "w" )
        fo.write( ss )
        fo.close()


    #return IQlambda
    xx_new = IQlambda(0).PutXList()
    num_of_lambda = IQlambda.PutSize()

    matrix = []
    matrix_e = []
    for i in range(len(xx_new)-1):
        a_line = [ 0.0 for i in range(num_of_lambda) ]
        matrix.append( a_line[:] )
        matrix_e.append( a_line[:] )

    for i in range(num_of_lambda):
        ec = IQlambda(i)
        yy_ec = ec.PutYList()
        ee_ec = ec.PutEList()
        for j,ax in enumerate(yy_ec):
            if ee_ec[j]>0.0:
                matrix[j][i] = ax
                matrix_e[j][i] = ee_ec[j]

    XX = []
    for ax1,ax2 in lam_list:
        XX.append(ax1)
    XX.append( lam_list[-1][1] )

    hh = mm.HeaderBase()
    #xlabel = "Q [1/Ang]"
    #hh.Add("XLABEL",xlabel)
    #hh.Add("YLABEL","Lambda")
    hh.Add("XLABEL","Q")
    hh.Add("Xunit", "1/Ang")
    hh.Add("YLABEL","Lambda")
    hh.Add("Yunit", "Ang")
    hh.Add("M2PP_X_KEY","Q")
    ret = mm.ElementContainerArray(hh)
    for i in range( len(xx_new)-1 ):
        xrange_v = mm.MakeDoubleVector()
        xrange_v.append( xx_new[i] )
        xrange_v.append( xx_new[i+1] )
        header = mm.HeaderBase()
        header.Add( "XRANGE", xrange_v )
        ec = mm.ElementContainer(header)
        ec.Add( "X", XX )
        ec.Add( "Y", matrix[i] )
        ec.Add( "E", matrix_e[i] )
        ec.SetKeys("X","Y","E")
        ret.Add(ec)
    return ret

########################################
def GetAzimuthProfile(dat, Qcenter=0.036, dQ=0.002, azim_bin=1.0, azim_range="0.0 360.0", lambda_range="0 0"):
    """
    Calculates Azimuth Profile from DataReduction

    @param dat          (ElementContainerMatrix)
    @param Qcenter      (float)
    @param dQ           (float)
    @param azim_bin     (float)
    @param azim_range   (string)
    @param lambda_range (string)
    @retval Iap
    """
    ec_list=GetAzimuthProfileMulti([dat], Qcenter, dQ, azim_bin, azim_range, lambda_range)
    return ec_list[0]

########################################
def GetAzimuthProfileMulti(dat_list, Qcenter=0.036, dQ=0.002, azim_bin=1.0, azim_range="0.0 360.0", lambda_range="0 0"):
    """
    Calculates Azimuth Profile from DataReduction

    @param dat_list     (list)
    @param Qcenter      (float)
    @param dQ           (float)
    @param azim_bin     (float)
    @param azim_range   (string)
    @param lambda_range (string)
    @retval IapList
    """
    azim_range_list = azim_range.split(" ")
    azim_min = float(azim_range_list[0])
    azim_max = float(azim_range_list[1])

    ec_list = []
    CAP = ms.CalcAzimuthProfile()
    if CAP.SetQRadius( Qcenter, dQ ):
        if CAP.SetAzimuthBinning( azim_bin, azim_min, azim_max ):
            if lambda_range!="0 0":
                l_list = lambda_range.split(" ")
                CAP.SetLambRange( float( l_list[0] ), float( l_list[1] ) )

            for i,dat in enumerate(dat_list):
                if not isinstance(dat, mm.ElementContainerMatrix):
                    raise UserWarning("Given data is not ElementContainerMatrix")
                p_chk = mu.UtsusemiCheckDataProcess()
                if not p_chk.CheckProcess(dat,"ConvertToQspace"):
                    raise UserWarning("Given data is invalid ( must do ConvertToQspace before )")
                CAP.SetTarget( dat )
                EC = CAP.GetProfileAverage()
                hh = EC.PutHeaderPointer()
                hh.Add( "AZIMUTHPROFILE","%03dfrm_Q%g_dQ%g"%(i+1,Qcenter,dQ) )
                ec_list.append( EC )
        else:
            raise UserWarning("Azimuth parameter is invalid.")
    else:
        raise UserWarning("Q or dQ parameter is invalid.")

    del CAP
    return ec_list
