import datetime

import apis
from helper import scale, pos_to_color, rgb_dec565
from pages import LuiPagesGen

class LuiController(object):

    def __init__(self, config, send_mqtt_msg):
        self._config = config
        self._send_mqtt_msg = send_mqtt_msg

        self._current_card = self._config._config_screensaver

        self._previous_cards = []
        # first card (default, after startup)
        self._previous_cards.append(self._config.get_default_card())
        self._pages_gen = LuiPagesGen(config, send_mqtt_msg)

        # send panel back to startup page on restart of this script
        self._pages_gen.page_type("pageStartup")

        # calculate current brightness
        self.current_screensaver_brightness = self.calc_current_brightness(self._config.get("sleepBrightness"))
        self.current_screen_brightness      = self.calc_current_brightness(self._config.get("screenBrightness"))
        # register callbacks
        self.register_callbacks()


    def startup(self):
        apis.ha_api.log(f"Startup Event")
        # send time and date on startup
        self._pages_gen.update_time("")
        self._pages_gen.update_date("")
        
        # set current screensaver brightness
        self.update_screensaver_brightness(kwargs={"ssbr": self.current_screensaver_brightness, "sbr": self.current_screen_brightness})
        
        # send panel to screensaver
        self._pages_gen.render_card(self._current_card)


    def update_screensaver_brightness_state_callback(self, entity, attribute, old, new, kwargs):
        self.current_screensaver_brightness = self.calc_current_brightness(self._config.get("sleepBrightness"))
        self.current_screen_brightness      = self.calc_current_brightness(self._config.get("screenBrightness"))
        self.update_screensaver_brightness(kwargs={"ssbr": self.current_screensaver_brightness, "sbr": self.current_screen_brightness})
        
    def update_screensaver_brightness(self, kwargs):
        bst = self._config.get("sleepTracking")
        sleepOverride = self._config.get("sleepOverride")
        sOEntity = None
        sOBrightness = None
        if sleepOverride is not None and type(sleepOverride) is dict:
            sOEntity = sleepOverride["entity"]
            sOBrightness = sleepOverride["brightness"]

        sleepBrightness = 0
        brightness = self.calc_current_brightness(self._config.get("screenBrightness"))

        if bst is not None and apis.ha_api.entity_exists(bst) and apis.ha_api.get_entity(bst).state in self._config.get("sleepTrackingZones"):
            apis.ha_api.log(f"sleepTracking setting brightness to 0")
            sleepBrightness = 0

        elif sOEntity is not None and sOBrightness is not None and apis.ha_api.entity_exists(sOEntity) and apis.ha_api.get_entity(sOEntity).state in ["on", "true", "home"]:
            apis.ha_api.log(f"sleepOverride setting brightness to {sOBrightness}")
            sleepBrightness = sOBrightness
        
        else:
            self.current_screensaver_brightness = kwargs['ssbr']
            sleepBrightness                     = self.current_screensaver_brightness
            self.current_screen_brightness      = kwargs['sbr']
            brightness                          = self.current_screen_brightness
        # same value for both values will break sleep timer of the firmware
        if sleepBrightness==brightness:
            sleepBrightness = sleepBrightness-1

        # background color
        dbc = 0
        defaultBackgroundColor = self._config.get("defaultBackgroundColor")
        if type(defaultBackgroundColor) is str:
            if defaultBackgroundColor == "ha-dark":
                dbc = 6371
            elif defaultBackgroundColor == "black":
                dbc = 0
        elif type(defaultBackgroundColor) is list:
            dbc = rgb_dec565(defaultBackgroundColor)

        featureExperimentalSliders=0
        if self._config.get("featureExperimentalSliders"):
            featureExperimentalSliders=1

        self._send_mqtt_msg(f"dimmode~{sleepBrightness}~{brightness}~{dbc}~~{featureExperimentalSliders}")
        
    def calc_current_brightness(self, sleep_brightness_config):
        current_screensaver_brightness = 20
        #sleep_brightness_config = self._config.get("sleepBrightness")
        # set brightness of screensaver
        if type(sleep_brightness_config) == int:
            current_screensaver_brightness = sleep_brightness_config
        elif type(sleep_brightness_config) == str:
                current_screensaver_brightness = int(float(apis.ha_api.get_state(sleep_brightness_config)))
        elif type(sleep_brightness_config) == list:
            sorted_timesets = sorted(sleep_brightness_config, key=lambda d: apis.ha_api.parse_time(d['time']))
            # calc current screensaver brightness
            found_current_dim_value = False
            for i in range(len(sorted_timesets)):
                found = apis.ha_api.now_is_between(sorted_timesets[i-1]['time'], sorted_timesets[i]['time'])
                if found:
                    found_current_dim_value = True
                    current_screensaver_brightness = sorted_timesets[i-1]['value']
            # still no dim value
            if not found_current_dim_value:
                apis.ha_api.log("Chooseing %s as fallback", sorted_timesets[0])
                current_screensaver_brightness = sorted_timesets[0]["value"]
        return current_screensaver_brightness
    
    def register_callbacks(self):
        # time update callback
        time = datetime.time(0, 0, 0)
        apis.ha_api.run_minutely(self._pages_gen.update_time, time)

        # Setup date callback
        apis.ha_api.run_hourly(self._pages_gen.update_date, time)

        # register callbacks for each time
        if type(self._config.get("sleepBrightness")) == list:
            for index, timeset in enumerate(self._config.get("sleepBrightness")):
                apis.ha_api.run_daily(self.update_screensaver_brightness, timeset["time"], ssbr=timeset["value"], sbr=self.current_screen_brightness)        

        # call update_screensaver_brightness on changes of entity configured in sleepTracking
        bst = self._config.get("sleepTracking")
        if bst is not None and apis.ha_api.entity_exists(bst):
            apis.ha_api.listen_state(self.update_screensaver_brightness_state_callback, entity_id=bst)
        
        # call update_screensaver_brightness on entity configured in sleepOverride
        sleepOverride = self._config.get("sleepOverride")
        if sleepOverride is not None and type(sleepOverride) is dict  and sleepOverride["entity"] is not None and sleepOverride["brightness"] is not None and apis.ha_api.entity_exists(sleepOverride["entity"]):
            apis.ha_api.log(f"Configuring Sleep Override. Config is {sleepOverride}")
            apis.ha_api.listen_state(self.update_screensaver_brightness_state_callback, entity_id=sleepOverride["entity"])

        # register callback for state changes on tracked value (for input_number) - sleepBrightness
        sleep_brightness_config = self._config.get("sleepBrightness")
        if type(sleep_brightness_config) == str and apis.ha_api.entity_exists(sleep_brightness_config):
            apis.ha_api.listen_state(self.update_screensaver_brightness_state_callback, entity_id=sleep_brightness_config)
        # register callback for state changes on tracked value (for input_number) - screenBrightness
        screen_brightness_config = self._config.get("screenBrightness")
        if type(screen_brightness_config) == str and apis.ha_api.entity_exists(screen_brightness_config):
            apis.ha_api.listen_state(self.update_screensaver_brightness_state_callback, entity_id=screen_brightness_config)   
    
        items = self._config.get_all_entity_names()
        apis.ha_api.log(f"gtest123: {items}")
        prefixes = ("navigate.", "delete", "iText")
        items = [x for x in items if not (x is None or x.startswith(prefixes))]
        apis.ha_api.log(f"Registering callbacks for the following items: {items}")
        for item in items:
            if apis.ha_api.entity_exists(item):
                apis.ha_api.listen_state(self.state_change_callback, entity_id=item, attribute="all")

    def state_change_callback(self, entity, attribute, old, new, kwargs):
        #apis.ha_api.log(f"Got callback for: {entity}")
        #apis.ha_api.log(f"Current page has the following items: {self._current_card.get_entity_names(uuid=True)}")
        entities_on_card = self._current_card.get_entity_names(uuid=True)

        res_uuid = "uuid.notfound"
        if entity in sum(entities_on_card.values(), []):
            for uuid, names in entities_on_card.items():
                #apis.ha_api.log(f"test124 items: {entities_on_card.items()} names: {names}")
                #apis.ha_api.log(f"State change callback matched for entity on current page: {names}")
                if entity in names:
                    res_uuid = uuid

            #apis.ha_api.log(f"Callback Entity is on current page: {entity}")
            self._pages_gen.render_card(self._current_card, send_page_type=False)
            # send detail page update, just in case
            if self._current_card.cardType in ["cardGrid", "cardGrid2", "cardEntities", "cardMedia"]:
                if entity.startswith("light"):
                    self._pages_gen.generate_light_detail_page(res_uuid)
                if entity.startswith("cover"):
                    self._pages_gen.generate_shutter_detail_page(entity)
                if entity.startswith("fan"):
                    self._pages_gen.generate_fan_detail_page(entity)
                if entity.startswith("input_select") or entity.startswith("select"):
                    self._pages_gen.generate_input_select_detail_page(entity)
                if entity.startswith("media_player"):
                    self._pages_gen.generate_input_select_detail_page(entity)
                if entity.startswith("timer"):
                    self._pages_gen.generate_timer_detail_page(entity)
            if self._current_card.cardType == "cardThermo":
                if entity.startswith("climate"):
                    self._pages_gen.generate_thermo_detail_page(entity)


    def detail_open(self, detail_type, entity_id):
        if detail_type == "popupShutter":
            self._pages_gen.generate_shutter_detail_page(entity_id, True)
        if detail_type == "popupLight":
            self._pages_gen.generate_light_detail_page(entity_id, True)
        if detail_type == "popupFan":
            self._pages_gen.generate_fan_detail_page(entity_id, True)
        if detail_type == "popupThermo":
            self._pages_gen.generate_thermo_detail_page(entity_id, True)
        if detail_type == "popupInSel":
            self._pages_gen.generate_input_select_detail_page(entity_id, True)
        if detail_type == "popupTimer":
            self._pages_gen.generate_timer_detail_page(entity_id, True)

    def button_press(self, entity_id, button_type, value):
        apis.ha_api.log(f"Button Press Event; entity_id: {entity_id}; button_type: {button_type}; value: {value} ")
        if entity_id.startswith('uuid'):
            entity_config = self._config._config_entites_table.get(entity_id)
            if entity_config is not None:
                entity_id = entity_config.entityId
        # internal buttons
        if entity_id == "screensaver" and button_type == "bExit":
            # get default card if there is one
            defaultCard = self._config.get("screensaver.defaultCard")
            if defaultCard is not None:
                defaultCard = apis.ha_api.render_template(defaultCard)
                apis.ha_api.log(f"Searching for the following page as defaultPage: {defaultCard}")
                dstCard = self._config.search_card(defaultCard)
                apis.ha_api.log(f"Result for the following page as defaultPage: {dstCard}")
                if dstCard is not None:
                    self._previous_cards = []
                    self._previous_cards.append(dstCard)
            # set _previous_cards to default page in case it's empty
            if len(self._previous_cards) == 0:
                self._previous_cards.append(self._config.get_default_card())
            # check for double tap if configured and render current page
            if self._config.get("screensaver.doubleTapToUnlock") and int(value) >= 2:
                self._current_card = self._previous_cards.pop()
                self._pages_gen.render_card(self._current_card)
            elif not self._config.get("screensaver.doubleTapToUnlock"):
                self._current_card = self._previous_cards.pop()
                self._pages_gen.render_card(self._current_card)
            return
            
        if button_type == "sleepReached":
            self._previous_cards.append(self._current_card)
            self._current_card = self._config._config_screensaver
            self._pages_gen.render_card(self._current_card)
            return

        if button_type == "bExit":
            self._pages_gen.render_card(self._current_card)
        
        elif entity_id == "updateDisplayNoYes" and value == "no":
            self._pages_gen.render_card(self._current_card)

        # buttons with actions on HA
        if button_type == "OnOff":
            if value == "1":
                apis.ha_api.turn_on(entity_id)
            else:
                apis.ha_api.turn_off(entity_id)

        if button_type == "number-set":
            if entity_id.startswith('fan'):
                entity = apis.ha_api.get_entity(entity_id)
                value = float(value)*float(entity.attributes.get("percentage_step", 0))
                entity.call_service("set_percentage", percentage=value)
            else:
                apis.ha_api.get_entity(entity_id).call_service("set_value", value=value)

        # for shutter / covers
        if button_type == "up":
            apis.ha_api.get_entity(entity_id).call_service("open_cover")
        if button_type == "stop":
            apis.ha_api.get_entity(entity_id).call_service("stop_cover")
        if button_type == "down":
            apis.ha_api.get_entity(entity_id).call_service("close_cover")
        if button_type == "positionSlider":
            pos = int(value)
            apis.ha_api.get_entity(entity_id).call_service("set_cover_position", position=pos)
        if button_type == "tiltOpen":
            apis.ha_api.get_entity(entity_id).call_service("open_cover_tilt")
        if button_type == "tiltStop":
            apis.ha_api.get_entity(entity_id).call_service("stop_cover_tilt")
        if button_type == "tiltClose":
            apis.ha_api.get_entity(entity_id).call_service("close_cover_tilt")
        if button_type == "tiltSlider":
            pos = int(value)
            apis.ha_api.get_entity(entity_id).call_service("set_cover_tilt_position", tilt_position=pos)


        if button_type == "button":
            if entity_id.startswith('navigate'):
                # internal navigation for next/prev
                if entity_id.startswith('navigate.uuid'):
                    dstCard = self._config.get_card_by_uuid(entity_id.replace('navigate.',''))
                # internal for navigation to nested pages
                else:
                    dstCard = self._config.search_card(entity_id)
                if dstCard is not None:
                    if dstCard.hidden:
                        self._previous_cards.append(self._current_card)
                    self._current_card = dstCard
                    self._pages_gen.render_card(self._current_card)
                else:
                    apis.ha_api.log(f"No page with key {entity_id} found")
            if entity_id.startswith('navUp'):
                if self._previous_cards:
                    self._current_card = self._previous_cards.pop()
                else:
                    self._current_card = self._config.get_default_card()
                self._pages_gen.render_card(self._current_card)
            if entity_id.startswith('navPrev'):
                if self._current_card.uuid_prev:
                    self._current_card = self._config.get_card_by_uuid(self._current_card.uuid_prev)
                    self._pages_gen.render_card(self._current_card)
            if entity_id.startswith('navNext'):
                if self._current_card.uuid_next:
                    self._current_card = self._config.get_card_by_uuid(self._current_card.uuid_next)
                    self._pages_gen.render_card(self._current_card)
            elif entity_id.startswith('scene'):
                apis.ha_api.get_entity(entity_id).call_service("turn_on")
            elif entity_id.startswith('script'):
                apis.ha_api.get_entity(entity_id).call_service("turn_on")
            elif entity_id.startswith('light') or entity_id.startswith('switch') or entity_id.startswith('input_boolean') or entity_id.startswith('automation') or entity_id.startswith('fan'):
                apis.ha_api.get_entity(entity_id).call_service("toggle")
            elif entity_id.startswith('lock'):
                if apis.ha_api.get_entity(entity_id).state == "locked":
                    apis.ha_api.get_entity(entity_id).call_service("unlock")
                else:
                    apis.ha_api.get_entity(entity_id).call_service("lock")
            elif entity_id.startswith('button') or entity_id.startswith('input_button'):
                apis.ha_api.get_entity(entity_id).call_service("press")
            elif entity_id.startswith('input_select'):
                apis.ha_api.get_entity(entity_id).call_service("select_next")
            elif entity_id.startswith('vacuum'):
                if apis.ha_api.get_entity(entity_id).state == "docked":
                    apis.ha_api.get_entity(entity_id).call_service("start")
                else:
                    apis.ha_api.get_entity(entity_id).call_service("return_to_base")
            elif entity_id.startswith('service'):
                apis.ha_api.call_service(entity_id.replace('service.', '', 1).replace('.','/', 1), **entity_config.data)

        # for media page
        if button_type == "media-next":
            apis.ha_api.get_entity(entity_id).call_service("media_next_track")
        if button_type == "media-back":
            apis.ha_api.get_entity(entity_id).call_service("media_previous_track")
        if button_type == "media-pause":
            apis.ha_api.get_entity(entity_id).call_service("media_play_pause")
        if button_type == "media-OnOff":
            if apis.ha_api.get_entity(entity_id).state == "off":
                apis.ha_api.get_entity(entity_id).call_service("turn_on")
            else:
                apis.ha_api.get_entity(entity_id).call_service("turn_off")
        if button_type == "media-shuffle":
            suffle = not apis.ha_api.get_entity(entity_id).attributes.shuffle
            apis.ha_api.get_entity(entity_id).call_service("shuffle_set", shuffle=suffle)
        if button_type == "volumeSlider":
            pos = int(value)
            # HA wants this value between 0 and 1 as float
            pos = pos/100
            apis.ha_api.get_entity(entity_id).call_service("volume_set", volume_level=pos)
        if button_type == "speaker-sel":
            apis.ha_api.get_entity(entity_id).call_service("select_source", source=value)
            
        # for light detail page
        if button_type == "brightnessSlider":
            # scale 0-100 to ha brightness range
            brightness = int(scale(int(value),(0,100),(0,255)))
            apis.ha_api.get_entity(entity_id).call_service("turn_on", brightness=brightness)
        if button_type == "colorTempSlider":
            entity = apis.ha_api.get_entity(entity_id)
            #scale 0-100 from slider to color range of lamp
            color_val = scale(int(value), (0, 100), (entity.attributes.min_mireds, entity.attributes.max_mireds))
            apis.ha_api.get_entity(entity_id).call_service("turn_on", color_temp=color_val)
        if button_type == "colorWheel":
            apis.ha_api.log(value)
            value = value.split('|')
            color = pos_to_color(int(value[0]), int(value[1]), int(value[2]))
            apis.ha_api.log(color)
            apis.ha_api.get_entity(entity_id).call_service("turn_on", rgb_color=color)
        
        # for climate page
        if button_type == "tempUpd":
            temp = int(value)/10
            apis.ha_api.get_entity(entity_id).call_service("set_temperature", temperature=temp)
        if button_type == "tempUpdHighLow":
            value = value.split("|")
            temp_high = int(value[0])/10
            temp_low  = int(value[1])/10
            apis.ha_api.get_entity(entity_id).call_service("set_temperature", target_temp_high=temp_high, target_temp_low=temp_low)
        if button_type == "hvac_action":
            apis.ha_api.get_entity(entity_id).call_service("set_hvac_mode", hvac_mode=value)
            
        # for alarm page
        if button_type in ["disarm", "arm_home", "arm_away", "arm_night", "arm_vacation"]:
            apis.ha_api.get_entity(entity_id).call_service(f"alarm_{button_type}", code=value)
        if button_type == "opnSensorNotify":
            msg = ""
            entity = apis.ha_api.get_entity(entity_id)
            if "open_sensors" in entity.attributes and entity.attributes.open_sensors is not None:
                for e in entity.attributes.open_sensors:
                    msg += f"- {apis.ha_api.get_entity(e).attributes.friendly_name}\r\n"
            self._pages_gen.send_message_page("opnSensorNotifyRes", "", msg, "", "")

        # for cardUnlock
        if button_type == "cardUnlock-unlock":
            curCard = self._config.get_card_by_uuid(entity_id.replace('navigate.',''))
            if curCard is not None:
                if int(curCard.raw_config.get("pin")) == int(value):
                    dstCard = self._config.search_card(curCard.raw_config.get("destination"))
                    if dstCard is not None:
                        if dstCard.hidden:
                            self._previous_cards.append(self._current_card)
                        self._current_card = dstCard
                        self._pages_gen.render_card(self._current_card)

        if button_type == "mode-preset_modes":
            entity = apis.ha_api.get_entity(entity_id)
            preset_mode = entity.attributes.preset_modes[int(value)]
            entity.call_service("set_preset_mode", preset_mode=preset_mode)

        if button_type == "mode-swing_modes":
            entity = apis.ha_api.get_entity(entity_id)
            swing_mode = entity.attributes.swing_modes[int(value)]
            entity.call_service("set_swing_mode", swing_mode=swing_mode)

        if button_type == "mode-fan_modes":
            entity = apis.ha_api.get_entity(entity_id)
            fan_mode = entity.attributes.fan_modes[int(value)]
            entity.call_service("set_fan_mode", fan_mode=fan_mode)

        if button_type == "mode-input_select":
            entity = apis.ha_api.get_entity(entity_id)
            option = entity.attributes.options[int(value)]
            entity.call_service("select_option", option=option)

        if button_type == "mode-light":
            if entity_id.startswith('uuid'):
                entity_config = self._config._config_entites_table.get(entity_id)
                entity_id = entity_config.entityId
            entity = apis.ha_api.get_entity(entity_id)
            options_list = entity_config.entity_input_config.get("effectList")
            if options_list is not None:
                option = options_list[int(value)]
            else:
                option = entity.attributes.effect_list[int(value)]
            entity.call_service("turn_on", effect=option)
            
        if button_type == "mode-media_player":
            entity = apis.ha_api.get_entity(entity_id)
            option = entity.attributes.source_list[int(value)]
            entity.call_service("select_source", source=option)
            
        # timer detail page
        if button_type == "timer-start":
            if value is not None:
                apis.ha_api.get_entity(entity_id).call_service("start", duration=value)
            else:
                apis.ha_api.get_entity(entity_id).call_service("start")
        if button_type == "timer-cancel":
            apis.ha_api.get_entity(entity_id).call_service("cancel")
        if button_type == "timer-pause":
            apis.ha_api.get_entity(entity_id).call_service("pause")
        if button_type == "timer-finish":
            apis.ha_api.get_entity(entity_id).call_service("finish")
