from __future__ import annotations
import scpup
import pygame
from abc import ABCMeta, abstractmethod

__all__: list[str] = [
  "EauView",
]


class EauViewMeta(ABCMeta):
  """Metaclass for EauView class. Implements a Singleton so that only 1 instance
  exists of all subclasses. For example if you have class View1 and class View2
  and you create an instance of class View1, and then create an instance of
  View2, the instance of View1 will get deleted before the instance of View2
  gets created."""
  _instance: EauView | None = None

  def __call__(cls, *args, **kwargs):
    if cls._instance is not None:
      cls._instance.sprites.empty()
      del cls._instance
    cls._instance = super().__call__(*args, **kwargs)
    return cls._instance


class EauView(metaclass=EauViewMeta):
  """A game view.

  Attributes:
    sprites:
      The sprite group that has the sprites that belong to the view.
    texts:
      The text group that has the texts that belong to the view.
  """
  __slots__: tuple = (
    "sprites",
    "texts"
  )

  @classmethod
  def __init_subclass__(cls, /, *, bgpath: str | None = None, player_sprite: str, **kwargs):
    """Subclass customization so that subclasses can specify their background
    image when subclassing and the sprite that players will use when this view
    is created. For example:

    ```python
    class View1(EauView, 'path', 'to', 'image.png', player_sprite="Sprite"):
      ...
    ```

    Args:
      *bgpaths:
        Path segments of the background image of the subclass. It has to be
        under `<root>/assets/images/`.
      player_sprite:
        The name of the class of the sprite that will be set to players when
        creating an instance of the subclass
    """
    super().__init_subclass__(**kwargs)
    cls.background = bgpath and scpup.load_image(bgpath)[0]
    cls.player_sprite = player_sprite

  def __init__(self):
    """Initialize a view

    When subclassing, don't forget to call `super().__init__()` because it
    does initialization logic that is needed for the view to work correctly.
    """
    self.sprites = self.init_sprites()
    self.texts = self.init_texts()

  def init_texts(self) -> scpup.EauTextsGroup | None:
    """Initialize the texts that are part of this view

    When subclassing EauView, if the subclass must display text then this method
    should return an EauTextGroup with the texts that will be shown. This method
    is not abstract because there might be views that don't have texts, so not
    all view need to implement this method

    Returns:
      The text group with the texts that belong to this view
    """

  @abstractmethod
  def init_sprites(self) -> scpup.EauGroup:
    """Initialize the sprites that are part of this view

    When subclassing EauView, all the sprites that are specifically part of this
    view should be added to an EauGroup and returned from this method.

    Returns:
      The sprite group with the sprites that belong to this view
    """

  def draw(self, surface: pygame.Surface) -> None:
    """Draw this view's sprites to a surface

    Args:
      surface:
        The target surface
    """
    if self.texts:
      self.texts.draw(surface)
    self.sprites.draw(surface)

  def clear(self, surface: pygame.Surface, background: pygame.Surface) -> None:
    """Clear this view's sprites from a surface with a background surface

    Args:
      surface:
        The target surface
      background:
        The background surface
    """
    self.sprites.clear(surface, background)

  def update(self, *args, **kwargs) -> None:
    """Update this view's sprites"""
    self.sprites.update(*args, **kwargs)
