"""Support for Dyson Pure Cool link fan.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/fan.dyson/
"""
import logging
import asyncio
from os import path
import voluptuous as vol
import homeassistant.helpers.config_validation as cv
from homeassistant.components.fan import (FanEntity, SUPPORT_OSCILLATE,
                                          SUPPORT_SET_SPEED,
                                          DOMAIN)
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.components.dyson import DYSON_DEVICES
from homeassistant.config import load_yaml_config_file

DEPENDENCIES = ['dyson']

_LOGGER = logging.getLogger(__name__)


DYSON_FAN_DEVICES = "dyson_fan_devices"
SERVICE_SET_NIGHT_MODE = 'dyson_set_night_mode'

DYSON_SET_NIGHT_MODE_SCHEMA = vol.Schema({
    vol.Required('entity_id'): cv.entity_id,
    vol.Required('night_mode'): cv.boolean
})


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Setup the Dyson fan components."""
    _LOGGER.info("Creating new Dyson fans")
    if DYSON_FAN_DEVICES not in hass.data:
        hass.data[DYSON_FAN_DEVICES] = []

    # Get Dyson Devices from parent component
    from libpurecoollink.dyson_pure_cool_link import DysonPureCoolLink
    for device in [d for d in hass.data[DYSON_DEVICES] if
                   isinstance(d, DysonPureCoolLink)]:
        dyson_entity = DysonPureCoolLinkDevice(hass, device)
        hass.data[DYSON_FAN_DEVICES].append(dyson_entity)

    add_devices(hass.data[DYSON_FAN_DEVICES])

    descriptions = load_yaml_config_file(
        path.join(path.dirname(__file__), 'services.yaml'))

    def service_handle(service):
        """Handle dyson services."""
        entity_id = service.data.get('entity_id')
        night_mode = service.data.get('night_mode')
        fan_device = next([fan for fan in hass.data[DYSON_FAN_DEVICES] if
                           fan.entity_id == entity_id].__iter__(), None)
        if fan_device is None:
            _LOGGER.warning("Unable to find Dyson fan device %s",
                            str(entity_id))
            return

        if service.service == SERVICE_SET_NIGHT_MODE:
            fan_device.night_mode(night_mode)

    # Register dyson service(s)
    hass.services.register(DOMAIN, SERVICE_SET_NIGHT_MODE,
                           service_handle,
                           descriptions.get(SERVICE_SET_NIGHT_MODE),
                           schema=DYSON_SET_NIGHT_MODE_SCHEMA)


class DysonPureCoolLinkDevice(FanEntity):
    """Representation of a Dyson fan."""

    def __init__(self, hass, device):
        """Initialize the fan."""
        _LOGGER.info("Creating device %s", device.name)
        self.hass = hass
        self._device = device

    @asyncio.coroutine
    def async_added_to_hass(self):
        """Callback when entity is added to hass."""
        self.hass.async_add_job(
            self._device.add_message_listener, self.on_message)

    def on_message(self, message):
        """Called when new messages received from the fan."""
        from libpurecoollink.dyson_pure_state import DysonPureCoolState
        if isinstance(message, DysonPureCoolState):
            _LOGGER.debug("Message received for fan device %s : %s", self.name,
                          message)
            self.schedule_update_ha_state()

    @property
    def should_poll(self):
        """No polling needed."""
        return False

    @property
    def name(self):
        """Return the display name of this fan."""
        return self._device.name

    def set_speed(self: ToggleEntity, speed: str) -> None:
        """Set the speed of the fan. Never called ??."""
        _LOGGER.debug("Set fan speed to: " + speed)
        from libpurecoollink.const import FanSpeed, FanMode
        if speed == FanSpeed.FAN_SPEED_AUTO.value:
            self._device.set_configuration(fan_mode=FanMode.AUTO)
        else:
            fan_speed = FanSpeed('{0:04d}'.format(int(speed)))
            self._device.set_configuration(fan_mode=FanMode.FAN,
                                           fan_speed=fan_speed)

    def turn_on(self: ToggleEntity, speed: str=None, **kwargs) -> None:
        """Turn on the fan."""
        _LOGGER.debug("Turn on fan %s with speed %s", self.name, speed)
        from libpurecoollink.const import FanSpeed, FanMode
        if speed:
            if speed == FanSpeed.FAN_SPEED_AUTO.value:
                self._device.set_configuration(fan_mode=FanMode.AUTO)
            else:
                fan_speed = FanSpeed('{0:04d}'.format(int(speed)))
                self._device.set_configuration(fan_mode=FanMode.FAN,
                                               fan_speed=fan_speed)
        else:
            # Speed not set, just turn on
            self._device.set_configuration(fan_mode=FanMode.FAN)

    def turn_off(self: ToggleEntity, **kwargs) -> None:
        """Turn off the fan."""
        _LOGGER.debug("Turn off fan %s", self.name)
        from libpurecoollink.const import FanMode
        self._device.set_configuration(fan_mode=FanMode.OFF)

    def oscillate(self: ToggleEntity, oscillating: bool) -> None:
        """Turn on/off oscillating."""
        _LOGGER.debug("Turn oscillation %s for device %s", oscillating,
                      self.name)
        from libpurecoollink.const import Oscillation

        if oscillating:
            self._device.set_configuration(
                oscillation=Oscillation.OSCILLATION_ON)
        else:
            self._device.set_configuration(
                oscillation=Oscillation.OSCILLATION_OFF)

    @property
    def oscillating(self):
        """Return the oscillation state."""
        return self._device.state and self._device.state.oscillation == "ON"

    @property
    def is_on(self):
        """Return true if the entity is on."""
        if self._device.state:
            return self._device.state.fan_state == "FAN"
        return False

    @property
    def speed(self) -> str:
        """Return the current speed."""
        if self._device.state:
            from libpurecoollink.const import FanSpeed
            if self._device.state.speed == FanSpeed.FAN_SPEED_AUTO.value:
                return self._device.state.speed
            return int(self._device.state.speed)
        return None

    @property
    def current_direction(self):
        """Return direction of the fan [forward, reverse]."""
        return None

    @property
    def is_night_mode(self):
        """Return Night mode."""
        return self._device.state.night_mode == "ON"

    def night_mode(self: ToggleEntity, night_mode: bool) -> None:
        """Turn fan in night mode."""
        _LOGGER.debug("Set %s night mode %s", self.name, night_mode)
        from libpurecoollink.const import NightMode
        if night_mode:
            self._device.set_configuration(night_mode=NightMode.NIGHT_MODE_ON)
        else:
            self._device.set_configuration(night_mode=NightMode.NIGHT_MODE_OFF)

    @property
    def is_auto_mode(self):
        """Return auto mode."""
        return self._device.state.fan_mode == "AUTO"

    def auto_mode(self: ToggleEntity, auto_mode: bool) -> None:
        """Turn fan in auto mode."""
        _LOGGER.debug("Set %s auto mode %s", self.name, auto_mode)
        from libpurecoollink.const import FanMode
        if auto_mode:
            self._device.set_configuration(fan_mode=FanMode.AUTO)
        else:
            self._device.set_configuration(fan_mode=FanMode.FAN)

    @property
    def speed_list(self: ToggleEntity) -> list:
        """Get the list of available speeds."""
        from libpurecoollink.const import FanSpeed
        supported_speeds = [FanSpeed.FAN_SPEED_AUTO.value,
                            int(FanSpeed.FAN_SPEED_1.value),
                            int(FanSpeed.FAN_SPEED_2.value),
                            int(FanSpeed.FAN_SPEED_3.value),
                            int(FanSpeed.FAN_SPEED_4.value),
                            int(FanSpeed.FAN_SPEED_5.value),
                            int(FanSpeed.FAN_SPEED_6.value),
                            int(FanSpeed.FAN_SPEED_7.value),
                            int(FanSpeed.FAN_SPEED_8.value),
                            int(FanSpeed.FAN_SPEED_9.value),
                            int(FanSpeed.FAN_SPEED_10.value)]

        return supported_speeds

    @property
    def supported_features(self: ToggleEntity) -> int:
        """Flag supported features."""
        return SUPPORT_OSCILLATE | SUPPORT_SET_SPEED
