"""
Support for magicseaweed data from magicseaweed.com.

For more details about this platform, please refer to the documentation at
https://home-assistant.io/components/sensor.magicseaweed/
"""
from datetime import timedelta
import logging
import voluptuous as vol

from homeassistant.components.sensor import PLATFORM_SCHEMA
from homeassistant.const import (
    CONF_API_KEY, CONF_NAME, CONF_MONITORED_CONDITIONS, ATTR_ATTRIBUTION)
import homeassistant.helpers.config_validation as cv
import homeassistant.util.dt as dt_util
from homeassistant.helpers.entity import Entity
from homeassistant.util import Throttle

REQUIREMENTS = ['magicseaweed==1.0.3']

_LOGGER = logging.getLogger(__name__)

CONF_HOURS = 'hours'
CONF_SPOT_ID = 'spot_id'
CONF_UNITS = 'units'

DEFAULT_UNIT = 'us'
DEFAULT_NAME = 'MSW'
DEFAULT_ATTRIBUTION = "Data provided by magicseaweed.com"

ICON = 'mdi:waves'

HOURS = ['12AM', '3AM', '6AM', '9AM', '12PM', '3PM', '6PM', '9PM']

SENSOR_TYPES = {
    'max_breaking_swell': ['Max'],
    'min_breaking_swell': ['Min'],
    'swell_forecast': ['Forecast'],
}

UNITS = ['eu', 'uk', 'us']

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Required(CONF_MONITORED_CONDITIONS):
        vol.All(cv.ensure_list, [vol.In(SENSOR_TYPES)]),
    vol.Required(CONF_API_KEY): cv.string,
    vol.Required(CONF_SPOT_ID): vol.All(cv.ensure_list, [cv.string]),
    vol.Optional(CONF_HOURS, default=None):
        vol.All(cv.ensure_list, [vol.In(HOURS)]),
    vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
    vol.Optional(CONF_UNITS): vol.In(UNITS),
})

# Return cached results if last scan was less then this time ago.
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=30)


def setup_platform(hass, config, add_entities, discovery_info=None):
    """Set up the Magicseaweed sensor."""
    name = config.get(CONF_NAME)
    spot_id = config[CONF_SPOT_ID]
    api_key = config[CONF_API_KEY]
    hours = config.get(CONF_HOURS)

    if CONF_UNITS in config:
        units = config.get(CONF_UNITS)
    elif hass.config.units.is_metric:
        units = UNITS[0]
    else:
        units = UNITS[2]

    forecast_data = MagicSeaweedData(
        api_key=api_key,
        spot_id=spot_id,
        units=units)
    forecast_data.update()

    # If connection failed don't setup platform.
    if forecast_data.currently is None or forecast_data.hourly is None:
        return

    sensors = []
    for variable in config[CONF_MONITORED_CONDITIONS]:
        sensors.append(MagicSeaweedSensor(forecast_data, variable, name,
                                          units))
        if 'forecast' not in variable and hours is not None:
            for hour in hours:
                sensors.append(MagicSeaweedSensor(
                    forecast_data, variable, name, units, hour))
    add_entities(sensors, True)


class MagicSeaweedSensor(Entity):
    """Implementation of a MagicSeaweed sensor."""

    def __init__(self, forecast_data, sensor_type, name, unit_system,
                 hour=None):
        """Initialize the sensor."""
        self.client_name = name
        self.data = forecast_data
        self.hour = hour
        self.type = sensor_type
        self._attrs = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
        self._name = SENSOR_TYPES[sensor_type][0]
        self._icon = None
        self._state = None
        self._unit_system = unit_system
        self._unit_of_measurement = None

    @property
    def name(self):
        """Return the name of the sensor."""
        if self.hour is None and 'forecast' in self.type:
            return "{} {}".format(self.client_name, self._name)
        if self.hour is None:
            return "Current {} {}".format(self.client_name, self._name)
        return "{} {} {}".format(
            self.hour, self.client_name, self._name)

    @property
    def state(self):
        """Return the state of the sensor."""
        return self._state

    @property
    def unit_system(self):
        """Return the unit system of this entity."""
        return self._unit_system

    @property
    def unit_of_measurement(self):
        """Return the unit of measurement of this entity, if any."""
        return self._unit_of_measurement

    @property
    def icon(self):
        """Return the entity weather icon, if any."""
        return ICON

    @property
    def device_state_attributes(self):
        """Return the state attributes."""
        return self._attrs

    def update(self):
        """Get the latest data from Magicseaweed and updates the states."""
        self.data.update()
        if self.hour is None:
            forecast = self.data.currently
        else:
            forecast = self.data.hourly[self.hour]

        self._unit_of_measurement = forecast.swell_unit
        if self.type == 'min_breaking_swell':
            self._state = forecast.swell_minBreakingHeight
        elif self.type == 'max_breaking_swell':
            self._state = forecast.swell_maxBreakingHeight
        elif self.type == 'swell_forecast':
            summary = "{} - {}".format(
                forecast.swell_minBreakingHeight,
                forecast.swell_maxBreakingHeight)
            self._state = summary
            if self.hour is None:
                for hour, data in self.data.hourly.items():
                    occurs = hour
                    hr_summary = "{} - {} {}".format(
                        data.swell_minBreakingHeight,
                        data.swell_maxBreakingHeight,
                        data.swell_unit)
                    self._attrs[occurs] = hr_summary

        if self.type != 'swell_forecast':
            self._attrs.update(forecast.attrs)


class MagicSeaweedData:
    """Get the latest data from MagicSeaweed."""

    def __init__(self, api_key, spot_id, units):
        """Initialize the data object."""
        import magicseaweed
        self._msw = magicseaweed.MSW_Forecast(api_key, spot_id,
                                              None, units)
        self.currently = None
        self.hourly = {}

        # Apply throttling to methods using configured interval
        self.update = Throttle(MIN_TIME_BETWEEN_UPDATES)(self._update)

    def _update(self):
        """Get the latest data from MagicSeaweed."""
        try:
            forecasts = self._msw.get_future()
            self.currently = forecasts.data[0]
            for forecast in forecasts.data[:8]:
                hour = dt_util.utc_from_timestamp(
                    forecast.localTimestamp).strftime("%-I%p")
                self.hourly[hour] = forecast
        except ConnectionError:
            _LOGGER.error("Unable to retrieve data from Magicseaweed")
