"""
Support for Melissa Climate A/C.

For more details about this platform, please refer to the documentation
https://home-assistant.io/components/climate.melissa/
"""
import logging

from homeassistant.components.climate import (
    ClimateDevice, SUPPORT_OPERATION_MODE, SUPPORT_TARGET_TEMPERATURE,
    SUPPORT_ON_OFF, STATE_AUTO, STATE_HEAT, STATE_COOL, STATE_DRY,
    STATE_FAN_ONLY, SUPPORT_FAN_MODE
)
from homeassistant.components.fan import SPEED_LOW, SPEED_MEDIUM, SPEED_HIGH
from homeassistant.components.melissa import DATA_MELISSA
from homeassistant.const import (
    TEMP_CELSIUS, STATE_ON, STATE_OFF, STATE_IDLE, ATTR_TEMPERATURE,
    PRECISION_WHOLE
)

DEPENDENCIES = ['melissa']

_LOGGER = logging.getLogger(__name__)

SUPPORT_FLAGS = (SUPPORT_FAN_MODE | SUPPORT_OPERATION_MODE |
                 SUPPORT_ON_OFF | SUPPORT_TARGET_TEMPERATURE)

OP_MODES = [
    STATE_COOL, STATE_DRY, STATE_FAN_ONLY, STATE_HEAT
]

FAN_MODES = [
    STATE_AUTO, SPEED_HIGH, SPEED_LOW, SPEED_MEDIUM
]


def setup_platform(hass, config, add_devices, discovery_info=None):
    """Iterate through and add all Melissa devices."""
    api = hass.data[DATA_MELISSA]
    devices = api.fetch_devices().values()

    all_devices = []

    for device in devices:
        if device['type'] == 'melissa':
            all_devices.append(MelissaClimate(
                api, device['serial_number'], device))

    add_devices(all_devices)


class MelissaClimate(ClimateDevice):
    """Representation of a Melissa Climate device."""

    def __init__(self, api, serial_number, init_data):
        """Initialize the climate device."""
        self._name = init_data['name']
        self._api = api
        self._serial_number = serial_number
        self._data = init_data['controller_log']
        self._state = None
        self._cur_settings = None

    @property
    def name(self):
        """Return the name of the thermostat, if any."""
        return self._name

    @property
    def is_on(self):
        """Return current state."""
        if self._cur_settings is not None:
            return self._cur_settings[self._api.STATE] in (
                self._api.STATE_ON, self._api.STATE_IDLE)
        return None

    @property
    def current_fan_mode(self):
        """Return the current fan mode."""
        if self._cur_settings is not None:
            return self.melissa_fan_to_hass(
                self._cur_settings[self._api.FAN])

    @property
    def current_temperature(self):
        """Return the current temperature."""
        if self._data:
            return self._data[self._api.TEMP]

    @property
    def target_temperature_step(self):
        """Return the supported step of target temperature."""
        return PRECISION_WHOLE

    @property
    def current_operation(self):
        """Return the current operation mode."""
        if self._cur_settings is not None:
            return self.melissa_op_to_hass(
                self._cur_settings[self._api.MODE])

    @property
    def operation_list(self):
        """Return the list of available operation modes."""
        return OP_MODES

    @property
    def fan_list(self):
        """List of available fan modes."""
        return FAN_MODES

    @property
    def target_temperature(self):
        """Return the temperature we try to reach."""
        if self._cur_settings is not None:
            return self._cur_settings[self._api.TEMP]

    @property
    def state(self):
        """Return current state."""
        if self._cur_settings is not None:
            return self.melissa_state_to_hass(
                self._cur_settings[self._api.STATE])

    @property
    def temperature_unit(self):
        """Return the unit of measurement which this thermostat uses."""
        return TEMP_CELSIUS

    @property
    def min_temp(self):
        """Return the minimum supported temperature for the thermostat."""
        return 16

    @property
    def max_temp(self):
        """Return the maximum supported temperature for the thermostat."""
        return 30

    @property
    def supported_features(self):
        """Return the list of supported features."""
        return SUPPORT_FLAGS

    def set_temperature(self, **kwargs):
        """Set new target temperature."""
        temp = kwargs.get(ATTR_TEMPERATURE)
        self.send({self._api.TEMP: temp})

    def set_fan_mode(self, fan_mode):
        """Set fan mode."""
        melissa_fan_mode = self.hass_fan_to_melissa(fan_mode)
        self.send({self._api.FAN: melissa_fan_mode})

    def set_operation_mode(self, operation_mode):
        """Set operation mode."""
        mode = self.hass_mode_to_melissa(operation_mode)
        self.send({self._api.MODE: mode})

    def turn_on(self):
        """Turn on device."""
        self.send({self._api.STATE: self._api.STATE_ON})

    def turn_off(self):
        """Turn off device."""
        self.send({self._api.STATE: self._api.STATE_OFF})

    def send(self, value):
        """Sending action to service."""
        try:
            old_value = self._cur_settings.copy()
            self._cur_settings.update(value)
        except AttributeError:
            old_value = None
        if not self._api.send(self._serial_number, self._cur_settings):
            self._cur_settings = old_value
            return False
        return True

    def update(self):
        """Get latest data from Melissa."""
        try:
            self._data = self._api.status(cached=True)[self._serial_number]
            self._cur_settings = self._api.cur_settings(
                self._serial_number
            )['controller']['_relation']['command_log']
        except KeyError:
            _LOGGER.warning(
                'Unable to update entity %s', self.entity_id)

    def melissa_state_to_hass(self, state):
        """Translate Melissa states to hass states."""
        if state == self._api.STATE_ON:
            return STATE_ON
        elif state == self._api.STATE_OFF:
            return STATE_OFF
        elif state == self._api.STATE_IDLE:
            return STATE_IDLE
        return None

    def melissa_op_to_hass(self, mode):
        """Translate Melissa modes to hass states."""
        if mode == self._api.MODE_HEAT:
            return STATE_HEAT
        elif mode == self._api.MODE_COOL:
            return STATE_COOL
        elif mode == self._api.MODE_DRY:
            return STATE_DRY
        elif mode == self._api.MODE_FAN:
            return STATE_FAN_ONLY
        _LOGGER.warning(
            "Operation mode %s could not be mapped to hass", mode)
        return None

    def melissa_fan_to_hass(self, fan):
        """Translate Melissa fan modes to hass modes."""
        if fan == self._api.FAN_AUTO:
            return STATE_AUTO
        elif fan == self._api.FAN_LOW:
            return SPEED_LOW
        elif fan == self._api.FAN_MEDIUM:
            return SPEED_MEDIUM
        elif fan == self._api.FAN_HIGH:
            return SPEED_HIGH
        _LOGGER.warning("Fan mode %s could not be mapped to hass", fan)
        return None

    def hass_mode_to_melissa(self, mode):
        """Translate hass states to melissa modes."""
        if mode == STATE_HEAT:
            return self._api.MODE_HEAT
        elif mode == STATE_COOL:
            return self._api.MODE_COOL
        elif mode == STATE_DRY:
            return self._api.MODE_DRY
        elif mode == STATE_FAN_ONLY:
            return self._api.MODE_FAN
        else:
            _LOGGER.warning("Melissa have no setting for %s mode", mode)

    def hass_fan_to_melissa(self, fan):
        """Translate hass fan modes to melissa modes."""
        if fan == STATE_AUTO:
            return self._api.FAN_AUTO
        elif fan == SPEED_LOW:
            return self._api.FAN_LOW
        elif fan == SPEED_MEDIUM:
            return self._api.FAN_MEDIUM
        elif fan == SPEED_HIGH:
            return self._api.FAN_HIGH
        else:
            _LOGGER.warning("Melissa have no setting for %s fan mode", fan)
