'''
Created on 22 août 2022

@author: denis
'''
import logging
import threading
from . import gpiolib as gpio

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

class DeviceRegister:

    def __init__(self, register:dict):
        """
        register must contain
             {'dev_type': 'None', 'index': 0, 'key': 'default', 'label': 'Default label', 'status': 0, 'active': 1}
        """
        self.register = register.copy()

    @property
    def dev_type(self):
        return self.register.get('dev_type')

    @property
    def status(self):
        return self.register.get('status')

    @status.setter
    def status(self, v):
        self.register['status'] = int(v)

    @property
    def active_pin(self):
        return self.register.get('active')

    @property
    def key(self):
        return self.register.get('key')

    @property
    def label(self):
        return self.register.get('label')

    @property
    def index(self):
        return self.register.get('index')

class Device(DeviceRegister):

    def __init__(self, pin, register=None, sens=gpio.IO_OUTPUT, initial=None, callback=None, edge=gpio.IO_FALLING, bouncetime=500, pull_up_down=None):
        super().__init__(register)

        self.pin = gpio.Pin(pin, sens=sens, initial=initial, callback=callback, edge=edge, bouncetime=bouncetime, pull_up_down=pull_up_down)
        self.sens = sens
        self.active = gpio.IO_HIGH if self.active_pin else gpio.IO_LOW
        self.not_active = gpio.IO_LOW if self.active_pin else gpio.IO_HIGH
        self.is_opened = self.is_on
        self.is_closed = self.is_off
        self.open = self.on
        self.close = self.off

        if self.sens == gpio.IO_OUTPUT:
            if initial==gpio.IO_HIGH:
                self.on()
            elif initial==gpio.IO_LOW:
                self.off()

    def state(self):
        return self.pin.read()

    def on(self):
        if self.sens == gpio.IO_OUTPUT:
            return self.pin.write(self.active)

    def off(self):
        if self.sens == gpio.IO_OUTPUT:
            return self.pin.write(self.not_active)

    def is_on(self):
        return self.state() == self.active

    def is_off(self):
        return self.state() == self.not_active

    def toggle(self):
        if self.is_on():
            return self.off()
        if self.is_off():
            return self.on()

class Led(Device):

    def __init__(self, pin, register=None, initial=gpio.IO_LOW):
        super().__init__(pin, register=register, sens=gpio.IO_OUTPUT, initial=initial)

class Relay(Device):

    def __init__(self, pin, register=None, initial=gpio.IO_LOW):
        super().__init__(pin, register=register, sens=gpio.IO_OUTPUT, initial=initial)

class Switch(Device):

    def __init__(self, pin, register=None, callback=None, edge=gpio.IO_BOTH):
        super().__init__(pin, register=register, callback=callback, sens=gpio.IO_INPUT, edge=edge)

    def on(self):
        pass

    def off(self):
        pass

class Button(Device):

    def __init__(self, pin, register=None, callback=None, bouncetime=500):
        super().__init__(pin, register=register, callback=callback, sens=gpio.IO_INPUT, edge=gpio.IO_FALLING, bouncetime=bouncetime)
        self._state = self.not_active

    def state(self):
        return self._state

    def on(self):
        self._state = self.active

    def off(self):
        self._state = self.not_active

class InputDevice(Device):

    def __init__(self, pin, register=None, callback=None, pull_up_down=gpio.IO_PULL_UP):
        super().__init__(pin, register=register, callback=callback, sens=gpio.IO_INPUT, edge=gpio.IO_FALLING, pull_up_down=pull_up_down)

    def on(self):
        pass

    def off(self):
        pass

class RelayFromShiftOut(DeviceRegister):

    def __init__(self, shift_out_register, register=None, relay_timeout=0, closed=True):
        super().__init__(register)
        self.shift_out_register = shift_out_register

        self.max_index = shift_out_register.datalen
        self.active = gpio.IO_HIGH if self.active_pin else gpio.IO_LOW
        self.not_active = gpio.IO_LOW if self.active_pin else gpio.IO_HIGH

        self.relay_timeout = 0
        self.is_opened = self.is_on
        self.is_closed = self.is_off
        self.open = self.on
        self.close = self.off

        if closed:
            self.off()
        else:
            self.on()

        self.relay_timeout = relay_timeout

        logger.info(f'RelayShiftOut Init index {self.index}')

    def wait_after_event(self, timeout):
        if timeout:
            threading.Event().wait(timeout)

    def on(self):
        self.status = self.shift_out_register.set(self.index, self.active)
        self.wait_after_event(self.relay_timeout)   # wait after opening
        logger.info(f'RelayShiftOut {self.index} on')
        return self.status

    def off(self):
        self.status = self.shift_out_register.set(self.index, self.not_active)
        self.wait_after_event(self.relay_timeout)   # wait after closing
        logger.info(f'RelayShiftOut {self.index} off')
        return self.status

    def is_on(self):
        self.status = self.shift_out_register.get(self.index) == self.active
        return self.status

    def is_off(self):
        self.status = self.shift_out_register.get(self.index) == self.not_active
        return self.status

    def toggle(self):
        if self.is_on():
            return self.off()
        if self.is_off():
            return self.on()
