"""Module defines the `Avatar` class, which will display a UI (`pygame` backend) and capture user input, including mouse clicks, motion, key events, window events, and eyetracking events (if the required sensor is added, see `EyetrackingIOSensor`)."""

from star_ray import Sensor, Actuator, observe
from star_ray_pygame.avatar import Avatar as PygameAvatar
from star_ray_pygame import WindowConfiguration
from icua.event import (
    RenderEvent,
    EyeMotionEvent,
)


class Avatar(PygameAvatar):
    """A special type of `Agent` that will display a UI (`pygame` backend) to the user and capture various kinds of user input, including: mouse clicks, motion, key events, window events, and eyetracking events (if the required sensor is added, see `EyetrackingIOSensor`)."""

    def __init__(
        self,
        sensors: list[Sensor],
        actuators: list[Actuator],
        window_config: WindowConfiguration = None,
        **kwargs,
    ):
        """Constructor.

        Args:
            sensors (list[Sensor]): list of initial sensors, this may include instances of `IOSensor` that will gather user input from devices (see e.g. `EyetrackingIOSensor`). Common user input events such as mouse, keyboard and window events are already handled by a io sensor that is added automatically.
            actuators (list[Actuator]): list of initial actuators, this should generally include an actuator that is capable of forwarding user input events (see e.g. `AvatarActuator`).
            window_config (WindowConfiguration, optional): UI window configuration. Defaults to None (see `star_ray_pygame.agent.PyGameAvatar` for details).
            kwargs (dict[str,Any]): additional optional keyword arguments.
        """
        super().__init__(sensors, actuators, window_config=window_config, **kwargs)

    def render(self) -> None:
        """Renders the UI and triggers a `RenderEvent`."""
        # this is for logging purposes, we can see when the rendering beings. if all agents are running locally (and synchronously), then we can assume that all preceeding events in the event log will be visible to the user! this is very useful for post-analysis in experiments.
        self.attempt(RenderEvent())
        super().render()

    @observe
    def on_gaze(self, event: EyeMotionEvent):
        """Observe method for `EyeMotionEvents`, this will only be called if an `EyetrackingIOSensor` (or similar) is attached to this agent. It finalises the events by computing the svg coordinates (from window coordinates) and finding all svg elements that lie under this coordinate. It will attempt the event so that it is made avaliable (via an appropriate actuator) to other subscribing agents.

        Args:
            event (EyeMotionEvent): the eye motion event.
        """
        # These events are generated by an EyetrackingIOSensor (if it exists).
        # The position is in pixel-coordinates, we need to transform to SVG coordinates
        # (similar to mouse events).
        event.position = self._view.pixel_to_svg(event.position_raw)
        # also find the elements that are under the gaze point
        event.target = self._view.elements_under(event.position, transform=False)
        # attempt the event, this will send it to other subscribing agents
        self.attempt(event)
