"""
Sensors on Zigbee Home Automation networks.

For more details on this platform, please refer to the documentation
at https://home-assistant.io/components/sensor.zha/
"""
import logging

from homeassistant.components.sensor import DOMAIN
from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from .core.const import (
    DATA_ZHA, DATA_ZHA_DISPATCHERS, ZHA_DISCOVERY_NEW, HUMIDITY, TEMPERATURE,
    ILLUMINANCE, PRESSURE, METERING, ELECTRICAL_MEASUREMENT,
    GENERIC, SENSOR_TYPE, LISTENER_ATTRIBUTE, LISTENER_ACTIVE_POWER,
    SIGNAL_ATTR_UPDATED, SIGNAL_STATE_ATTR)
from .entity import ZhaEntity

_LOGGER = logging.getLogger(__name__)

DEPENDENCIES = ['zha']


# Formatter functions
def pass_through_formatter(value):
    """No op update function."""
    return value


def temperature_formatter(value):
    """Convert temperature data."""
    if value is None:
        return None
    return round(value / 100, 1)


def humidity_formatter(value):
    """Return the state of the entity."""
    if value is None:
        return None
    return round(float(value) / 100, 1)


def active_power_formatter(value):
    """Return the state of the entity."""
    if value is None:
        return None
    return round(float(value) / 10, 1)


def pressure_formatter(value):
    """Return the state of the entity."""
    if value is None:
        return None

    return round(float(value))


FORMATTER_FUNC_REGISTRY = {
    HUMIDITY: humidity_formatter,
    TEMPERATURE: temperature_formatter,
    PRESSURE: pressure_formatter,
    ELECTRICAL_MEASUREMENT: active_power_formatter,
    GENERIC: pass_through_formatter,
}

UNIT_REGISTRY = {
    HUMIDITY: '%',
    TEMPERATURE: TEMP_CELSIUS,
    PRESSURE: 'hPa',
    ILLUMINANCE: 'lx',
    METERING: 'W',
    ELECTRICAL_MEASUREMENT: 'W',
    GENERIC: None
}

LISTENER_REGISTRY = {
    ELECTRICAL_MEASUREMENT: LISTENER_ACTIVE_POWER,
}

POLLING_REGISTRY = {
    ELECTRICAL_MEASUREMENT: True
}

FORCE_UPDATE_REGISTRY = {
    ELECTRICAL_MEASUREMENT: True
}


async def async_setup_platform(hass, config, async_add_entities,
                               discovery_info=None):
    """Old way of setting up Zigbee Home Automation sensors."""
    pass


async def async_setup_entry(hass, config_entry, async_add_entities):
    """Set up the Zigbee Home Automation sensor from config entry."""
    async def async_discover(discovery_info):
        await _async_setup_entities(hass, config_entry, async_add_entities,
                                    [discovery_info])

    unsub = async_dispatcher_connect(
        hass, ZHA_DISCOVERY_NEW.format(DOMAIN), async_discover)
    hass.data[DATA_ZHA][DATA_ZHA_DISPATCHERS].append(unsub)

    sensors = hass.data.get(DATA_ZHA, {}).get(DOMAIN)
    if sensors is not None:
        await _async_setup_entities(hass, config_entry, async_add_entities,
                                    sensors.values())
        del hass.data[DATA_ZHA][DOMAIN]


async def _async_setup_entities(hass, config_entry, async_add_entities,
                                discovery_infos):
    """Set up the ZHA sensors."""
    entities = []
    for discovery_info in discovery_infos:
        entities.append(await make_sensor(discovery_info))

    async_add_entities(entities, update_before_add=True)


async def make_sensor(discovery_info):
    """Create ZHA sensors factory."""
    return Sensor(**discovery_info)


class Sensor(ZhaEntity):
    """Base ZHA sensor."""

    _domain = DOMAIN

    def __init__(self, unique_id, zha_device, listeners, **kwargs):
        """Init this sensor."""
        super().__init__(unique_id, zha_device, listeners, **kwargs)
        sensor_type = kwargs.get(SENSOR_TYPE, GENERIC)
        self._unit = UNIT_REGISTRY.get(sensor_type)
        self._formatter_function = FORMATTER_FUNC_REGISTRY.get(
            sensor_type,
            pass_through_formatter
        )
        self._force_update = FORCE_UPDATE_REGISTRY.get(
            sensor_type,
            False
        )
        self._should_poll = POLLING_REGISTRY.get(
            sensor_type,
            False
        )
        self._listener = self.cluster_listeners.get(
            LISTENER_REGISTRY.get(sensor_type, LISTENER_ATTRIBUTE)
        )

    async def async_added_to_hass(self):
        """Run when about to be added to hass."""
        await super().async_added_to_hass()
        await self.async_accept_signal(
            self._listener, SIGNAL_ATTR_UPDATED, self.async_set_state)
        await self.async_accept_signal(
            self._listener, SIGNAL_STATE_ATTR,
            self.async_update_state_attribute)

    @property
    def unit_of_measurement(self):
        """Return the unit of measurement of this entity."""
        return self._unit

    @property
    def state(self) -> str:
        """Return the state of the entity."""
        if self._state is None:
            return None
        if isinstance(self._state, float):
            return str(round(self._state, 2))
        return self._state

    def async_set_state(self, state):
        """Handle state update from listener."""
        self._state = self._formatter_function(state)
        self.async_schedule_update_ha_state()
