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

from __future__ import print_function
import time
import Manyo as mm
import Manyo.Utsusemi as mu
import utsusemi.ana.Reduction.BaseCommands as BaseCom
import os
# special reserved word for commands history
HISTORY = None

# reserved words for return value in this.
DAT = None
RetECA = None
ret = None


#########################################
def CopyContainers(dat=DAT):
    """
    Copy data in SequenceEditer
    @param dat [Def:DAT] (ElementContainer,-Array,-Matrix, List)
    @retval ret (ElementContainer,-Array,-Matrix, List)
    """
    if isinstance(dat, mm.ElementContainer):
        ret = mm.ElementContainer(dat)
    if isinstance(dat, mm.ElementContainerArray):
        ret = mm.ElementContainerArray(dat)
    if isinstance(dat, mm.ElementContainerMatrix):
        ret = mm.ElementContainerMatrix(dat)
    if isinstance(dat, list):
        ret = []
        for item in dat:
            ret.append(item)

    return ret

##########################################


def CalcContainers(dat1=DAT, dat2=DAT, coef1=1.0, coef2=0.0, ope="+", pow1=1.0, pow2=1.0):
    """
    Execute caluculation ret = coef1*dat1 + coef2*dat2
    @param  dat1 [Def:DAT] (ElementContainerMatrix or Array)  first data
    @param  dat2 [Def:DAT] (ElementContainerMatrix or Array)  second data
    @param  coef1  (float)    coefficient of dat1
    @param  coef2  (float)    coefficient of dat2
    @param  ope    (str)      operator
    @param  pow1   (float)    power value for dat1
    @param  pow2   (float)    power value for dat2
    @retval ret    Return Value
    """

    if (coef1 == 0.0) and (coef2 == 0.0):

        raise UserWarning("Both Coefficents are 0.")

    if (coef1 < 0.0) or (coef2 < 0.0):
        raise UserWarning("Coefficents must be positive number.")

    if not (ope in ["+", "-", "*", "/"]):
        raise UserWarning("ope should be +,-,*,/ .")

    if pow1 <= 0.0 or pow2 <= 0.0:
        raise UserWarning("pow1 and pow2 should be over 0.")

    isUsePow = False
    if pow1 != 1.0 or pow2 != 1.0:
        isUsePow = True

    if (coef1 != 0.0) and (coef2 != 0.0):
        if type(dat1) != type(dat2):
            mu.UtsusemiError("Arguments should be same type.")
            raise UserWarning("Arguments should be same type.")

        if isinstance(dat1, mm.ElementContainerMatrix):
            ret = mm.ElementContainerMatrix(dat1.PutHeader())
            tt = mu.UtsusemiCalcContainers()
            if isUsePow:
                tt.CalcContainers(ret, coef1, dat1, pow1, str(ope), coef2, dat2, pow2)
            else:
                tt.CalcContainers(ret, coef1, dat1, str(ope), coef2, dat2)
            del tt
            """
            ret = mm.ElementContainerMatrix(dat1.PutHeader())
            for i in range(dat1.PutTableSize()):
                eca = mm.ElementContainerArray(dat1(i).PutHeader())
                for j in range(dat1(i).PutTableSize()):
                    ec1 = dat1(i,j)
                    ec2 = dat2(i,j)
                    if (ope=="+"):
                        ec = ec1.Mul(coef1)+ec2.Mul(coef2)
                    elif (ope=="-"):
                        ec = ec1.Mul(coef1)-ec2.Mul(coef2)
                    elif (ope=="*"):
                        ec = ec1.Mul(coef1)*ec2.Mul(coef2)
                    else:
                        ec = ec1.Mul(coef1)/ec2.Mul(coef2)

                    ec.InputHeader(dat1(i,j).PutHeader())
                    if ec.PutHeaderPointer().CheckKey("TotalCounts")==1:
                        ec.PutHeaderPointer().OverWrite("TotalCounts",sum(ec.PutYList()))
                    else:
                        ec.AddToHeader("TotalCounts",sum(ec.PutYList()))
                    eca.Add(ec)
                ret.Add(eca)
            """
        if isinstance(dat1, mm.ElementContainerArray):
            ret = mm.ElementContainerArray(dat1.PutHeader())
            tt = mu.UtsusemiCalcContainers()
            if isUsePow:
                tt.CalcContainers(ret, coef1, dat1, pow1, str(ope), coef2, dat2, pow2)
            else:
                tt.CalcContainers(ret, coef1, dat1, str(ope), coef2, dat2)
            del tt
            """
            for i in range(dat1.PutTableSize()):
                ec1=dat1(i)
                ec2=dat2(i)
                if (ope=="+"):
                    ec = ec1.Mul(coef1)+ec2.Mul(coef2)
                elif (ope=="-"):
                    ec = ec1.Mul(coef1)-ec2.Mul(coef2)
                elif (ope=="*"):
                    ec = ec1.Mul(coef1)*ec2.Mul(coef2)
                else:
                    ec = ec1.Mul(coef1)/ec2.Mul(coef2)

                ec.InputHeader(dat1(i).PutHeader())
                if ec.PutHeaderPointer().CheckKey("TotalCounts")==1:
                    ec.PutHeaderPointer().OverWrite("TotalCounts",sum(ec.PutYList()))
                else:
                    ec.AddToHeader("TotalCounts",sum(ec.PutYList()))
                ret.Add(ec)
            """
        if isinstance(dat1, mm.ElementContainer):
            if isUsePow:
                d1 = dat1.Pow(pow1)
                d2 = dat2.Pow(pow2)
            else:
                d1 = dat1
                d2 = dat2
            ec = None
            if (ope == "+"):
                ec = d1.Mul(coef1) + d2.Mul(coef2)
            elif (ope == "-"):
                ec = d1.Mul(coef1) - d2.Mul(coef2)
            elif (ope == "*"):
                ec = d1.Mul(coef1) * d2.Mul(coef2)
            else:
                ec = d1.Mul(coef1) / d2.Mul(coef2)

            return ec

        if isinstance(dat1, list):
            if len(dat1) == 0:
                raise UserWarning("Argument1 is empty list")
            if not isinstance(dat1[0], mm.ElementContainer):
                raise UserWarning("Argument1 has no ElementContainer")
            if len(dat1) != len(dat2):
                raise UserWarning(
                    "The number of ElmentContainer in Argument1 and Argument2 are different")

            ret = []
            for ec1o, ec2o in zip(dat1, dat2):
                if isinstance(ec1o, mm.ElementContainer) and isinstance(ec2o, mm.ElementContainer):
                    if isUsePow:
                        ec1 = ec1o.Pow(pow1)
                        ec2 = ec2o.Pow(pow2)
                    else:
                        ec1 = ec1o
                        ec2 = ec2o
                    if (ope == "+"):
                        ec = ec1.Mul(coef1) + ec2.Mul(coef2)
                    elif (ope == "-"):
                        ec = ec1.Mul(coef1) - ec2.Mul(coef2)
                    elif (ope == "*"):
                        ec = ec1.Mul(coef1) * ec2.Mul(coef2)
                    else:
                        ec = ec1.Mul(coef1) / ec2.Mul(coef2)

                    ret.append(ec)
                else:
                    try:
                        if (ope == "+"):
                            ec = ec1 * coef1 + ec2 * coef2
                        elif (ope == "-"):
                            ec = ec1 * coef1 - ec2 * coef2
                        elif (ope == "*"):
                            ec = ec1 * coef1 * ec2 * coef2
                        else:
                            ec = (ec1 * coef1) / (ec2 * coef2)
                    except:
                        raise UserWarning("Arguments are NOT calculable")

                    ret.append(ec)
        return ret

    if (coef1 == 0):
        if isinstance(dat2, list):
            ret = []
            for ec2 in dat2:
                if isinstance(ec2, mm.ElementContainer):
                    ec2_temp = ec2.Pow(pow2)
                    ret.append(ec2_temp.Mul(coef2))
        else:
            dat2_temp = dat2.Pow(pow2)
            ret = dat2_temp.Mul(coef2)
        return ret

    if (coef2 == 0):
        if isinstance(dat1, list):
            ret = []
            for ec1 in dat1:
                if isinstance(ec1, mm.ElementContainer):
                    ec1_temp = ec1.Pow(pow1)
                    ret.append(ec1_temp.Mul(coef1))
        else:
            dat1_temp = dat1.Pow(pow1)
            ret = dat1_temp.Mul(coef1)
        return ret


#########################################
def SearchHeaderInteger(Target=DAT, HeaderKey="PIXELID", Min=0, Max=0):
    """
    Search in each ElementContainer with Header with HeaderKey and its Value from Matrix.
    @param  Target [Def:DAT]     ElementContainerMatrix
    @param  HeaderKey  string Key of Integer Header for search
    @param  Min        minimun value for search
    @param  Max        maximum value for search
    @retval RetECA     search results in ElementContainerArray
    """

    if isinstance(Target, mm.ElementContainerMatrix):
        ECM = Target
    else:
        mu.UtsusemiError("This Target is NOT suitable argument!")
        return None

    if ((HeaderKey == "") and (Max == "")):
        return None

    sh = mm.SearchInHeader(ECM)

    if (Min == Max):
        sh.Search(HeaderKey, Min)
    else:
        sh.Search(HeaderKey, Min, Max)

    RetECA = sh.PutResultAsArray()
    if (RetECA.PutTableSize() == 0):
        mu.UtsusemiWarning("No data corresponding with given parameter")

    del sh
    return RetECA

#########################################


def SearchHeaderDouble(Target=DAT, HeaderKey="", Min=0.0, Max=0.0):
    """
    Search in each ElementContainer with Header with HeaderKey and its Value from Matrix.
    @param  Target [Def:DAT]     ElementContainerMatrix
    @param  HeaderKey  string Key of Double Header for search
    @param  Min        minimun value for search
    @param  Max        maximum value for search
    @retval RetECA     search results in ElementContainerArray
    """

    if isinstance(Target, mm.ElementContainerMatrix):
        ECM = Target
    else:
        mu.UtsusemiError("This Target is NOT suitable argument!")
        return None

    if ((HeaderKey == "") and (Max == "")):
        return None

    sh = mm.SearchInHeaders()
    sh.SetTarget(ECM)
    if (Min == Max):
        sh.Search(HeaderKey, Min)
    else:
        sh.Search(HeaderKey, Min, Max)

    RetECA = sh.PutCurrent()
    if (RetECA.PutTableSize() == 0):
        mu.UtsusemiWarning("No data corresponding with given parameter")

    return RetECA


#########################################
def ExecCode(Code="A.Dump()", A=DAT):
    """
    Execute given Code : Input code "print 1" means to execute "print 1" at console
    @param  Code     string  Equation using A
    @param  A [Def:DAT] 1st Variable (ElementContainer, some variables)
    """

    exec(Code)
    return


#########################################
def ExecFunctionCode(Code="A+B", A=DAT, B=DAT):
    """
    Execute given Code : Input code "A+B" means to execute "RET=A+B"
    @param  Code     string  Equation using A and B
    @param  A [Def:DAT] 1st Variable (ElementContainer, some variables)
    @param  B [Def:DAT] 2nd Variable (ElementContainer, some variables)
    @retval ret      Return Value
    """

    # exe_code = "ret="+Code
    # exec(exe_code)
    ret = eval(Code)
    return ret


#########################################
def ExecCodeManyArgs(Code="A.Dump()", A=DAT, B=DAT, C=DAT, D=DAT, E=DAT, F=DAT):
    """
    Execute given Code : Input code "print 1" means to execute "print 1" at console
    @param  Code        string  Equation using A
    @param  A [Def:DAT]  1st Variable (ElementContainer, some variables)
    @param  B [Def:EMPTYDAT]  2nd Variable (ElementContainer, some variables)
    @param  C [Def:EMPTYDAT]  3rd Variable (ElementContainer, some variables)
    @param  D [Def:EMPTYDAT]  4th Variable (ElementContainer, some variables)
    @param  E [Def:EMPTYDAT]  5th Variable (ElementContainer, some variables)
    @param  F [Def:EMPTYDAT]  6th Variable (ElementContainer, some variables)
    """

    exec(Code)
    return None

#########################################


def ExecFunctionCodeManyArgs(Code="A+B", A=DAT, B=DAT, C=DAT, D=DAT, E=DAT, F=DAT):
    """
    Execute given Code : Input code "A+B" means to execute "RET=A+B"
    @param  Code        string  Equation using A and B
    @param  A [Def:DAT]  1st Variable (ElementContainer, some variables)
    @param  B [Def:EMPTYDAT]  2nd Variable (ElementContainer, some variables)
    @param  C [Def:EMPTYDAT]  3rd Variable (ElementContainer, some variables)
    @param  D [Def:EMPTYDAT]  4th Variable (ElementContainer, some variables)
    @param  E [Def:EMPTYDAT]  5th Variable (ElementContainer, some variables)
    @param  F [Def:EMPTYDAT]  6th Variable (ElementContainer, some variables)
    @retval ret      Return Value
    """

    # exe_code = "ret="+Code
    # exec(exe_code)
    ret = eval(Code)
    return ret


def MakeECList(DAT=DAT, Min=0, Max=0):
    """
    Make list of ElementContainers from ElementContainerArray
    @param DAT [Def:DAT] ElementContainerArray
    @param Min
    @param Max
    @retval ecs (list of ElementContainers)
    """
    ret = []
    if isinstance(DAT, mm.ElementContainerArray):
        if Min == 0 and Max == 0:
            for i in range(DAT.PutSize()):
                ret.append(DAT.Put(i))
        else:
            for i in range(Min, (Max + 1)):
                ret.append(DAT.Put(i))
    return ret

#########################################


def ArrayDataCut(Dat=DAT, Axis="X", Xkey="Default", IntegRange="All", Ave=True, AxisRange="All"):
    """
    Cut the given data (ElementContainerArray)
    If given data is ElementContainerMatrix, first ElementContainerArray is used for cutting
    @param  Dat [Def:DAT]   data
    @param  AxisX (string) axis of cut data. can use a key of vector stored in header of ElementContainer
    @param  IntegRange (string) range of integral on data "All" or "<min>,<max>"
    @param  Ave (bool) True : cut intensity is averaging, False : summation.
    @param  AxisRange (string) : range of axis "All" or "<min>,<max>"
    @retval EC (ElementContaienr)
    """
    BaseCom.ArrayDataCut(Dat, Axis, Xkey, IntegRange, Ave, AxisRange)

#########################################


def IntegPixels(Target=DAT, PSD_min=1, PSD_max=1, Pixel_min=1, Pixel_max=1, isAve=True):
    """
    Put summation of intensity along Pixel
    @param Target [Def:DAT] (ElementContainerMatrix)
    @param PSD_min   (int) lower limit of PSD-ID range
    @param PSD_max   (int) upper limit of PSD-ID range
    @param Pixel_min (int) lower limit of Pixel range
    @param Pixel_max (int) upper limit of Pixel range
    @param isAve     (bool) averaging or summation
    @retval EC ElementContainer
    """
    return BaseCom.IntegPixels(Target, PSD_min, PSD_max, Pixel_min, Pixel_max, isAve)


# Dictionary for entry the name of functions
_functions = {
    "CopyContainers": CopyContainers,
    "CalcContainers": CalcContainers,
    "IntegPixels": IntegPixels,
    "ArrayDataCut": ArrayDataCut,
    "SearchHeaderInteger": SearchHeaderInteger,
    "SearchHeaderDouble": SearchHeaderDouble,
    "ExecCode": ExecCode,
    "ExecFunctionCode": ExecFunctionCode,
    "ExecCodeManyArgs": ExecCodeManyArgs,
    "ExecFunctionCodeManyArgs": ExecFunctionCodeManyArgs,
    "MakeECList": MakeECList
}

_functionsOrder = ["CopyContainers",
                   "CalcContainers",
                   "IntegPixels",
                   "ArrayDataCut",
                   "SearchHeaderInteger",
                   "SearchHeaderDouble",
                   "ExecCode",
                   "ExecFunctionCode",
                   "ExecCodeManyArgs",
                   "ExecFunctionCodeManyArgs", "MakeECList"]
