"""FHEM integration"""

import logging
import voluptuous as vol
import asyncio

import homeassistant.helpers.config_validation as cv
from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP

_LOGGER = logging.getLogger(__name__)
CONF_CUL_DEVICE_NAME = 'cul_device_name'
DOMAIN = 'fhem'
DATA_FHEM = "data_fhem"
CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_HOST): cv.string,
        vol.Optional(CONF_PORT, default=7072): cv.port,
        vol.Required(CONF_CUL_DEVICE_NAME): cv.string,
    })
}, extra=vol.ALLOW_EXTRA)


async def async_setup(hass, config):
    connection = FhemConnection(hass, config)
    hass.data[DATA_FHEM] = connection
    await connection.start()
    return True


class FhemConnection:

    def __init__(self, hass, config):
        self.hass = hass
        self._host = config[DOMAIN][CONF_HOST]
        self._port = config[DOMAIN][CONF_PORT]
        self._cul_device_name = config[DOMAIN][CONF_CUL_DEVICE_NAME]

        self.connected = False
        self.reconnect_time_start = 1
        self.reconnect_time_max = 60
        self.reconnect_time = self.reconnect_time_start
        self.devices = {}
        self._run = False
        self._writer = None

    async def start(self):
        self._run = True
        self.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, self.stop)
        self.hass.loop.create_task(self._connection())

    async def stop(self):
        self._run = False
        self.connected = False

    async def _connection(self):
        try:
            reader, writer = await asyncio.open_connection(self._host, self._port)
            _LOGGER.info("Connected to FHEM {}:{}".format(self._host, self._port))
            self._writer = writer
            self.connected = True
            self.reconnect_time = self.reconnect_time_start
            writer.writelines([
                "displayattr .*\n".encode(),
                "inform on\n".encode(),
            ])
            while self._run:
                line = await reader.readline()
                line = line.decode()
                _LOGGER.debug("FHEM received line: {}".format(line))
                await self._process_line(line)
        except OSError:
            _LOGGER.warning("Connection to FHEM failed {}:{}".format(self._host, self._port))
            self.connected = False
            await asyncio.sleep(self.reconnect_time)
            self.reconnect_time = min(2 * self.reconnect_time, self.reconnect_time)
            self.hass.loop.create_task(self._connection())

    async def _process_line(self, line):
        if line.startswith(self._cul_device_name + " "):  # Status update message
            _, device_name, command = line.split(" ", 2)
            if device_name in self.devices:
                await self.devices[device_name].line_received(command.strip())
        else:  # potential response to displayattr
            split_line = line.split(" ", 1)
            if len(split_line) == 2:
                device_name, command = split_line
                if device_name in self.devices:
                    await self.devices[device_name].line_received(command.strip())

    def write_line(self, line):
        if self._writer:
            line += '\n'
            self._writer.write(line.encode())

    def fhem_set(self, id, *arguments):
        """
        Send command to FHEM using this device
            :param arguments: string or list of strings containing command parameters
        """
        arguments = " ".join([str(a) for a in arguments])
        self._writer.write("set {} {}\n".format(id, arguments).encode())
