from __future__ import annotations
import logging
from typing import Callable, Optional, TYPE_CHECKING, Union
import pygame
import pygame_gui
from pygame import Rect
from pygame_gui import UIManager
from pygame_gui.core import ObjectID
from pygame_gui.elements import UIButton, UIDropDownMenu, UIImage, UITextBox
from scripts.engine.core.utility import build_sprites_from_paths
from scripts.engine.internal import library
from scripts.engine.internal.constant import GAP_SIZE, RenderLayer, TILE_SIZE, TraitGroup
from scripts.engine.internal.definition import ActorData
from scripts.engine.internal.event import event_hub, StartGameEvent
from scripts.engine.widgets.panel import Panel
if TYPE_CHECKING:
    from typing import Dict, List
__all__ = ["CharacterSelector"]
[docs]class CharacterSelector(Panel):
    """
    A menu widget. Used to hold a collection of text, images, buttons etc. Expects to have buttons and provides
    functionality to handle the clicks
    """
[docs]    def __init__(self, rect: Rect, manager: UIManager):
        self.button_events: Dict[str, Union[pygame.event.Event, Callable]] = {
            "confirm": self._post_confirm_event,
        }
        # containers for created widgets
        self.buttons: List[UIButton] = []
        self.drop_downs: List[UIDropDownMenu] = []
        self.text_boxes: List[UITextBox] = []
        self.ui_image: Optional[UIImage] = None
        # complete base class init
        super().__init__(rect, RenderLayer.BOTTOM, manager, object_id=ObjectID("#character_selector", "@menu_screen"))
        self._init_buttons()
        self._init_drop_downs()
        self._refresh_info()
        # confirm init complete
        logging.debug(f"CharacterSelector initialised.") 
[docs]    def update(self, time_delta: float):
        """
        Update based on current state and data. Run every frame.
        """
        super().update(time_delta) 
[docs]    def process_event(self, event):
        super().process_event(event)
        # only progress for user events
        if event.type != pygame.USEREVENT:
            return
        # handle button presses
        if event.user_type == pygame_gui.UI_BUTTON_PRESSED:
            # Find out which button we are clicking
            button = event.ui_element
            # post the new event
            if button in self.buttons:
                # get the id
                ids = event.ui_object_id.split(".")
                button_id = ids[-1]  # get last element
                new_event = self.button_events[button_id]
                # process the func from the button
                new_event()
                logging.debug(f"CharacterSelector button '{button_id}' pressed.")
        # handle new selection in drop downs
        if event.user_type == pygame_gui.UI_DROP_DOWN_MENU_CHANGED:
            self._refresh_info() 
    def _init_buttons(self):
        """
        Init the buttons for the menu
        """
        manager = self.ui_manager
        # set button dimensions
        max_width = self.rect.width
        max_height = self.rect.height
        height = int(max_height / 12)
        width = int(max_width / 6)
        x = -width  # anchor top right
        y = 0
        button = UIButton(
            relative_rect=Rect((x, y), (width, height)),
            anchors={"left": "right", "right": "right", "top": "top", "bottom": "bottom"},
            manager=manager,
            container=self,
            text="Embark",
            tool_tip_text="Confirm your selection and embark on your adventure.",
            object_id="confirm",
        )
        self.buttons.append(button)
    def _init_drop_downs(self):
        """
        Initialise the drop downs for the menu
        """
        people = []
        savvy = []
        homeland = []
        manager = self.ui_manager
        max_width = self.rect.width
        max_height = self.rect.height
        num_drop_downs = 3
        gap = GAP_SIZE
        width = int((max_width / num_drop_downs) - (gap * num_drop_downs + 2))
        height = int(max_height / 12)
        start_x = 0
        start_y = int(max_height / 2)  # anchored to the bottom left
        max_expansion = int(start_y - height)
        # get traits
        for key, trait in library.TRAITS.items():
            if trait.group == TraitGroup.PEOPLE:
                people.append(key)
            elif trait.group == TraitGroup.SAVVY:
                savvy.append(key)
            elif trait.group == TraitGroup.HOMELAND:
                homeland.append(key)
        # bundle trait lists with group name
        all_traits = [(homeland, TraitGroup.SAVVY), (people, TraitGroup.PEOPLE), (savvy, TraitGroup.SAVVY)]
        # create drop downs
        count = 0
        for traits, trait_group in all_traits:
            x = start_x + (count * (width + gap))
            rect = Rect((x, start_y), (width, height))
            drop_down = UIDropDownMenu(
                options_list=traits,
                starting_option=traits[0],
                relative_rect=rect,
                manager=manager,
                container=self,
                object_id=trait_group,
                expansion_height_limit=max_expansion,
            )
            count += 1
            self.drop_downs.append(drop_down)
    def _refresh_info(self):
        """
        Update the image and text in line with drop down selections
        """
        # image dimensions
        image_width = TILE_SIZE * 4
        image_height = TILE_SIZE * 4
        image_x = int((self.rect.width / 2) - (image_width / 2))
        image_y = int(self.rect.height / 10)
        image_rect = Rect((image_x, image_y), (image_width, image_height))
        # clear ui image if it exists
        if self.ui_image:
            self.ui_image.kill()
            self.ui_image = None
        # clear text box if it exists
        if self.text_boxes:
            for text_box in self.text_boxes:
                text_box.kill()
            self.text_boxes = []
        # get info from traits
        sprite_paths = []
        info = {}
        info_rects = []
        text_size = 4
        col = "#ffffff"
        for drop_down in self.drop_downs:
            trait = library.TRAITS[drop_down.selected_option]
            sprite_paths.append(trait.sprite_paths)
            info[trait.name] = [
                f"<font face=cozette color={col} size={text_size}>"
                # f"{trait.name} <br>",
                f"{trait.description} <br>",
                f"<br>",
                f"Clout: {trait.clout} <br>",
                f"Vigour: {trait.vigour} <br>",
                f"Skullduggery: {trait.skullduggery} <br>",
                f"Bustle: {trait.bustle} <br>",
                f"Exactitude: {trait.exactitude} <br>",
                f"Permanent Afflictions: {trait.permanent_afflictions} <br>",
                f"</font",
            ]
            # info dimensions
            drop_down_rect = drop_down.rect
            info_width = drop_down_rect.width
            info_height = int((self.rect.height - drop_down_rect.y) - drop_down_rect.height)
            info_x = drop_down_rect.x
            info_y = drop_down_rect.y + drop_down_rect.height
            info_rects.append(Rect((info_x, info_y), (info_width, info_height)))
        # sort and build into sprites
        sprite_paths.sort(key=lambda path: path.render_order, reverse=True)
        sprites = build_sprites_from_paths(sprite_paths, (image_width, image_height))
        # create UI image
        ui_image = UIImage(
            relative_rect=image_rect,
            image_surface=sprites.idle,
            manager=self.ui_manager,
            container=self,
        )
        self.ui_image = ui_image
        # create the text boxes
        count = 0
        for name, details in info.items():
            text_box = UITextBox(
                html_text=" ".join(details),
                relative_rect=info_rects[count],
                manager=self.ui_manager,
                object_id="name",
                container=self,
            )
            count += 1
            self.text_boxes.append(text_box)
    def _post_confirm_event(self):
        """
        Post the confirm event to be picked up elsewhere. Includes the selected data.
        """
        # get trait names
        traits = []
        for drop_down in self.drop_downs:
            traits.append(drop_down.selected_option)
        # get player data
        player_data = ActorData(
            key="player",
            possible_names=["player"],
            description="Player desc",
            position_offsets=[(0, 0)],
            trait_names=traits,
            height="middling",  # type: ignore  # need to pass as str to be converted during post-init
        )
        # post game event
        event_hub.post(StartGameEvent(player_data))