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

from __future__ import print_function
import Manyo.Utsusemi as mu
import Manyo  # [inamura 160804]
import os  # [inamura 160804]
import math  # [inamura 120806]


class TreatSPE(object):
    """
    ISISやMSLICEで用いられるSPE fileおよびPHX fileの作成、読み込みを行うクラス
    ElementContainerMatrixからSPEおよびPHXファイルを作成する
    """

    def __init__(self):
        """
        コンストラクタ
        @param Nothing
        """
        self.CommentHead = "TreatSPE>>> "

    def _MakeOneGroup(self, in_list):
        """
        SPEファイル内で使用される8列のフォーマットの作成し、ストリングで戻す
        @param   in_list (LIST) データ
        @retval  (String) フォーマット化されたデータ
        """
        num = len(in_list)
        rows = int(num / 8)
        amari = num % 8
        # print "lines,colms,amari=",num,colms,amari

        SS = ""
        for i in range(rows):
            # SS += "%-10.4g%-10.4g%-10.4g%-10.4g%-10.4g%-10.4g%-10.4g%-10.4g\n" \
            # SS += "%-10.4f%-10.4f%-10.4f%-10.4f%-10.4f%-10.4f%-10.4f%-10.4f\n" \
            SS += "%10.3E%10.3E%10.3E%10.3E%10.3E%10.3E%10.3E%10.3E\n" % (in_list[i * 8],
                                                                          in_list[i * 8 + 1],
                                                                          in_list[i * 8 + 2],
                                                                          in_list[i * 8 + 3],
                                                                          in_list[i * 8 + 4],
                                                                          in_list[i * 8 + 5],
                                                                          in_list[i * 8 + 6],
                                                                          in_list[i * 8 + 7])
        if amari != 0:
            for i in range(amari):
                SS += "%10.3E" % in_list[8 * rows + i]
            SS += "\n"

        return SS

    def MakeSPE(self, dat, filename="", Def_Pixel_Size=[], phx_filepath="default", par_filepath="default"):
        """
        SPE,PHXファイルを作成する
        ElementContainerMatrixから必要な情報を取り出し、必要なフォーマットへ変換し、ファイルに保存する
        @param   dat   (ElementContainerMatrix) TofToEnergyTransferが終わっている必要がある
        @param   file  (string) ファイル名（拡張子なし）
        @retval  None
        """
        numQ = 0
        Int_list = []  # List of the intensity list put from all ElementContainers
        Err_list = []  # List of the error list put from all ElementContainers
        Pol_list = []  # List of the polar angles put from all ElementContainers
        Azm_list = []  # List of the azimuthal angles put from all ElementContainers
        L2_list = []  # List of L2 #[inamura 120806]

        # エレメントコンテナを取り出す
        print(self.CommentHead + "Loading ElementContainerMatix...")
        for psd in range(dat.PutTableSize()):
            eca = dat(psd)
            # PSDはMASKEDか
            hh_eca = eca.PutHeaderPointer()
            if (hh_eca.CheckKey("MASKED") == 1) and (hh_eca.PutInt4("MASKED") == 1):
                continue
            # MASKEDでなければPixelのECを取り出す
            for pixel in range(eca.PutTableSize()):
                # PixelはMASKEDか
                if (hh_eca.CheckKey("MASKED") == 1) and (hh_eca.PutInt4("MASKED") == 1):
                    continue
                # エレメントコンテナの取り出しと単位量あたりへの変換
                ec_org = eca(pixel)
                hh_ec = ec_org.PutHeaderPointer()
                # Polar angleとAzimuthal angleの取り出し
                if (hh_ec.CheckKey("MASKED") == 1) and (hh_ec.PutInt4("MASKED") == 1):
                    continue
                pol_vec = hh_ec.PutDoubleVector("PixelPolarAngle")
                azm_vec = hh_ec.PutDoubleVector("PixelAzimAngle")
                pol = [pol_vec[0], pol_vec[1]]
                azm = [azm_vec[0], azm_vec[1]]
                Pol_list.append(pol)
                Azm_list.append(azm)

                # [inamura 120806]-->
                # Pixel Position
                pos_vec = hh_ec.PutDoubleVector("PixelPosition")
                L2_list.append(math.sqrt(pos_vec[0] * pos_vec[0] + pos_vec[1] * pos_vec[1] + pos_vec[2] * pos_vec[2]) / 1000.0)
                # <--[inamura 120806]

                # IntensityとErrorの取り出し
                ec = mu.HistogramBinToPoint(ec_org)
                Int_list.append(ec.PutYList())
                Err_list.append(ec.PutEList())
                numQ += 1

        # Energy Transferはどのヒストグラムでも同一だとして取り出す
        hw_list = dat(0, 0).PutXList()
        # Phiの値を計算（gridなので個数は+1）
        phi_list = [(Pol_list[0][0] - (Pol_list[1][0] - Pol_list[0][0]) / 2.0)]
        for i in range(len(Pol_list) - 1):
            phi_list.append((Pol_list[i][0] + Pol_list[i + 1][0]) / 2.0)
        phi_list.append(Pol_list[-1][0] + (Pol_list[-1][0] - Pol_list[-2][0]) / 2.0)

        # print "numQ, length of hw, phi, Int, Err, pols, azms = ",numQ,len(hw_list),len(phi_list),len(Int_list),len(Err_list),len(Pol_list),len(Azm_list)

        # ファイルを開く
        # SPE_filename = filename+".spe"
        # PHX_filename = filename+".phx"
        # PAR_filename = filename+".par" #[inamura 120806]
        ROOT_filename = ""
        if filename[-4:].upper() == ".SPE":
            SPE_filename = filename
            ROOT_filename = filename[:-4]
        else:
            SPE_filename = filename + ".spe"
            ROOT_filename = filename
        if phx_filepath.upper() == "NONE":
            PHX_filename = ""
        elif phx_filepath.upper() == "DEFAULT":
            PHX_filename = ROOT_filename + ".phx"
        else:
            if phx_filepath[-4:].upper() == ".PHX":
                PHX_filename = phx_filepath
            else:
                PHX_filename = phx_filepath + ".phx"
        if par_filepath.upper() == "NONE":
            PAR_filename = ""
        elif par_filepath.upper() == "DEFAULT":
            PAR_filename = ROOT_filename + ".par"
        else:
            if par_filepath[-4:].upper() == ".PAR":
                PAR_filename = par_filepath
            else:
                PAR_filename = par_filepath + ".par"

        # fo_phx = open(PHX_filename,'w')
        # fo_spe = open(SPE_filename,'w')
        # fo_par = open(PAR_filename,'w') #[inamura 120806]

        # PHXファイルの作成
        if PHX_filename != "":
            fo_phx = open(PHX_filename, 'w')
            print(self.CommentHead + "Making PHX file... %s" % (PHX_filename))
            fo_phx.write(("%d\n" % len(Pol_list)))
            i = 1
            SS = ""
            for l2, pol, azm in zip(L2_list, Pol_list, Azm_list):
                # SS += "%8.3f%8.3f%8.3f%8.3f%8.3f%8.3f%8d\n" % (10.0,0.0,pol[0],azm[0],pol[1],azm[1],i)
                # SS += " %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f %9d\n" % (10.0,0.0,pol[0],azm[0],pol[1],azm[1],i)
                # SS += " %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f %9d\n" % (10.0,0.0,pol[0],azm[0],pol[1]*2.0,azm[1]*2.0,i)  ##[inamura 120511]
                SS += " %9.4f %9.4f %9.4f %9.4f %9.4f %9.4f %9d\n" % (l2, 0.0, pol[0], azm[0], (pol[1] * 2.0), (azm[1] * 2.0), i)
                i += 1
            fo_phx.write(SS)
            fo_phx.close()

        # [inamura 120806]-->
        # PAR file
        if PAR_filename != "":
            fo_par = open(PAR_filename, 'w')
            if len(Def_Pixel_Size) < 2:
                hh = dat.PutHeaderPointer()
                if hh.CheckKey("TypicalPixelSize") == 0:
                    msg = "TreatSPE : There is no pixel size in given data"
                    raise UserWarning(msg)
                ps = hh.PutDoubleVector("TypicalPixelSize")
                Def_Pixel_Size = [ps[0], ps[1], ps[2]]

            print("len(Def_Pixel_Size)={}".format(len(Def_Pixel_Size)))
            if len(Def_Pixel_Size) >= 2:
                det_width = Def_Pixel_Size[1] / 1000.0  # [mm]->[m]
                det_height = Def_Pixel_Size[0] / 1000.0  # [mm]->[m]
                print(self.CommentHead + "Making PAR file... {}".format(PAR_filename))
                fo_par.write("%d\n" % len(L2_list))
                i = 1
                SS = ""
                for j in range(len(L2_list)):
                    # tmp = ( L2_list[j], Pol_list[j][0], Azm_list[j][0], det_width, det_height, i )
                    tmp = (L2_list[j], Pol_list[j][0], -1.0 * Azm_list[j][0], det_width, det_height, i)
                    SS += "%9.4f\t%9.4f\t%9.4f\t%9.4f\t%9.4f\t%9d\n" % tmp
                    i += 1
                fo_par.write(SS)
            fo_par.close()
        # <--[inamura 120806]

        # SPEファイルの作成
        print(self.CommentHead + "Making SPE file... %s" % (SPE_filename))
        fo_spe = open(SPE_filename, 'w')
        fo_spe.write(("%d %d\n" % (numQ, (len(hw_list) - 1))))

        fo_spe.write("### Phi Grid\n")

        SS = self._MakeOneGroup(phi_list)
        fo_spe.write(SS)

        fo_spe.write("### Energy Grid\n")
        SS = self._MakeOneGroup(hw_list)
        fo_spe.write(SS)

        counts = 0
        total_num = len(Int_list)
        for intensity, error in zip(Int_list, Err_list):
            # 進捗状況を示す
            if ((counts % 1000) == 0):
                print(self.CommentHead + "  %10d/%d" % (counts, total_num))
            SS = self._MakeOneGroup(intensity)
            fo_spe.write("### S(Phi,w)\n")
            fo_spe.write(SS)

            SS = self._MakeOneGroup(error)
            fo_spe.write("### Errors\n")
            fo_spe.write(SS)
            counts += 1

        print(self.CommentHead + "  %10d/%d" % (counts, total_num))
        fo_spe.close()

        print(self.CommentHead + ".....completed.")

    # [inamura 160804]-->
    def _LoadOneBlock(self, SS):
        """
        SPEファイル内で使用される8列のテキストフォーマットをデコードする
        @param   SS (String LIST) 文字列データ
        @retval  (LIST) 数値データ
        """
        ret_list = []
        for a_line in SS:
            ind = 0
            while(True):
                if len(a_line) < (10 * ind + 10):
                    break
                ret_list.append(float(a_line[(10 * ind):(10 * ind + 10)]))
                ind += 1

        return ret_list

    def _LoadOneBlockForPhx(self, SS):
        """
        PHXファイル内で使用されるテキストフォーマットをデコードする
        @param   SS (String LIST) 文字列データ
        @retval  (LIST) 数値データ
        """
        ret_list = []
        for a_line in SS:
            ind = 0
            a_line = a_line.replace("\t", " ")
            list_line = a_line.split(" ")
            for item in list_line:
                if item != "":
                    ret_list.append(float(item))

        return ret_list

    def LoadSPE(self, spepath, Ei=100.0, num_of_pixels=100, runNo="XXX000001", phx_fullpath="default", par_fullpath="default"):
        """
        SPEファイルを読み込んでElementContainerMatrixを作成する
        SPEファイルだけを指定する
        PHXファイルやPARファイルは拡張子だけが異なるファイル名と仮定している
        @param spefile (string) full path to SPE file
        @param Ei (float) Incident Energy
        @param num_of_pixels (int)  number of pixels in one detector
        @param runNo (string) Run Number (including instrument code)
        @param phx_fullpath (string) full path to PHX file
        @param par_fullpath (string) full path to PAR file
        @retval (ElementContainerMatrix)
        """
        if not os.path.exists(spepath):
            msg = "SPE file not found ({})".format(spepath)
            raise UserWarning(msg)
            return

        spefile = spepath
        rootpath = spepath[:-4]
        phxfile = ""
        parfile = ""
        if phx_fullpath.upper() == "DEFAULT":
            if os.path.exists(rootpath + ".phx"):
                phxfile = rootpath + ".phx"
            elif os.path.exists(rootpath + ".PHX"):
                phxfile = rootpath + ".PHX"
            else:
                msg = "PHX file not found ({}.phx)".format(rootpath)
                raise UserWarning(msg)
                return
        else:
            if os.path.exists(phx_fullpath):
                phxfile = phx_fullpath
            else:
                msg = "PHX file not found ({})".format(phx_fullpath)
                raise UserWarning(msg)
                return

        if par_fullpath.upper() == "DEFAULT":
            if os.path.exists(rootpath + ".par"):
                parfile = rootpath + ".par"
            elif os.path.exists(rootpath + ".PAR"):
                parfile = rootpath + ".PAR"
            else:
                msg = "PAR file not found ({}.par)".format(rootpath)
                raise UserWarning(msg)
                return
        else:
            if os.path.exists(par_fullpath):
                parfile = par_fullpath
            else:
                msg = "PAR file not found ({})".format(par_fullpath)
                raise UserWarning(msg)
                return

        print("Load SPE file : %s" % (spefile))
        print("Load PHX file : %s" % (phxfile))
        f_spe = open(spefile, "r")
        f_phx = open(phxfile, "r")
        f_par = None
        if (parfile != ""):
            print("Load PAR file : %s" % (parfile))
            f_par = open(parfile, "r")

        L2_list = []
        Pol_list = []
        Azm_list = []
        DW_list = []
        DH_list = []

        try:
            runNo_num = int(runNo)
            runNo_code = "XXX"
        except ValueError:
            runNo_code = runNo[:3]
            runNo_num = int(runNo[3:])

        # read PHX
        numQ_phx = 0
        fst_line = f_phx.readline()
        numQ_phx = int(fst_line.strip())

        for i in range(numQ_phx):
            a_line = f_phx.readline()
            a_line_list = self._LoadOneBlockForPhx([a_line])
            Pol_list.append([a_line_list[2], a_line_list[4] / 2.0])
            Azm_list.append([a_line_list[3], a_line_list[5] / 2.0])
        f_phx.close()

        # read PAR
        if f_par is not None:
            numL2_par = 0
            fst_line = f_par.readline()
            numL2_par = int(fst_line.strip())
            if numL2_par != numQ_phx:
                msg = "PHX and PAR files are inconsistency."
                raise UserWarning(msg)
                return

            isOldForm = True
            for i in range(numL2_par):
                a_line = f_par.readline()
                if isOldForm:
                    try:
                        a_line_list = self._LoadOneBlock([a_line])
                    except:
                        mu.UtsusemiMessage("PAR file is newer format.")
                        isOldForm = False
                        a_line_list = self._LoadOneBlockForPhx([a_line])
                else:
                    a_line_list = self._LoadOneBlockForPhx([a_line])
                L2_list.append(a_line_list[0])
                Pol_list[i] = [a_line_list[1], a_line_list[3]]
                # Azm_list[i] = [a_line_list[2],a_line_list[4]]
                Azm_list[i] = [-1.0 * a_line_list[2], a_line_list[4]]
                DW_list.append(a_line_list[3])
                DH_list.append(a_line_list[4])

        # read SPE
        first_line = f_spe.readline()
        first_line_list = []
        for item in first_line.strip().split(" "):
            if item != "":
                first_line_list.append(item)

        if len(first_line_list) != 2:
            raise UserWarning("This file is not SPE file.({})".format(spefile))

        numQ = int(first_line_list[0])
        numHW_bin = int(first_line_list[1]) + 1

        if numQ != numQ_phx:
            msg = "PHX and SPE files are inconsistency."
            raise UserWarning(msg)
            return

        numOfRows_Q = int(numQ / 8)
        numOfRows_HW = int((numHW_bin - 1) / 8)
        if (numQ % 8) != 0:
            numOfRows_Q += 1
        if ((numHW_bin - 1) % 8) != 0:
            numOfRows_HW += 1

        pixelId = 0
        PHI_list = []
        HW_list = []
        int_list = []
        err_list = []

        hh = Manyo.HeaderBase()
        hh.Add("RUNNUMBER", str(runNo_num))
        hh.Add("INSTRUMENT", runNo_code)
        hh.Add("Ei", Ei)
        hh.Add("isHistogram", 0)  # [inamura 210118]
        ecm = Manyo.ElementContainerMatrix(hh)
        detId = 0
        hh = Manyo.HeaderBase()
        hh.Add("MASKED", 0)
        hh.Add("DETID", detId)
        eca = Manyo.ElementContainerArray(hh)
        while(True):
            a_line = f_spe.readline()
            if a_line == "":
                ecm.Add(eca)
                break

            if a_line.find("### Phi") == 0:
                SS = []
                for i in range(numOfRows_Q):
                    SS.append(f_spe.readline())
                PHI_list = self._LoadOneBlock(SS)

            if a_line.find("### Ene") == 0:
                SS = []
                for i in range(numOfRows_HW):
                    SS.append(f_spe.readline())
                HW_list = self._LoadOneBlock(SS)

            if a_line.find("### S(P") == 0:
                SS = []
                for i in range(numOfRows_HW):
                    SS.append(f_spe.readline())
                int_list = self._LoadOneBlock(SS)

            if a_line.find("### Err") == 0:
                SS = []
                for i in range(numOfRows_HW):
                    SS.append(f_spe.readline())
                err_list = self._LoadOneBlock(SS)

            if len(int_list) != 0 and (len(int_list) == len(err_list)):
                ec = Manyo.ElementContainer()
                hh = ec.PutHeaderPointer()
                hh.Add("PIXELID", pixelId)
                pa = Manyo.MakeDoubleVector(2)
                aa = Manyo.MakeDoubleVector(2)
                pa[0] = Pol_list[pixelId][0]
                pa[1] = Pol_list[pixelId][1]
                aa[0] = Azm_list[pixelId][0]
                aa[1] = Azm_list[pixelId][1]
                hh.Add("PixelPolarAngle", pa)
                hh.Add("PixelAzimAngle", aa)
                hh.Add("DETID", detId)
                hh.Add("MASKED", 0)

                # [inamura 210118] removed
                # deltaHW = HW_list[1]-HW_list[0]
                # for i in range(len(int_list)):
                #    int_list[i] = int_list[i]*deltaHW

                ec.Add("Energy", HW_list, "meV")
                ec.Add("Intensity", int_list, "counts")
                ec.Add("Error", err_list, "counts")
                ec.SetKeys("Energy", "Intensity", "Error")

                int_list = []
                err_list = []

                eca.Add(ec)
                pixelId += 1

            if pixelId != 0 and pixelId % num_of_pixels == 0 and eca.PutSize() != 0:
                ecm.Add(eca)
                detId += 1

                hh = Manyo.HeaderBase()
                hh.Add("MASKED", 0)
                hh.Add("DETID", detId)
                eca = Manyo.ElementContainerArray(hh)

        return ecm
    # <--[inamura 160804]
