"""
homeassistant.components.light.limitlessled
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Support for LimitlessLED bulbs.

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

from homeassistant.components.light import (
    ATTR_BRIGHTNESS, ATTR_COLOR_TEMP, ATTR_EFFECT, ATTR_FLASH, ATTR_RGB_COLOR,
    ATTR_TRANSITION, EFFECT_COLORLOOP, EFFECT_WHITE, FLASH_LONG, Light)

_LOGGER = logging.getLogger(__name__)
REQUIREMENTS = ['limitlessled==1.0.0']
RGB_BOUNDARY = 40
DEFAULT_TRANSITION = 0
DEFAULT_PORT = 8899
DEFAULT_VERSION = 5
DEFAULT_LED_TYPE = 'rgbw'
WHITE = [255, 255, 255]


def rewrite_legacy(config):
    """ Rewrite legacy configuration to new format. """
    bridges = config.get('bridges', [config])
    new_bridges = []
    for bridge_conf in bridges:
        groups = []
        if 'groups' in bridge_conf:
            groups = bridge_conf['groups']
        else:
            _LOGGER.warning("Legacy configuration format detected")
            for i in range(1, 5):
                name_key = 'group_%d_name' % i
                if name_key in bridge_conf:
                    groups.append({
                        'number': i,
                        'type':  bridge_conf.get('group_%d_type' % i,
                                                 DEFAULT_LED_TYPE),
                        'name': bridge_conf.get(name_key)
                    })
        new_bridges.append({
            'host': bridge_conf.get('host'),
            'groups': groups
        })
    return {'bridges': new_bridges}


def setup_platform(hass, config, add_devices_callback, discovery_info=None):
    """ Gets the LimitlessLED lights. """
    from limitlessled.bridge import Bridge

    # Two legacy configuration formats are supported to
    # maintain backwards compatibility.
    config = rewrite_legacy(config)

    # Use the expanded configuration format.
    lights = []
    for bridge_conf in config.get('bridges'):
        bridge = Bridge(bridge_conf.get('host'),
                        port=bridge_conf.get('port', DEFAULT_PORT),
                        version=bridge_conf.get('version', DEFAULT_VERSION))
        for group_conf in bridge_conf.get('groups'):
            group = bridge.add_group(group_conf.get('number'),
                                     group_conf.get('name'),
                                     group_conf.get('type', DEFAULT_LED_TYPE))
            lights.append(LimitlessLEDGroup.factory(group))
    add_devices_callback(lights)


def state(new_state):
    """ State decorator.

    Specify True (turn on) or False (turn off).
    """
    def decorator(function):
        """ Decorator function. """
        # pylint: disable=no-member,protected-access
        def wrapper(self, **kwargs):
            """ Wrap a group state change. """
            from limitlessled.pipeline import Pipeline
            pipeline = Pipeline()
            transition_time = DEFAULT_TRANSITION
            # Stop any repeating pipeline.
            if self.repeating:
                self.repeating = False
                self.group.stop()
            # Not on and should be? Turn on.
            if not self.is_on and new_state is True:
                pipeline.on()
            # Set transition time.
            if ATTR_TRANSITION in kwargs:
                transition_time = kwargs[ATTR_TRANSITION]
            # Do group type-specific work.
            function(self, transition_time, pipeline, **kwargs)
            # Update state.
            self._is_on = new_state
            self.group.enqueue(pipeline)
            self.update_ha_state()
        return wrapper
    return decorator


class LimitlessLEDGroup(Light):
    """ LimitessLED group. """
    def __init__(self, group):
        """ Initialize a group. """
        self.group = group
        self.repeating = False
        self._is_on = False
        self._brightness = None

    @staticmethod
    def factory(group):
        """ Produce LimitlessLEDGroup objects. """
        from limitlessled.group.rgbw import RgbwGroup
        from limitlessled.group.white import WhiteGroup
        if isinstance(group, WhiteGroup):
            return LimitlessLEDWhiteGroup(group)
        elif isinstance(group, RgbwGroup):
            return LimitlessLEDRGBWGroup(group)

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

        LimitlessLED state cannot be fetched.
        """
        return False

    @property
    def name(self):
        """ Returns the name of the group. """
        return self.group.name

    @property
    def is_on(self):
        """ True if device is on. """
        return self._is_on

    @property
    def brightness(self):
        """ Brightness property. """
        return self._brightness

    @state(False)
    def turn_off(self, transition_time, pipeline, **kwargs):
        """ Turn off a group. """
        if self.is_on:
            pipeline.transition(transition_time, brightness=0.0).off()


class LimitlessLEDWhiteGroup(LimitlessLEDGroup):
    """ LimitlessLED White group. """
    def __init__(self, group):
        """ Initialize White group. """
        super().__init__(group)
        # Initialize group with known values.
        self.group.on = True
        self.group.temperature = 1.0
        self.group.brightness = 0.0
        self._brightness = _to_hass_brightness(1.0)
        self._temperature = _to_hass_temperature(self.group.temperature)
        self.group.on = False

    @property
    def color_temp(self):
        """ Temperature property. """
        return self._temperature

    @state(True)
    def turn_on(self, transition_time, pipeline, **kwargs):
        """ Turn on (or adjust property of) a group. """
        # Check arguments.
        if ATTR_BRIGHTNESS in kwargs:
            self._brightness = kwargs[ATTR_BRIGHTNESS]
        if ATTR_COLOR_TEMP in kwargs:
            self._temperature = kwargs[ATTR_COLOR_TEMP]
        # Set up transition.
        pipeline.transition(transition_time,
                            brightness=_from_hass_brightness(
                                self._brightness),
                            temperature=_from_hass_temperature(
                                self._temperature))


class LimitlessLEDRGBWGroup(LimitlessLEDGroup):
    """ LimitlessLED RGBW group. """
    def __init__(self, group):
        """ Initialize RGBW group. """
        super().__init__(group)
        # Initialize group with known values.
        self.group.on = True
        self.group.white()
        self._color = WHITE
        self.group.brightness = 0.0
        self._brightness = _to_hass_brightness(1.0)
        self.group.on = False

    @property
    def rgb_color(self):
        """ Color property. """
        return self._color

    @state(True)
    def turn_on(self, transition_time, pipeline, **kwargs):
        """ Turn on (or adjust property of) a group. """
        from limitlessled.presets import COLORLOOP
        # Check arguments.
        if ATTR_BRIGHTNESS in kwargs:
            self._brightness = kwargs[ATTR_BRIGHTNESS]
        if ATTR_RGB_COLOR in kwargs:
            self._color = kwargs[ATTR_RGB_COLOR]
        # White is a special case.
        if min(self._color) > 256 - RGB_BOUNDARY:
            pipeline.white()
            self._color = WHITE
        # Set up transition.
        pipeline.transition(transition_time,
                            brightness=_from_hass_brightness(
                                self._brightness),
                            color=_from_hass_color(self._color))
        # Flash.
        if ATTR_FLASH in kwargs:
            duration = 0
            if kwargs[ATTR_FLASH] == FLASH_LONG:
                duration = 1
            pipeline.flash(duration=duration)
        # Add effects.
        if ATTR_EFFECT in kwargs:
            if kwargs[ATTR_EFFECT] == EFFECT_COLORLOOP:
                self.repeating = True
                pipeline.append(COLORLOOP)
            if kwargs[ATTR_EFFECT] == EFFECT_WHITE:
                pipeline.white()
                self._color = WHITE


def _from_hass_temperature(temperature):
    """ Convert Home Assistant color temperature
    units to percentage.
    """
    return (temperature - 154) / 346


def _to_hass_temperature(temperature):
    """ Convert percentage to Home Assistant
    color temperature units.
    """
    return int(temperature * 346) + 154


def _from_hass_brightness(brightness):
    """ Convert Home Assistant brightness units
    to percentage.
    """
    return brightness / 255


def _to_hass_brightness(brightness):
    """ Convert percentage to Home Assistant
    brightness units.
    """
    return int(brightness * 255)


def _from_hass_color(color):
    """ Convert Home Assistant RGB list
    to Color tuple.
    """
    from limitlessled import Color
    return Color(*tuple(color))


def _to_hass_color(color):
    """ Convert from Color tuple to
    Home Assistant RGB list.
    """
    return list([int(c) for c in color])
