"""Device model package."""

from arduinozore.models.card import Card
from arduinozore.models.model import Model
from arduinozore.settings import DEVICE_CONFIG_FOLDER
from serial.tools import list_ports


class Port(Model):
    """Port class."""

    yaml_tag = u'!Port'

    def __init__(self, name, number, enabled, _type="output"):
        """Init port."""
        self.name = name
        self._type = _type
        self.number = number
        self.enabled = enabled if enabled is True or False else (
            True if enabled == 'on' else False)

    def __repr__(self):
        """Represent port in order to save it."""
        return "%s(number=%r, name=%r, _type=%r, enabled=%r)" % (
            self.__class__.__name__, self.number, self.name, self._type, self.enabled)


class Device(Model):
    """Device class."""

    yaml_tag = u'!Device'

    def __init__(self, name, identifier, card_name):
        """Init device."""
        self.name = name
        self.identifier = identifier
        self.card = Card.get(card_name)
        self.init_ports()

    def save(self):
        """Save device."""
        self.save_yaml(DEVICE_CONFIG_FOLDER)

    def delete(self):
        """Delete device."""
        self._delete(DEVICE_CONFIG_FOLDER)

    def __repr__(self):
        """Represent device in order to save it."""
        return "%s(name=%r, identifier=%r, card=%r, ports=%r)" % (
            self.__class__.__name__, self.name, self.identifier, self.card, self.ports)

    def init_ports(self):
        """Init port list."""
        self.ports = {'input': list(), 'output': list()}
        for i in range(self.card.nb_input_pins):
            self.ports['input'].append(Port(
                number=i, name="", enabled="False", _type=""))
        for i in range(self.card.nb_output_pins):
            self.ports['output'].append(Port(
                number=i, name="", enabled="False", _type=""))

    def add_port_from_dict(self, port, dict):
        """Create port and add from dict."""
        port = int(port.replace("port", ""))
        if port >= self.card.nb_input_pins:
            port = port - self.card.nb_input_pins
        p = Port(number=port, **dict)
        _type = p._type if p._type == "output" else "input"
        port_to_replace = next(
            (self.ports[_type].index(port) for port in self.ports[_type] if p.number == port.number), None)
        self.ports[_type][port_to_replace] = p

    def get_filename(self):
        """Get filename to save."""
        return __class__.filenamify(self.identifier) + ".yaml"

    @classmethod
    def get_arduinos(cls):
        """Get list of connected arduinos."""
        serials = list(list_ports.comports())
        arduinos = [s for s in serials if 'Arduino' in s.description or (
            s.manufacturer is not None and 'Arduino' in s.manufacturer)]
        return arduinos

    @classmethod
    def get_all(cls):
        """Get all device configurations."""
        return __class__._get_all(DEVICE_CONFIG_FOLDER)

    @classmethod
    def get(cls, name):
        """Get device by name."""
        return __class__._get(name, DEVICE_CONFIG_FOLDER)

    @classmethod
    def get_connected_devices(cls):
        """Get devices connected."""
        arduinos = cls.get_arduinos()
        devices = {a.device: __class__.get(__class__.get_identifier(
            a)) for a in arduinos}
        return devices

    @classmethod
    def get_config(cls, name):
        """Get config by name."""
        configs = cls.get_all()
        if configs is not None:
            return next(
                (config for config in configs if config.name == name), None)
        else:
            return configs

    @classmethod
    def get_identifier_from_serial(cls, serial):
        """Get device identifier from serial name."""
        arduinos = cls.get_arduinos()
        arduino = next(
            (arduino for arduino in arduinos if serial in arduino.device), None)
        return __class__.get_identifier(arduino)

    @classmethod
    def get_identifier(cls, arduino):
        """Get identifier from arduino."""
        if arduino is not None:
            config_name = str(arduino.vid)
            config_name += str(arduino.pid)
            config_name += str(arduino.serial_number)
            return config_name
        else:
            return None

    @classmethod
    def from_request_args(cls, slug, args):
        """Init device from request args."""
        args = {arg: args[arg][0].decode() for arg in args}
        res = dict()
        for arg in args:
            if '[' in arg and ']' in arg:
                split_arg = arg.split('[')
                var_name = split_arg[0]
                index = split_arg[1].split(']')[0]
                if var_name not in res:
                    res[var_name] = dict()
                res[var_name][index] = args[arg]
            else:
                res[arg] = args[arg]
        if 'identifier' not in res:
            identifier = cls.get_identifier_from_serial(slug)
        else:
            identifier = res.pop('identifier')
        dev = cls(res.pop('name'), identifier,
                  res.pop('card_name'))
        for port in res:
            dev.add_port_from_dict(port, res[port])
        return dev

"""Device model package."""

from arduinozore.models.card import Card
from arduinozore.models.model import Model
from arduinozore.settings import DEVICE_CONFIG_FOLDER
from serial.tools import list_ports


class Port(Model):
    """Port class."""

    yaml_tag = u'!Port'

    def __init__(self, name, number, enabled, _type="output"):
        """Init port."""
        self.name = name
        self._type = _type
        self.number = number
        self.enabled = enabled if enabled is True or False else (
            True if enabled == 'on' else False)

    def __repr__(self):
        """Represent port in order to save it."""
        return "%s(number=%r, name=%r, _type=%r, enabled=%r)" % (
            self.__class__.__name__, self.number, self.name, self._type, self.enabled)


class Device(Model):
    """Device class."""

    yaml_tag = u'!Device'

    def __init__(self, name, identifier, card_name):
        """Init device."""
        self.name = name
        self.identifier = identifier
        self.card = Card.get(card_name)
        self.init_ports()

    def save(self):
        """Save device."""
        self.save_yaml(DEVICE_CONFIG_FOLDER)

    def delete(self):
        """Delete device."""
        self._delete(DEVICE_CONFIG_FOLDER)

    def __repr__(self):
        """Represent device in order to save it."""
        return "%s(name=%r, identifier=%r, card=%r, ports=%r)" % (
            self.__class__.__name__, self.name, self.identifier, self.card, self.ports)

    def init_ports(self):
        """Init port list."""
        self.ports = {'input': list(), 'output': list()}
        for i in range(self.card.nb_input_pins):
            self.ports['input'].append(Port(
                number=i, name="", enabled="False", _type=""))
        for i in range(self.card.nb_output_pins):
            self.ports['output'].append(Port(
                number=i, name="", enabled="False", _type=""))

    def add_port_from_dict(self, port, dict):
        """Create port and add from dict."""
        port = int(port.replace("port", ""))
        if port >= self.card.nb_input_pins:
            port = port - self.card.nb_input_pins
        p = Port(number=port, **dict)
        _type = p._type if p._type == "output" else "input"
        port_to_replace = next(
            (self.ports[_type].index(port) for port in self.ports[_type] if p.number == port.number), None)
        self.ports[_type][port_to_replace] = p

    def get_filename(self):
        """Get filename to save."""
        return __class__.filenamify(self.identifier) + ".yaml"

    @classmethod
    def get_arduinos(cls):
        """Get list of connected arduinos."""
        serials = list(list_ports.comports())
        arduinos = [s for s in serials if 'Arduino' in s.description or (
            s.manufacturer is not None and 'Arduino' in s.manufacturer)]
        return arduinos

    @classmethod
    def get_all(cls):
        """Get all device configurations."""
        return __class__._get_all(DEVICE_CONFIG_FOLDER)

    @classmethod
    def get(cls, name):
        """Get device by name."""
        return __class__._get(name, DEVICE_CONFIG_FOLDER)

    @classmethod
    def get_connected_devices(cls):
        """Get devices connected."""
        arduinos = cls.get_arduinos()
        devices = {a.device: __class__.get(__class__.get_identifier(
            a)) for a in arduinos}
        return devices

    @classmethod
    def get_config(cls, name):
        """Get config by name."""
        configs = cls.get_all()
        if configs is not None:
            return next(
                (config for config in configs if config.name == name), None)
        else:
            return configs

    @classmethod
    def get_identifier_from_serial(cls, serial):
        """Get device identifier from serial name."""
        arduinos = cls.get_arduinos()
        arduino = next(
            (arduino for arduino in arduinos if serial in arduino.device), None)
        return __class__.get_identifier(arduino)

    @classmethod
    def get_identifier(cls, arduino):
        """Get identifier from arduino."""
        if arduino is not None:
            config_name = str(arduino.vid)
            config_name += str(arduino.pid)
            config_name += str(arduino.serial_number)
            return config_name
        else:
            return None

    @classmethod
    def from_request_args(cls, slug, args):
        """Init device from request args."""
        args = {arg: args[arg][0].decode() for arg in args}
        res = dict()
        for arg in args:
            if '[' in arg and ']' in arg:
                split_arg = arg.split('[')
                var_name = split_arg[0]
                index = split_arg[1].split(']')[0]
                if var_name not in res:
                    res[var_name] = dict()
                res[var_name][index] = args[arg]
            else:
                res[arg] = args[arg]
        if 'identifier' not in res:
            identifier = cls.get_identifier_from_serial(slug)
        else:
            identifier = res.pop('identifier')
        dev = cls(res.pop('name'), identifier,
                  res.pop('card_name'))
        for port in res:
            dev.add_port_from_dict(port, res[port])
        return dev

Device model package. Port class. Init port. Represent port in order to save it. Device class. Init device. Save device. Delete device. Represent device in order to save it. Init port list. Create port and add from dict. Get filename to save. Get list of connected arduinos. Get all device configurations. Get device by name. Get devices connected. Get config by name. Get device identifier from serial name. Get identifier from arduino. Init device from request args.