# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import logging
from threading import Thread
import time

from odoo.addons.hw_drivers.main import drivers, interfaces, iot_devices

_logger = logging.getLogger(__name__)


class InterfaceMetaClass(type):
    def __new__(cls, clsname, bases, attrs):
        new_interface = super(InterfaceMetaClass, cls).__new__(cls, clsname, bases, attrs)
        interfaces[clsname] = new_interface
        return new_interface


class Interface(Thread, metaclass=InterfaceMetaClass):
    _loop_delay = 3  # Delay (in seconds) between calls to get_devices or 0 if it should be called only once
    _detected_devices = {}
    connection_type = ''

    def __init__(self):
        super(Interface, self).__init__()
        self.drivers = sorted([d for d in drivers if d.connection_type == self.connection_type], key=lambda d: d.priority, reverse=True)

    def run(self):
        while self.connection_type and self.drivers:
            self.update_iot_devices(self.get_devices())
            if not self._loop_delay:
                break
            time.sleep(self._loop_delay)

    def update_iot_devices(self, devices={}):
        added = devices.keys() - self._detected_devices
        removed = self._detected_devices - devices.keys()
        # keys() returns a dict_keys, and the values of that stay in sync with the
        # original dictionary if it changes. This means that get_devices needs to return
        # a newly created dictionary every time. If it doesn't do that and reuses the
        # same dictionary, this logic won't detect any changes that are made. Could be
        # avoided by converting the dict_keys into a regular dict. The current logic
        # also can't detect if a device is replaced by a different one with the same
        # key. Also, _detected_devices starts out as a class variable but gets turned
        # into an instance variable here. It would be better if it was an instance
        # variable from the start to avoid confusion.
        self._detected_devices = devices.keys()

        for identifier in removed:
            if identifier in iot_devices:
                iot_devices[identifier].disconnect()
                _logger.info('Device %s is now disconnected', identifier)

        for identifier in added:
            for driver in self.drivers:
                if driver.supported(devices[identifier]):
                    _logger.info('Device %s is now connected', identifier)
                    d = driver(identifier, devices[identifier])
                    d.daemon = True
                    iot_devices[identifier] = d
                    # Start the thread after creating the iot_devices entry so the
                    # thread can assume the iot_devices entry will exist while it's
                    # running, at least until the `disconnect` above gets triggered
                    # when `removed` is not empty.
                    d.start()
                    break

    def get_devices(self):
        raise NotImplementedError()