from lxml import etree
from src.LoggingClasses import SimpleLogger
import src.settings as settings

class DeviceContainer:
    
    def __init__(self, devices = [],  auth = None, loggerId = None, msgType=None, upid = None):
        
        self.listObjects = devices
        self.auth = auth
        self.loggerId = loggerId
        self.msgType = msgType
        self.upid = upid
        self.filters = {}
        self.filters['vtype'] = []
        self.filters['dtype'] = []
        self.listStatusCodes = {}
        self.responseRoot = etree.Element('response')
        self.responseTypeNode = None
        self.missingValue = None
        self.invalidXMLFlag = False
       
         

    # reads the XML message. Every contained class has its own method for
    # reading the values from a XML node
    def readXMLSetRequest(self, xmlMsg):
        
        settings.logger.info('DeviceContainer: entered readXMLSetRequest()')
        root = etree.fromstring(xmlMsg)
        
        # attribute values are checked for empty strings and None values  
        # if an incorrect value is found the reading process stops and returns 
        # an error. NOTE: for a None value an INVALID_XML message is returned and no 
        # DB queries are executed. For an empty attribute value only the data for the 
        # given device is discarded and a status code MISSING_[ATTR_NAME] is returned, the rest of the data is saved.       
        self.loggerId = root.get('slid')
        if self.loggerId=='':
            self.missingValue ='slid'
            return
        if self.loggerId == None:
            self.invalidXMLFlag = True
            return
        
            
        self.auth = root.get('auth')
        if self.auth=='':
            self.missingValue='auth'
            return        
        if self.auth==None:
            self.invalidXMLFlag = True
            return
        
        setDevice = root.find('.//setDevice')
        if setDevice == None or setDevice == []:
            setDevice = root.find('.//setdevice') 
            
        
        for devtree in setDevice:
            dev = Device(loggerId=self.loggerId)
            dev.readFromXML(devtree)
            if dev.invalidXMLFlag:
                settings.logger.error('INVALID_XML: missing required attribute')
                self.invalidXMLFlag = True
                return
            if dev.missingValue!=None:
                settings.logger.error('MISSING_'+dev.missingValue.upper())
                self.appendDeviceNode(dev.deviceId, 'MISSING_'+dev.missingValue.upper())
                continue
            dev.setLIDs(self.loggerId)
            self.listObjects.append(dev)  
                   
       
        
    def readXMLGetRequest(self, xmlMsg):
        
        root = etree.fromstring(xmlMsg)
        upidEl = root.find('.//upid').text
        if upidEl!= None or upidEl!='':
            self.upid = upidEl
        
        filtersEl= root.find('.//filters')
        
        vtypeEl = filtersEl.find('.//vtypes')
        dtypeEl = filtersEl.find('.//deviceTypes')
        
        if vtypeEl != None and vtypeEl!= '':
            for vt in vtypeEl:
                self.filters['vtype'].append(vt.text)
            
        if dtypeEl != None and dtypeEl!='':
            for dt in dtypeEl:
                self.filters['dtype'].append(dt.text)     
        
        
    def writeXMLGetResponse(self):
        
        root = etree.Element('response')     
        for dev in self.listObjects:
            root.append(dev.writeToXML())            
        
        #xmlString = etree.tostring(root)
        xmlString = etree.tostring(root, pretty_print=True)
        
        return xmlString
    
    def writeXMLSetResponse(self):
        
        for dev in self.listObjects:
            d = etree.Element('device', sdid = dev.deviceId)
            if dev.deviceId in self.listStatusCodes:
                status = etree.Element('status', code=self.listStatusCodes[dev.deviceId])
                d.append(status)
            else:
                status = etree.Element('status', code='SUCCESS')
                d.append(status)
            self.responseTypeNode.append(d)
                
    # the message response type is set e.g. <setDevice> or <getDevice> 
    def setResponseTypeNode(self, respType):
        self.responseTypeNode = etree.Element(respType)
    
    # append a device node to the response XML tree    
    def appendDeviceNode(self,sdid,code):
        deviceNode = etree.Element('device', sdid=sdid)
        statusNode = etree.Element('status', code=code)
        deviceNode.append(statusNode)
        self.responseTypeNode.append(deviceNode)
        
    def appendStatusCodeNode(self, code):
        statusNode = etree.Element('status', code=code)
        self.responseTypeNode.append(statusNode)
        
    
    # returns the response tree as a string   
    def getResponseMsg(self):
        self.responseRoot.append(self.responseTypeNode)
        return etree.tostring(self.responseRoot)
               
   
class Device(object):

    def __init__(self, deviceId = None, loggerId = None, dtype = None, version = None, parameters = [], deviceValues = [], ldid = None, category = None ):
        
        self.deviceId = deviceId
        self.loggerId = loggerId
        self.dtype = dtype
        self.version = version
        self.parameters = [] 
        self.paramsToDelete = []
        self.deviceValues = []
        self.ldid = ldid
        self.category = category
        self.missingValue = None
        self.invalidXMLFlag = False
        
        
    def readFromXML(self, xmlMsg):
        
        self.deviceId = xmlMsg.get('sdid')
        if self.deviceId=='':
            self.missingValue='sdid'
            return
        if self.deviceId==None:
            self.invalidXMLFlag = True
            return
        
        self.dtype = xmlMsg.get('dtype')
        if self.dtype=='':
            self.missingValue='dtype'
            return
        if self.dtype==None:
            self.invalidXMLFlag = True
            return
  
        self.version = xmlMsg.get('version')
        if self.version=='':
            self.missingValue = 'version'
            return
        if self.version==None:
            self.invalidXMLFlag = True
            return
        
        self.category = xmlMsg.get('dcategory')
        if self.category=='':
            self.missingValue = 'dcategory'
            return
        if self.category==None:
            self.invalidXMLFlag = True
            return
        if self.category=='meter':
            self.category = 'energy_meter'
        self.category = self.category.upper()
        
        
        params = xmlMsg.find('.//dparams')
   
        for p in params:
            param = DeviceParameter()            
            param.readFromXML(p)   
            if param.missingValue!=None:
                self.missingValue = param.missingValue
                return 
            if param.invalidXMLFlag:
                self.invalidXMLFlag = True
                return
            if param.value=='':
                self.paramsToDelete.append(param.key)
                continue        
            self.parameters.append(param)
            
        values = xmlMsg.find('.//values')
        
        for v in values:
            
            devValue = DeviceValue()
            devValue.readFromXML(v)
            if devValue.missingValue!=None:
                self.missingValue = devValue.missingValue
                return
            if devValue.invalidXMLFlag:
                self.invalidXMLFlag = True
                return 
            
            self.deviceValues.append(devValue)
            
                    
    def writeToXML(self):

        device = etree.Element('device', sdid = self.deviceId, dtype = self.dtype)
        dparams = etree.Element('dparams')
        
        for p in self.parameters:
            
            dparams.append(p.writeToXML())
        
        device.append(dparams)
        
        values = etree.Element('values')
        
        for val in self.deviceValues:
            
            values.append(val.writeToXML())
            
        device.append(values)
        
        return device
    
    def setLIDs(self, lid):
      
        self.ldid = lid + '.' + self.deviceId
        for val in self.deviceValues:
            val.setLIDs(self.ldid)
        
class DeviceParameter:
    
    def __init__(self, key = None, value = None):
        
        self.key = key
        self.value = value    
        self.missingValue=None 
        self.invalidXMLFlag = False
        
    def readFromXML (self, xmlMsg):
        
        self.key = xmlMsg.get('k')
        if self.key=='':
            self.missingValue = 'k'
            return
        if self.key == None:
            self.invalidXMLFlag = True
            return
        self.value = xmlMsg.get('v')
        if self.value == None:
            self.invalidXMLFlag = True
            return
        
    def writeToXML (self):
        
        return (etree.Element('p', k = self.key, v = self.value))
        
class DeviceValue:
    
    def __init__(self, valueId = None, vtype = None, lvid = None, storage = None):
        
        self.valueId = valueId
        self.lvid = lvid
        self.vtype = vtype
        self.datatype = 'nvarchar(100)'
        self.parameters = []
        self.paramsToDelete = []
        self.missingValue = None
        self.invalidXMLFlag = False
        self.storage = storage
        # dictionary for the possible datatype values
        self.datatype_dict = {'INT16' : 'smallint',
                              'INT32' : 'int',
                              'INT64' : 'bigint',
                              'FLOAT32' : 'float',
                              'BOOL' : 'bit',
                              'TIMESTAMP' : 'timestamp',
                              'STRING' : 'nvarchar(100)'}
        
        
    def readFromXML(self, xmlmsg):        
        
        self.valueId = xmlmsg.get('svid')
        if self.valueId=='':
            self.missingValue = 'svid'
            return
        if self.valueId == None:
            self.invalidXMLFlag = True
            return
        
        self.vtype = xmlmsg.get('vtype')
        if self.vtype == '':
            self.missingValue = 'vtype'
            return
        if self.vtype == None:
            self.invalidXMLFlag = True
            return 
        
        self.storage = xmlmsg.get('storage')
        if self.storage is None:
            self.storage = 'cyclic'
        
        
        data_storage_type = xmlmsg.get('datatype')
        if data_storage_type!=None:
            if data_storage_type in self.datatype_dict:
                self.datatype = self.datatype_dict.get(data_storage_type)
            else:
                # default data storage type
                self.datatype = 'nvarchar(100)'            
        
        
        params = xmlmsg.find('.//vparams')
        if params != None:
            for p in params:
                param = ValueParameter()
                param.readFromXML(p)
                if param.missingValue!=None:
                    self.missingValue = param.missingValue
                    return
                if param.invalidXMLFlag:
                    self.invalidXMLFlag = True
                    return
                if param.value=='':
                    self.paramsToDelete.append(param.key)
                    continue
                self.parameters.append(param)      
        
    def writeToXML(self):
        
        value = etree.Element('value', svid = self.valueId, vtype = self.vtype)
        
        parameters = etree.Element('parameters')
        
        for param in self.parameters:
            
            parameters.append(param.writeToXML())
            
        value.append(parameters)
        
        return value
    
    def setLIDs(self, ldid):

        self.lvid = ldid + '.' + self.valueId
            
        
class ValueParameter:
    
    def __init__(self, key = None, value = None):
        
        self.key = key  
        self.value = value
        self.missingValue = None
        self.invalidXMLFlag = False
        
    def readFromXML(self, xmlMsg):
        
        self.key = xmlMsg.get('k')
        if self.key =='':
            self.missingValue = 'k'
            return
        if self.key == None:
            self.invalidXMLFlag = True
            return
        
        self.value = xmlMsg.get('v')
        if self.value == None:
            self.invalidXMLFlag = True
            return
          
        
    def writeToXML(self):
        
        return (etree.Element('p', k = self.key, v = self.value))
            
           
            
                
        
