"""Bridge between emulated_roku and Home Assistant."""
import logging

from homeassistant.const import (
    EVENT_HOMEASSISTANT_START, EVENT_HOMEASSISTANT_STOP)
from homeassistant.core import CoreState, EventOrigin

LOGGER = logging.getLogger('.')

EVENT_ROKU_COMMAND = 'roku_command'

ATTR_COMMAND_TYPE = 'type'
ATTR_SOURCE_NAME = 'source_name'
ATTR_KEY = 'key'
ATTR_APP_ID = 'app_id'

ROKU_COMMAND_KEYDOWN = 'keydown'
ROKU_COMMAND_KEYUP = 'keyup'
ROKU_COMMAND_KEYPRESS = 'keypress'
ROKU_COMMAND_LAUNCH = 'launch'


class EmulatedRoku:
    """Manages an emulated_roku server."""

    def __init__(self, hass, name, host_ip, listen_port,
                 advertise_ip, advertise_port, upnp_bind_multicast):
        """Initialize the properties."""
        self.hass = hass

        self.roku_usn = name
        self.host_ip = host_ip
        self.listen_port = listen_port

        self.advertise_port = advertise_port
        self.advertise_ip = advertise_ip

        self.bind_multicast = upnp_bind_multicast

        self._api_server = None

        self._unsub_start_listener = None
        self._unsub_stop_listener = None

    async def setup(self):
        """Start the emulated_roku server."""
        from emulated_roku import EmulatedRokuServer, \
            EmulatedRokuCommandHandler

        class EventCommandHandler(EmulatedRokuCommandHandler):
            """emulated_roku command handler to turn commands into events."""

            def __init__(self, hass):
                self.hass = hass

            def on_keydown(self, roku_usn, key):
                """Handle keydown event."""
                self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
                    ATTR_SOURCE_NAME: roku_usn,
                    ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYDOWN,
                    ATTR_KEY: key
                }, EventOrigin.local)

            def on_keyup(self, roku_usn, key):
                """Handle keyup event."""
                self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
                    ATTR_SOURCE_NAME: roku_usn,
                    ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYUP,
                    ATTR_KEY: key
                }, EventOrigin.local)

            def on_keypress(self, roku_usn, key):
                """Handle keypress event."""
                self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
                    ATTR_SOURCE_NAME: roku_usn,
                    ATTR_COMMAND_TYPE: ROKU_COMMAND_KEYPRESS,
                    ATTR_KEY: key
                }, EventOrigin.local)

            def launch(self, roku_usn, app_id):
                """Handle launch event."""
                self.hass.bus.async_fire(EVENT_ROKU_COMMAND, {
                    ATTR_SOURCE_NAME: roku_usn,
                    ATTR_COMMAND_TYPE: ROKU_COMMAND_LAUNCH,
                    ATTR_APP_ID: app_id
                }, EventOrigin.local)

        LOGGER.debug("Intializing emulated_roku %s on %s:%s",
                     self.roku_usn, self.host_ip, self.listen_port)

        handler = EventCommandHandler(self.hass)

        self._api_server = EmulatedRokuServer(
            self.hass.loop, handler,
            self.roku_usn, self.host_ip, self.listen_port,
            advertise_ip=self.advertise_ip,
            advertise_port=self.advertise_port,
            bind_multicast=self.bind_multicast
        )

        async def emulated_roku_stop(event):
            """Wrap the call to emulated_roku.close."""
            LOGGER.debug("Stopping emulated_roku %s", self.roku_usn)
            self._unsub_stop_listener = None
            await self._api_server.close()

        async def emulated_roku_start(event):
            """Wrap the call to emulated_roku.start."""
            try:
                LOGGER.debug("Starting emulated_roku %s", self.roku_usn)
                self._unsub_start_listener = None
                await self._api_server.start()
            except OSError:
                LOGGER.exception("Failed to start Emulated Roku %s on %s:%s",
                                 self.roku_usn, self.host_ip, self.listen_port)
                # clean up inconsistent state on errors
                await emulated_roku_stop(None)
            else:
                self._unsub_stop_listener = self.hass.bus.async_listen_once(
                    EVENT_HOMEASSISTANT_STOP,
                    emulated_roku_stop)

        # start immediately if already running
        if self.hass.state == CoreState.running:
            await emulated_roku_start(None)
        else:
            self._unsub_start_listener = self.hass.bus.async_listen_once(
                EVENT_HOMEASSISTANT_START,
                emulated_roku_start)

        return True

    async def unload(self):
        """Unload the emulated_roku server."""
        LOGGER.debug("Unloading emulated_roku %s", self.roku_usn)

        if self._unsub_start_listener:
            self._unsub_start_listener()
            self._unsub_start_listener = None

        if self._unsub_stop_listener:
            self._unsub_stop_listener()
            self._unsub_stop_listener = None

        await self._api_server.close()

        return True
