#!/bin/python

from typing import Optional, Any

from .task import MBIOTask
from .xmlconfig import XMLConfig

from .bacnetserver import BACnetInterface

BACNET_VENDOR_ID = 892


class MyBACnetInterface(BACnetInterface):
    def setParent(self, parent):
        self._parent=parent

    def getParent(self):
        return self._parent

    def getMBIO(self):
        try:
            return self.getParent().getMBIO()
        except:
            pass

    def on_persistence_load(self) -> Optional[dict]:
        parent=self.getParent()
        try:
            return parent.pickleRead('server.pdata')
        except:
            pass
        return None

    def on_persistence_save(self, data: dict):
        parent=self.getParent()
        parent.pickleWrite('server.pdata', data)

    def on_external_write(self, name: str, v: Any, priority: int):
        self.logger.debug(f'onBacnetWrite({name}, {value})')
        parent=self.getParent()
        value=parent.values[name]
        if value is not None:
            value.updateValue(v)

    def on_server_error(self, error: Exception):
        self.logger.error('onBacnetError()')


class MBIOTaskBacnet(MBIOTask):
    def initName(self):
        return 'bnet'

    def onInit(self):
        mbio=self.getMBIO()
        self._bnet=None
        interface=mbio.interface
        self.config.set('interface', interface)
        self.config.set('port', 47808)
        self.config.set('deviceid', 0)
        self.valueDigital('comerr', default=False)
        self._av={}
        self._bv={}
        self._msv={}

    def onLoad(self, xml: XMLConfig):
        mbio=self.getMBIO()
        self.config.update('deviceid', xml.getInt('deviceid'))
        self.config.update('interface', xml.get('interface'))
        self.config.update('port', xml.getInt('port'))

        vtype='av'
        items=xml.children(vtype)
        if items:
            for item in items:
                name=item.get('name')
                unit=item.get('unit')
                resolution=item.getFloat('resolution', 0.1)
                if name:
                    data={}
                    name='%s_%s' % (vtype, name.lower())
                    value=self.value(name, writable=True, unit=unit, default=item.getFloat('default'), resolution=resolution)
                    data['value']=value
                    self._av[name]=data

    def createBacnetInterface(self):
        if self._bnet is not None:
            return True

        self._bnet=MyBACnetInterface(app_logger=self.logger,
            unit_mapping=None,
            device_id=self.config.deviceid,
            ip_address=self.config.interface,
            port=self.config.port,
            vendor_id=BACNET_VENDOR_ID,
            vendor_name='digimat',
            app_watchdog_timeout=60)

        self._bnet.setParent(self)

        for key in self._av.keys():
            data=self._av[key]
            value=data['value']
            self.logger.debug(f'declaring bacnet AV {key}')
            self._bnet.declare_object(name=key, type='AV',
                writable=True,
                description=value.description,
                units=None,
                state_texts=None,
                active_text=None,
                inactive_text=None,
                initial_value=value.value)

    def poweron(self):
        if self._bnet is None:
            self.logger.info('Creating BACnet interface %s:%d...' % (self.config.interface, self.config.port))
            self.createBacnetInterface()

        if self._bnet is not None:
            self.logger.info('Starting BACnet interface...')
            self._bnet.start()
        return True

    def poweroff(self):
        if self._bnet is not None:
            self.logger.info('Stopping BACnet interface...')
            self._bnet.stop()
        return True

    def run(self):
        if self._bnet is None or not self._bnet.is_healthy():
            self.logger.error('Error detected in BACnet Interface!')
            self.setState(self.STATE_ERROR)
            return

        self._bnet.feed_watchdog()
        error=False

        for name in self._av.keys():
            value=self._av[name]['value']
            v=self._bnet.get_value(name)
            if v is not None:
                value.updateValue(v)
                value.setError(False)
            else:
                error=True
                value.setError(True)

        self.values.comerr.updateValue(error)
        return 5.0

"""
        for name in self._av.keys():
            value=self._av[name]['value']
            if value.isPendingSync():
                self.microsleep()
                try:

                    device=value.config.device
                    cid=value.config.cid
                    if device.set(cid, value.toReachValue):
                        value.clearSync()
                        self._timeoutRefresh=0
                except:
                    pass
                value.clearSyncAndUpdateValue()

            v=self._bnet.get_value(name)
            if v is not None:
                value.updateData(v)
                value.setError(False)
            else:
                error=True
                value.setError(True)
        # TODO:
        # self._bnet.update(name, value.value, value.isError(), value.isManuel(), value.isAlarm())
"""


if __name__ == "__main__":
    pass
