import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.const import CONF_ID, CONF_TRANSITION_LENGTH, CONF_STATE, CONF_FLASH_LENGTH, \
    CONF_EFFECT, CONF_BRIGHTNESS, CONF_RED, CONF_GREEN, CONF_BLUE, CONF_WHITE, \
    CONF_COLOR_TEMPERATURE, CONF_RANGE_FROM, CONF_RANGE_TO
from .types import DimRelativeAction, ToggleAction, LightState, LightControlAction, \
    AddressableLightState, AddressableSet, LightIsOnCondition, LightIsOffCondition


@automation.register_action('light.toggle', ToggleAction, automation.maybe_simple_id({
    cv.Required(CONF_ID): cv.use_id(LightState),
    cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
}))
def light_toggle_to_code(config, action_id, template_arg, args):
    paren = yield cg.get_variable(config[CONF_ID])
    var = cg.new_Pvariable(action_id, template_arg, paren)
    if CONF_TRANSITION_LENGTH in config:
        template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
        cg.add(var.set_transition_length(template_))
    yield var


LIGHT_CONTROL_ACTION_SCHEMA = cv.Schema({
    cv.Required(CONF_ID): cv.use_id(LightState),
    cv.Optional(CONF_STATE): cv.templatable(cv.boolean),
    cv.Exclusive(CONF_TRANSITION_LENGTH, 'transformer'):
        cv.templatable(cv.positive_time_period_milliseconds),
    cv.Exclusive(CONF_FLASH_LENGTH, 'transformer'):
        cv.templatable(cv.positive_time_period_milliseconds),
    cv.Exclusive(CONF_EFFECT, 'transformer'): cv.templatable(cv.string),
    cv.Optional(CONF_BRIGHTNESS): cv.templatable(cv.percentage),
    cv.Optional(CONF_RED): cv.templatable(cv.percentage),
    cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
    cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
    cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
    cv.Optional(CONF_COLOR_TEMPERATURE): cv.templatable(cv.color_temperature),
})
LIGHT_TURN_OFF_ACTION_SCHEMA = automation.maybe_simple_id({
    cv.Required(CONF_ID): cv.use_id(LightState),
    cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
    cv.Optional(CONF_STATE, default=False): False,
})
LIGHT_TURN_ON_ACTION_SCHEMA = automation.maybe_simple_id(LIGHT_CONTROL_ACTION_SCHEMA.extend({
    cv.Optional(CONF_STATE, default=True): True,
}))


@automation.register_action('light.turn_off', LightControlAction, LIGHT_TURN_OFF_ACTION_SCHEMA)
@automation.register_action('light.turn_on', LightControlAction, LIGHT_TURN_ON_ACTION_SCHEMA)
@automation.register_action('light.control', LightControlAction, LIGHT_CONTROL_ACTION_SCHEMA)
def light_control_to_code(config, action_id, template_arg, args):
    paren = yield cg.get_variable(config[CONF_ID])
    var = cg.new_Pvariable(action_id, template_arg, paren)
    if CONF_STATE in config:
        template_ = yield cg.templatable(config[CONF_STATE], args, bool)
        cg.add(var.set_state(template_))
    if CONF_TRANSITION_LENGTH in config:
        template_ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
        cg.add(var.set_transition_length(template_))
    if CONF_FLASH_LENGTH in config:
        template_ = yield cg.templatable(config[CONF_FLASH_LENGTH], args, cg.uint32)
        cg.add(var.set_flash_length(template_))
    if CONF_BRIGHTNESS in config:
        template_ = yield cg.templatable(config[CONF_BRIGHTNESS], args, float)
        cg.add(var.set_brightness(template_))
    if CONF_RED in config:
        template_ = yield cg.templatable(config[CONF_RED], args, float)
        cg.add(var.set_red(template_))
    if CONF_GREEN in config:
        template_ = yield cg.templatable(config[CONF_GREEN], args, float)
        cg.add(var.set_green(template_))
    if CONF_BLUE in config:
        template_ = yield cg.templatable(config[CONF_BLUE], args, float)
        cg.add(var.set_blue(template_))
    if CONF_WHITE in config:
        template_ = yield cg.templatable(config[CONF_WHITE], args, float)
        cg.add(var.set_white(template_))
    if CONF_COLOR_TEMPERATURE in config:
        template_ = yield cg.templatable(config[CONF_COLOR_TEMPERATURE], args, float)
        cg.add(var.set_color_temperature(template_))
    if CONF_EFFECT in config:
        template_ = yield cg.templatable(config[CONF_EFFECT], args, cg.std_string)
        cg.add(var.set_effect(template_))
    yield var


CONF_RELATIVE_BRIGHTNESS = 'relative_brightness'
LIGHT_DIM_RELATIVE_ACTION_SCHEMA = cv.Schema({
    cv.Required(CONF_ID): cv.use_id(LightState),
    cv.Required(CONF_RELATIVE_BRIGHTNESS): cv.templatable(cv.possibly_negative_percentage),
    cv.Optional(CONF_TRANSITION_LENGTH): cv.templatable(cv.positive_time_period_milliseconds),
})


@automation.register_action('light.dim_relative', DimRelativeAction,
                            LIGHT_DIM_RELATIVE_ACTION_SCHEMA)
def light_dim_relative_to_code(config, action_id, template_arg, args):
    paren = yield cg.get_variable(config[CONF_ID])
    var = cg.new_Pvariable(action_id, template_arg, paren)
    templ = yield cg.templatable(config[CONF_RELATIVE_BRIGHTNESS], args, float)
    cg.add(var.set_relative_brightness(templ))
    if CONF_TRANSITION_LENGTH in config:
        templ = yield cg.templatable(config[CONF_TRANSITION_LENGTH], args, cg.uint32)
        cg.add(var.set_transition_length(templ))
    yield var


LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA = cv.Schema({
    cv.Required(CONF_ID): cv.use_id(AddressableLightState),
    cv.Optional(CONF_RANGE_FROM): cv.templatable(cv.positive_int),
    cv.Optional(CONF_RANGE_TO): cv.templatable(cv.positive_int),
    cv.Optional(CONF_RED): cv.templatable(cv.percentage),
    cv.Optional(CONF_GREEN): cv.templatable(cv.percentage),
    cv.Optional(CONF_BLUE): cv.templatable(cv.percentage),
    cv.Optional(CONF_WHITE): cv.templatable(cv.percentage),
})


@automation.register_action('light.addressable_set', AddressableSet,
                            LIGHT_ADDRESSABLE_SET_ACTION_SCHEMA)
def light_addressable_set_to_code(config, action_id, template_arg, args):
    paren = yield cg.get_variable(config[CONF_ID])
    var = cg.new_Pvariable(action_id, template_arg, paren)
    if CONF_RANGE_FROM in config:
        templ = yield cg.templatable(config[CONF_RANGE_FROM], args, cg.int32)
        cg.add(var.set_range_from(templ))
    if CONF_RANGE_TO in config:
        templ = yield cg.templatable(config[CONF_RANGE_TO], args, cg.int32)
        cg.add(var.set_range_to(templ))

    def rgbw_to_exp(x):
        return int(round(x * 255))

    if CONF_RED in config:
        templ = yield cg.templatable(config[CONF_RED], args, cg.uint8, to_exp=rgbw_to_exp)
        cg.add(var.set_red(templ))
    if CONF_GREEN in config:
        templ = yield cg.templatable(config[CONF_GREEN], args, cg.uint8, to_exp=rgbw_to_exp)
        cg.add(var.set_green(templ))
    if CONF_BLUE in config:
        templ = yield cg.templatable(config[CONF_BLUE], args, cg.uint8, to_exp=rgbw_to_exp)
        cg.add(var.set_blue(templ))
    if CONF_WHITE in config:
        templ = yield cg.templatable(config[CONF_WHITE], args, cg.uint8, to_exp=rgbw_to_exp)
        cg.add(var.set_white(templ))
    yield var


@automation.register_condition('light.is_on', LightIsOnCondition,
                               automation.maybe_simple_id({
                                   cv.Required(CONF_ID): cv.use_id(LightState),
                               }))
@automation.register_condition('light.is_off', LightIsOffCondition,
                               automation.maybe_simple_id({
                                   cv.Required(CONF_ID): cv.use_id(LightState),
                               }))
def light_is_on_off_to_code(config, condition_id, template_arg, args):
    paren = yield cg.get_variable(config[CONF_ID])
    yield cg.new_Pvariable(condition_id, template_arg, paren)
