# McRogueFace Cookbook - Choice List Widget
"""
Vertical list of selectable text options with keyboard/mouse navigation.

Example:
    from lib.choice_list import ChoiceList

    def on_select(index, value):
        print(f"Selected {value} at index {index}")

    choices = ChoiceList(
        pos=(100, 100),
        size=(200, 150),
        choices=["New Game", "Load Game", "Options", "Quit"],
        on_select=on_select
    )
    scene.children.append(choices.frame)

    # Navigate with keyboard
    choices.navigate(1)   # Move down
    choices.navigate(-1)  # Move up
    choices.confirm()     # Select current
"""
import mcrfpy


class ChoiceList:
    """Vertical list of selectable text options.

    Args:
        pos: (x, y) position tuple
        size: (width, height) tuple
        choices: List of choice strings
        on_select: Callback(index, value) when selection is confirmed
        item_height: Height of each item (default: 30)
        selected_color: Background color of selected item
        hover_color: Background color when hovered
        normal_color: Background color of unselected items
        text_color: Color of choice text
        selected_text_color: Text color when selected
        font_size: Size of choice text (default: 16)
        outline: Border thickness (default: 1)

    Attributes:
        frame: The outer frame (add this to scene)
        selected_index: Currently selected index
        choices: List of choice strings
    """

    DEFAULT_NORMAL = mcrfpy.Color(40, 40, 45)
    DEFAULT_HOVER = mcrfpy.Color(60, 60, 70)
    DEFAULT_SELECTED = mcrfpy.Color(80, 100, 140)
    DEFAULT_TEXT = mcrfpy.Color(200, 200, 200)
    DEFAULT_SELECTED_TEXT = mcrfpy.Color(255, 255, 255)
    DEFAULT_OUTLINE = mcrfpy.Color(100, 100, 110)

    def __init__(self, pos, size, choices, on_select=None,
                 item_height=30, selected_color=None, hover_color=None,
                 normal_color=None, text_color=None, selected_text_color=None,
                 font_size=16, outline=1):
        self.pos = pos
        self.size = size
        self._choices = list(choices)
        self.on_select = on_select
        self.item_height = item_height
        self.font_size = font_size
        self._selected_index = 0

        # Colors
        self.normal_color = normal_color or self.DEFAULT_NORMAL
        self.hover_color = hover_color or self.DEFAULT_HOVER
        self.selected_color = selected_color or self.DEFAULT_SELECTED
        self.text_color = text_color or self.DEFAULT_TEXT
        self.selected_text_color = selected_text_color or self.DEFAULT_SELECTED_TEXT

        # Hover tracking
        self._hovered_index = -1

        # Create outer frame
        self.frame = mcrfpy.Frame(
            pos=pos,
            size=size,
            fill_color=self.normal_color,
            outline_color=self.DEFAULT_OUTLINE,
            outline=outline
        )

        # Item frames and labels
        self._item_frames = []
        self._item_labels = []

        self._rebuild_items()

    def _rebuild_items(self):
        """Rebuild all item frames and labels."""
        # Clear existing - pop all items from the collection
        while len(self.frame.children) > 0:
            self.frame.children.pop()
        self._item_frames = []
        self._item_labels = []

        # Create item frames
        for i, choice in enumerate(self._choices):
            # Create item frame
            item_frame = mcrfpy.Frame(
                pos=(0, i * self.item_height),
                size=(self.size[0], self.item_height),
                fill_color=self.selected_color if i == self._selected_index else self.normal_color,
                outline=0
            )

            # Create label
            label = mcrfpy.Caption(
                text=choice,
                pos=(10, (self.item_height - self.font_size) / 2),
                fill_color=self.selected_text_color if i == self._selected_index else self.text_color,
                font_size=self.font_size
            )

            item_frame.children.append(label)

            # Set up click handler
            idx = i  # Capture index in closure
            def make_click_handler(index):
                def handler(pos, button, action):
                    if button == mcrfpy.MouseButton.LEFT and action == mcrfpy.InputState.RELEASED:
                        self.set_selected(index)
                        if self.on_select:
                            self.on_select(index, self._choices[index])
                return handler

            def make_enter_handler(index):
                def handler(pos, button, action):
                    self._on_item_enter(index)
                return handler

            def make_exit_handler(index):
                def handler(pos, button, action):
                    self._on_item_exit(index)
                return handler

            item_frame.on_click = make_click_handler(idx)
            item_frame.on_enter = make_enter_handler(idx)
            item_frame.on_exit = make_exit_handler(idx)

            self._item_frames.append(item_frame)
            self._item_labels.append(label)
            self.frame.children.append(item_frame)

    def _on_item_enter(self, index):
        """Handle mouse entering an item."""
        self._hovered_index = index
        if index != self._selected_index:
            self._item_frames[index].fill_color = self.hover_color

    def _on_item_exit(self, index):
        """Handle mouse leaving an item."""
        self._hovered_index = -1
        if index != self._selected_index:
            self._item_frames[index].fill_color = self.normal_color

    def _update_display(self):
        """Update visual state of all items."""
        for i, (frame, label) in enumerate(zip(self._item_frames, self._item_labels)):
            if i == self._selected_index:
                frame.fill_color = self.selected_color
                label.fill_color = self.selected_text_color
            elif i == self._hovered_index:
                frame.fill_color = self.hover_color
                label.fill_color = self.text_color
            else:
                frame.fill_color = self.normal_color
                label.fill_color = self.text_color

    @property
    def selected_index(self):
        """Currently selected index."""
        return self._selected_index

    @property
    def selected_value(self):
        """Currently selected value."""
        if 0 <= self._selected_index < len(self._choices):
            return self._choices[self._selected_index]
        return None

    @property
    def choices(self):
        """List of choices."""
        return list(self._choices)

    @choices.setter
    def choices(self, value):
        """Set new choices list."""
        self._choices = list(value)
        self._selected_index = min(self._selected_index, len(self._choices) - 1)
        if self._selected_index < 0:
            self._selected_index = 0
        self._rebuild_items()

    def set_selected(self, index):
        """Set the selected index.

        Args:
            index: Index to select (clamped to valid range)
        """
        if not self._choices:
            return

        old_index = self._selected_index
        self._selected_index = max(0, min(index, len(self._choices) - 1))

        if old_index != self._selected_index:
            self._update_display()

    def navigate(self, direction):
        """Navigate up or down in the list.

        Args:
            direction: +1 for down, -1 for up
        """
        if not self._choices:
            return

        new_index = self._selected_index + direction
        # Wrap around
        if new_index < 0:
            new_index = len(self._choices) - 1
        elif new_index >= len(self._choices):
            new_index = 0

        self.set_selected(new_index)

    def confirm(self):
        """Confirm the current selection (triggers callback)."""
        if self.on_select and 0 <= self._selected_index < len(self._choices):
            self.on_select(self._selected_index, self._choices[self._selected_index])

    def add_choice(self, choice, index=None):
        """Add a choice at the given index (or end if None)."""
        if index is None:
            self._choices.append(choice)
        else:
            self._choices.insert(index, choice)
        self._rebuild_items()

    def remove_choice(self, index):
        """Remove the choice at the given index."""
        if 0 <= index < len(self._choices):
            del self._choices[index]
            if self._selected_index >= len(self._choices):
                self._selected_index = max(0, len(self._choices) - 1)
            self._rebuild_items()

    def set_choice(self, index, value):
        """Change the text of a choice."""
        if 0 <= index < len(self._choices):
            self._choices[index] = value
            self._item_labels[index].text = value


def create_menu(pos, choices, on_select=None, title=None, width=200):
    """Create a simple menu with optional title.

    Args:
        pos: (x, y) position
        choices: List of choice strings
        on_select: Callback(index, value)
        title: Optional menu title
        width: Menu width

    Returns:
        Tuple of (container_frame, choice_list) or just choice_list if no title
    """
    if title:
        # Create container with title
        item_height = 30
        title_height = 40
        total_height = title_height + len(choices) * item_height

        container = mcrfpy.Frame(
            pos=pos,
            size=(width, total_height),
            fill_color=mcrfpy.Color(30, 30, 35),
            outline_color=mcrfpy.Color(100, 100, 110),
            outline=2
        )

        # Title caption
        title_cap = mcrfpy.Caption(
            text=title,
            pos=(width / 2, 10),
            fill_color=mcrfpy.Color(255, 255, 255),
            font_size=18
        )
        container.children.append(title_cap)

        # Choice list below title
        choice_list = ChoiceList(
            pos=(0, title_height),
            size=(width, len(choices) * item_height),
            choices=choices,
            on_select=on_select,
            outline=0
        )

        # Add choice list frame as child
        container.children.append(choice_list.frame)

        return container, choice_list
    else:
        return ChoiceList(
            pos=pos,
            size=(width, len(choices) * 30),
            choices=choices,
            on_select=on_select
        )
