#
#  --------------------------------------------------------------------------
#   Gurux Ltd
#
#
#
#  Filename: $HeadURL$
#
#  Version: $Revision$,
#                   $Date$
#                   $Author$
#
#  Copyright (c) Gurux Ltd
#
# ---------------------------------------------------------------------------
#
#   DESCRIPTION
#
#  This file is a part of Gurux Device Framework.
#
#  Gurux Device Framework is Open Source software; you can redistribute it
#  and/or modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; version 2 of the License.
#  Gurux Device Framework is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#  See the GNU General Public License for more details.
#
#  More information of Gurux products: http://www.gurux.org
#
#  This code is licensed under the GNU General Public License v2.
#  Full text may be retrieved at http://www.gnu.org/licenses/gpl-2.0.txt
# ---------------------------------------------------------------------------
from __future__ import print_function
import pkg_resources
from .enums import Standard, ObjectType, DataType
from .GXStandardObisCodeCollection import GXStandardObisCodeCollection
from .GXStandardObisCode import GXStandardObisCode
from .internal._GXCommon import _GXCommon
from .GXDate import GXDate
from .GXDateTime import GXDateTime
from .GXTime import GXTime
from .GXByteBuffer import GXByteBuffer
from .manufacturersettings.GXObisCode import GXObisCode

###Python 2 requires this
#pylint: disable=bad-option-value,old-style-class
class GXDLMSConverter:
    #
    # Constructor.
    #
    # value: Used standard.
    #
    def __init__(self, value=Standard.DLMS):
        self.standard = value
        # Collection of standard OBIS codes.
        self.codes = GXStandardObisCodeCollection()

    #
    # Get OBIS code description.
    #
    # @param logicalName
    # Logical name (OBIS code).
    # @param type
    # Object type.
    # @param description
    # Description filter.
    # Array of descriptions that match given OBIS code.
    #
    def getDescription(self, logicalName, type_=ObjectType.NONE, description=None):
        if not self.codes:
            self.__readStandardObisInfo(self.standard, self.codes)
        list_ = list()
        all_ = not logicalName
        for it in self.codes.find(logicalName, type_):
            if description and not it.description.lower().contains(description.lower()):
                continue
            if all_:
                list_.append("A=" + it.getOBIS()[0] + ", B=" + it.getOBIS()[1] + ", C=" + it.getOBIS()[2] + ", D=" + it.getOBIS()[3] + ", E=" + it.getOBIS()[4] + ", F=" + it.getOBIS()[5] + "\r\n" + it.description)
            else:
                list_.append(it.description)
        return list_

    #pylint: disable=too-many-boolean-expressions
    @classmethod
    def __updateOBISCodeInfo(cls, codes, it):
        ln = it.logicalName
        code_ = codes.find(ln, it.objectType)[0]
        if code_:
            if not it.description:
                it.description = code_.description
            if "10" in code_.dataType:
                code_.uiDataType = "10"
            elif "25" in code_.dataType or "26" in code_.dataType:
                code_.uiDataType = code_.dataType = "25"
            elif "9" in code_.dataType:
                if (GXStandardObisCodeCollection.equalsMask2("0.0-64.96.7.10-14.255", ln) or GXStandardObisCodeCollection.equalsMask2("0.0-64.0.1.5.0-99,255", ln) or GXStandardObisCodeCollection.equalsMask2("0.0-64.0.1.2.0-99,255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.1.2.0-99,255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.1.5.0-99,255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.0.255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.6.255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.7.255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.13.255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.14.255", ln) or GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.15.255", ln)):
                    code_.uiDataType = "25"
                #Local time
                elif GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.1.255", ln):
                    code_.uiDataType = "27"
                #Local date
                elif GXStandardObisCodeCollection.equalsMask2("1.0-64.0.9.2.255", ln):
                    code_.uiDataType = "26"
                #Active firmware identifier
                elif GXStandardObisCodeCollection.equalsMask2("1.0.0.2.0.255", ln):
                    code_.uiDataType = "10"
            #Unix time
            elif it.objectType == ObjectType.DATA and GXStandardObisCodeCollection.equalsMask2("0.0.1.1.0.255", it.logicalName):
                code_.uiDataType = "25"

            if not code_.dataType == "*" and not code_.dataType == "" and "," not in code_.dataType:
                tp = int(code_.dataType)
                if it.objectType in (ObjectType.DATA, ObjectType.REGISTER, ObjectType.REGISTER_ACTIVATION, ObjectType.EXTENDED_REGISTER):
                    it.setDataType(2, tp)
            if code_.uiDataType:
                tp = int(code_.uiDataType)
                if it.objectType in (ObjectType.DATA, ObjectType.REGISTER, ObjectType.REGISTER_ACTIVATION, ObjectType.EXTENDED_REGISTER):
                    it.setUIDataType(2, tp)
        else:
            print("Unknown OBIS Code: " + it.logicalName + " Type: " + it.objectType)

    def updateOBISCodeInformation(self, objects):
        if not self.codes:
            self.__readStandardObisInfo(self.standard, self.codes)
        if isinstance(objects, list):
            for it in objects:
                self.__updateOBISCodeInfo(self.codes, it)
        else:
            self.__updateOBISCodeInfo(self.codes, objects)

    @classmethod
    def __getObjects(cls, standard):
        codes = list()
        if standard == Standard.ITALY:
            str_ = pkg_resources.resource_string(__name__, "Italy.txt").decode("utf-8")
        elif standard == Standard.INDIA:
            str_ = pkg_resources.resource_string(__name__, "India.txt").decode("utf-8")
        elif standard == Standard.SAUDI_ARABIA:
            str_ = pkg_resources.resource_string(__name__, "SaudiArabia.txt").decode("utf-8")
        if not str_:
            return None
        str_ = str_.replace("\n", "\r")
        rows = str_.split('\r')
        for it in rows:
            if it and not it.startswith("#"):
                items = it.split(';')
                if len(items) > 1:
                    ot = int(items[0])
                    ln = _GXCommon.toLogicalName(_GXCommon.logicalNameToBytes(items[1]))
                    version = int(items[2])
                    desc = items[3]
                    code_ = GXObisCode(ln, ot, 0, desc)
                    code_.version = version
                    codes.append(code_)
        return codes

    @classmethod
    def __readStandardObisInfo(cls, standard, codes):
        if standard != Standard.DLMS:
            for it in cls.__getObjects(standard):
                tmp = GXStandardObisCode(it.logicalName.split('.'))
                tmp.interfaces = str(it.objectType)
                tmp.description = it.description
                codes.append(tmp)

        str_ = pkg_resources.resource_string(__name__, "OBISCodes.txt").decode("utf-8")
        str_ = str_.replace("\n", "\r")
        rows = str_.split('\r')
        for it in rows:
            if it and not it.startswith("#"):
                items = it.split(';')
                obis = items[0].split('.')
                try:
                    code_ = GXStandardObisCode(obis, str(items[3]) + "; " + str(items[4]) + "; " + str(items[5]) + "; " + str(items[6]) + "; " + str(items[7]), str(items[1]), str(items[2]))
                    codes.append(code_)
                except UnicodeEncodeError:
                    pass

    @classmethod
    def changeType(cls, value, type_):
        if _GXCommon.getDLMSDataType(value) == type_:
            return value
        if type_ == DataType.ARRAY:
            raise ValueError("Can't change array types.")
        if type_ == DataType.BCD:
            ret = int(value)
        elif type_ == DataType.BOOLEAN:
            ret = bool(value)
        elif type_ == DataType.COMPACT_ARRAY:
            raise ValueError("Can't change compact array types.")
        elif type_ == DataType.DATE:
            ret = GXDate(value)
        elif type_ == DataType.DATETIME:
            ret = GXDateTime(value)
        elif type_ == DataType.ENUM:
            raise ValueError("Can't change enumeration types.")
        elif type_ == DataType.FLOAT32:
            ret = float(value)
        elif type_ == DataType.FLOAT64:
            ret = float(value)
        elif type_ == DataType.INT16:
            ret = int(value)
        elif type_ == DataType.INT32:
            ret = int(value)
        elif type_ == DataType.INT64:
            ret = int(value)
        elif type_ == DataType.INT8:
            ret = int(value)
        elif type_ == DataType.NONE:
            ret = None
        elif type_ == DataType.OCTET_STRING:
            if isinstance(value, str):
                ret = GXByteBuffer.hexToBytes(str(value))
            else:
                raise ValueError("Can't change octect string type.")
        elif type_ == DataType.STRING:
            ret = str(value)
        elif type_ == DataType.BITSTRING:
            ret = str(value)
        elif type_ == DataType.STRING_UTF8:
            ret = str(value)
        elif type_ == DataType.STRUCTURE:
            raise ValueError("Can't change structure types.")
        elif type_ == DataType.TIME:
            ret = GXTime(value)
        elif type_ == DataType.UINT16:
            ret = int(value)
        elif type_ == DataType.UINT32:
            ret = int(value)
        elif type_ == DataType.UINT64:
            ret = int(value)
        elif type_ == DataType.UINT8:
            ret = int(value)
        else:
            raise ValueError('Invalid data type.')
        return ret

    @classmethod
    def objectTypeToString(cls, ot):
        if ot == ObjectType.ACTION_SCHEDULE:
            ret = "ActionSchedule"
        elif ot == ObjectType.ACTIVITY_CALENDAR:
            ret = "ActivityCalendar"
        elif ot == ObjectType.ASSOCIATION_LOGICAL_NAME:
            ret = "AssociationLogicalName"
        elif ot == ObjectType.ASSOCIATION_SHORT_NAME:
            ret = "AssociationShortName"
        elif ot == ObjectType.AUTO_ANSWER:
            ret = "AutoAnswer"
        elif ot == ObjectType.AUTO_CONNECT:
            ret = "AutoConnect"
        elif ot == ObjectType.CLOCK:
            ret = "Clock"
        elif ot == ObjectType.DATA:
            ret = "Data"
        elif ot == ObjectType.DEMAND_REGISTER:
            ret = "DemandRegister"
        elif ot == ObjectType.MAC_ADDRESS_SETUP:
            ret = "MacAddressSetup"
        elif ot == ObjectType.EXTENDED_REGISTER:
            ret = "ExtendedRegister"
        elif ot == ObjectType.GPRS_SETUP:
            ret = "GprsSetup"
        elif ot == ObjectType.SECURITY_SETUP:
            ret = "SecuritySetup"
        elif ot == ObjectType.IEC_HDLC_SETUP:
            ret = "IecHdlcSetup"
        elif ot == ObjectType.IEC_LOCAL_PORT_SETUP:
            ret = "IECOpticalPortSetup"
        elif ot == ObjectType.IEC_TWISTED_PAIR_SETUP:
            ret = "IEC_TWISTED_PAIR_SETUP"
        elif ot == ObjectType.IP4_SETUP:
            ret = "Ip4Setup"
        elif ot == ObjectType.MBUS_SLAVE_PORT_SETUP:
            ret = "MBusSlavePortSetup"
        elif ot == ObjectType.IMAGE_TRANSFER:
            ret = "ImageTransfer"
        elif ot == ObjectType.DISCONNECT_CONTROL:
            ret = "DisconnectControl"
        elif ot == ObjectType.LIMITER:
            ret = "Limiter"
        elif ot == ObjectType.MBUS_CLIENT:
            ret = "MBusClient"
        elif ot == ObjectType.MODEM_CONFIGURATION:
            ret = "ModemConfiguration"
        elif ot == ObjectType.PPP_SETUP:
            ret = "PppSetup"
        elif ot == ObjectType.PROFILE_GENERIC:
            ret = "ProfileGeneric"
        elif ot == ObjectType.REGISTER:
            ret = "Register"
        elif ot == ObjectType.REGISTER_ACTIVATION:
            ret = "RegisterActivation"
        elif ot == ObjectType.REGISTER_MONITOR:
            ret = "RegisterMonitor"
        elif ot == ObjectType.REGISTER_TABLE:
            ret = "RegisterTable"
        elif ot == ObjectType.ZIG_BEE_SAS_STARTUP:
            ret = "ZigBeeSasStartup"
        elif ot == ObjectType.ZIG_BEE_SAS_JOIN:
            ret = "ZigBeeSasJoin"
        elif ot == ObjectType.ZIG_BEE_SAS_APS_FRAGMENTATION:
            ret = "ZigBeeSasApsFragmentation"
        elif ot == ObjectType.ZIG_BEE_NETWORK_CONTROL:
            ret = "ZigBeeNetworkControl"
        elif ot == ObjectType.SAP_ASSIGNMENT:
            ret = "SapAssignment"
        elif ot == ObjectType.SCHEDULE:
            ret = "Schedule"
        elif ot == ObjectType.SCRIPT_TABLE:
            ret = "ScriptTable"
        elif ot == ObjectType.SMTP_SETUP:
            ret = "SMTPSetup"
        elif ot == ObjectType.SPECIAL_DAYS_TABLE:
            ret = "SpecialDaysTable"
        elif ot == ObjectType.STATUS_MAPPING:
            ret = "StatusMapping"
        elif ot == ObjectType.TCP_UDP_SETUP:
            ret = "TcpUdpSetup"
        elif ot == ObjectType.UTILITY_TABLES:
            ret = "UtilityTables"
        elif ot == ObjectType.MBUS_MASTER_PORT_SETUP:
            ret = "MBusMasterPortSetup"
        elif ot == ObjectType.PUSH_SETUP:
            ret = "PushSetup"
        elif ot == ObjectType.ACCOUNT:
            ret = "Account"
        elif ot == ObjectType.CREDIT:
            ret = "Credit"
        elif ot == ObjectType.CHARGE:
            ret = "Charge"
        elif ot == ObjectType.PARAMETER_MONITOR:
            ret = "ParameterMonitor"
        elif ot == ObjectType.TOKEN_GATEWAY:
            ret = "TokenGateway"
        elif ot == ObjectType.GSM_DIAGNOSTIC:
            ret = "GSMDiagnostic"
        elif ot == ObjectType.COMPACT_DATA:
            ret = "CompactData"
        elif ot == ObjectType.IP6_SETUP:
            ret = "Ip6Setup"
        else:
            ret = "Manufacture spesific."
        return ret

    @classmethod
    def valueOfObjectType(cls, value):
        if value == "ActionSchedule":
            ot = ObjectType.ACTION_SCHEDULE
        elif value == "ActivityCalendar":
            ot = ObjectType.ACTIVITY_CALENDAR
        elif value == "AssociationLogicalName":
            ot = ObjectType.ASSOCIATION_LOGICAL_NAME
        elif value == "AssociationShortName":
            ot = ObjectType.ASSOCIATION_SHORT_NAME
        elif value == "AutoAnswer":
            ot = ObjectType.AUTO_ANSWER
        elif value == "AutoConnect":
            ot = ObjectType.AUTO_CONNECT
        elif value == "Clock":
            ot = ObjectType.CLOCK
        elif value == "Data":
            ot = ObjectType.DATA
        elif value == "DemandRegister":
            ot = ObjectType.DEMAND_REGISTER
        elif value == "MacAddressSetup":
            ot = ObjectType.MAC_ADDRESS_SETUP
        elif value == "ExtendedRegister":
            ot = ObjectType.EXTENDED_REGISTER
        elif value == "GprsSetup":
            ot = ObjectType.GPRS_SETUP
        elif value == "SecuritySetup":
            ot = ObjectType.SECURITY_SETUP
        elif value == "IecHdlcSetup" or value == "HdlcSetup":
            ot = ObjectType.IEC_HDLC_SETUP
        elif value == "IECOpticalPortSetup":
            ot = ObjectType.IEC_LOCAL_PORT_SETUP
        elif value == "IEC_TWISTED_PAIR_SETUP":
            ot = ObjectType.IEC_TWISTED_PAIR_SETUP
        elif value == "Ip4Setup":
            ot = ObjectType.IP4_SETUP
        elif value == "MBusSlavePortSetup":
            ot = ObjectType.MBUS_SLAVE_PORT_SETUP
        elif value == "ImageTransfer":
            ot = ObjectType.IMAGE_TRANSFER
        elif value == "DisconnectControl":
            ot = ObjectType.DISCONNECT_CONTROL
        elif value == "Limiter":
            ot = ObjectType.LIMITER
        elif value == "MBusClient":
            ot = ObjectType.MBUS_CLIENT
        elif value == "ModemConfiguration":
            ot = ObjectType.MODEM_CONFIGURATION
        elif value == "PppSetup":
            ot = ObjectType.PPP_SETUP
        elif value == "ProfileGeneric":
            ot = ObjectType.PROFILE_GENERIC
        elif value == "Register":
            ot = ObjectType.REGISTER
        elif value == "RegisterActivation":
            ot = ObjectType.REGISTER_ACTIVATION
        elif value == "RegisterMonitor":
            ot = ObjectType.REGISTER_MONITOR
        elif value == "RegisterTable":
            ot = ObjectType.REGISTER_TABLE
        elif value == "ZigBeeSasStartup":
            ot = ObjectType.ZIG_BEE_SAS_STARTUP
        elif value == "ZigBeeSasJoin":
            ot = ObjectType.ZIG_BEE_SAS_JOIN
        elif value == "ZigBeeSasApsFragmentation":
            ot = ObjectType.ZIG_BEE_SAS_APS_FRAGMENTATION
        elif value == "ZigBeeNetworkControl":
            ot = ObjectType.ZIG_BEE_NETWORK_CONTROL
        elif value == "SapAssignment":
            ot = ObjectType.SAP_ASSIGNMENT
        elif value == "Schedule":
            ot = ObjectType.SCHEDULE
        elif value == "ScriptTable":
            ot = ObjectType.SCRIPT_TABLE
        elif value == "SMTPSetup":
            ot = ObjectType.SMTP_SETUP
        elif value == "SpecialDaysTable":
            ot = ObjectType.SPECIAL_DAYS_TABLE
        elif value == "StatusMapping":
            ot = ObjectType.STATUS_MAPPING
        elif value == "TcpUdpSetup":
            ot = ObjectType.TCP_UDP_SETUP
        elif value == "UtilityTables":
            ot = ObjectType.UTILITY_TABLES
        elif value == "MBusMasterPortSetup":
            ot = ObjectType.MBUS_MASTER_PORT_SETUP
        elif value == "PushSetup":
            ot = ObjectType.PUSH_SETUP
        elif value == "Account":
            ot = ObjectType.ACCOUNT
        elif value == "Credit":
            ot = ObjectType.CREDIT
        elif value == "Charge":
            ot = ObjectType.CHARGE
        elif value == "ParameterMonitor":
            ot = ObjectType.PARAMETER_MONITOR
        elif value == "TokenGateway":
            ot = ObjectType.TOKEN_GATEWAY
        elif value == "GSMDiagnostic":
            ot = ObjectType.GSM_DIAGNOSTIC
        elif value == "CompactData":
            ot = ObjectType.COMPACT_DATA
        elif value == "Ip6Setup":
            ot = ObjectType.IP6_SETUP
        else:
            ot = ObjectType.NONE
        return ot
