from PIL import ImageEnhance
from typing_extensions import override

from arcade import Texture
from arcade.gui.events import UIOnChangeEvent, UIOnClickEvent
from arcade.gui.property import Property, bind
from arcade.gui.surface import Surface
from arcade.gui.widgets import UIInteractiveWidget


class UITextureToggle(UIInteractiveWidget):
    """A toggle button switching between on (True) and off (False) state.

    on_texture and off_texture are required.
    State dependent textures are generated by changing the brightness (hover, press)
    of the provided textures or converting them to grayscale (disabled).

    Args:
        x: x coordinate of bottom left
        y: y coordinate of bottom left
        width: Width of the button.
        height: Height of the button.
        on_texture: Texture to show when the button is on.
        off_texture: Texture to show when the button is off.
        value: Initial value of the button.
        size_hint: Size hint for the layout.
        size_hint_min: Minimum size hint for the layout.
        size_hint_max: Maximum size hint for the layout.
    """

    # Experimental ui class
    value = Property(False)

    def __init__(
        self,
        *,
        x: float = 0,
        y: float = 0,
        width: float = 100,
        height: float = 50,
        on_texture: Texture | None = None,
        off_texture: Texture | None = None,
        value=False,
        size_hint=None,
        size_hint_min=None,
        size_hint_max=None,
        **kwargs,
    ):
        # Generate hover and pressed texture by changing the brightness
        if on_texture is None:
            raise ValueError("You have to provide a `on_texture`")
        self.normal_on_tex = on_texture
        enhancer = ImageEnhance.Brightness(self.normal_on_tex.image)
        self.hover_on_tex = Texture(
            enhancer.enhance(1.5),
            name=self.normal_on_tex.cache_name + "_brighter",
        )
        self.pressed_on_tex = Texture(
            enhancer.enhance(0.5),
            name=self.normal_on_tex.cache_name + "_darker",
        )
        self.disabled_on_tex = Texture(
            self.normal_on_tex.image.convert("LA").convert("RGBA"),
        )

        if off_texture is None:
            raise ValueError("You have to provide a `off_texture`")
        self.normal_off_tex = off_texture
        enhancer = ImageEnhance.Brightness(self.normal_off_tex.image)
        self.hover_off_tex = Texture(
            enhancer.enhance(1.5),
            name=self.normal_off_tex.cache_name + "_brighter",
        )
        self.pressed_off_tex = Texture(
            enhancer.enhance(0.5),
            name=self.normal_off_tex.cache_name + "_darker",
        )
        self.disabled_off_tex = Texture(
            self.normal_off_tex.image.convert("LA").convert("RGBA"),
        )

        self.value = value
        self.register_event_type("on_change")

        bind(self, "value", self.trigger_render)
        bind(self, "value", self._dispatch_on_change_event)

        super().__init__(
            x=x,
            y=y,
            width=width,
            height=height,
            size_hint=size_hint,
            size_hint_min=size_hint_min,
            size_hint_max=size_hint_max,
            **kwargs,
        )

    def _dispatch_on_change_event(self):
        self.dispatch_event("on_change", UIOnChangeEvent(self, not self.value, self.value))

    @override
    def on_click(self, event: UIOnClickEvent):
        """Change the value of the button on click."""
        self.value = not self.value

    @override
    def do_render(self, surface: Surface):
        """Render the button, using texture depending on the state."""
        self.prepare_render(surface)
        tex = self.normal_on_tex if self.value else self.normal_off_tex
        if self.disabled:
            tex = self.disabled_on_tex if self.value else self.disabled_off_tex
        elif self.pressed:
            tex = self.pressed_on_tex if self.value else self.pressed_off_tex
        elif self.hovered:
            tex = self.hover_on_tex if self.value else self.hover_off_tex
        surface.draw_texture(0, 0, self.content_width, self.content_height, tex)

    def on_change(self, event: UIOnChangeEvent):
        """To be implemented by the user, triggered when the cursor's value is changed.

        Args:
            event: Event containing the old and new value of the cursor.
        """
        pass
