from __future__ import annotations

import sys
from enum import Enum, auto
from typing import Callable, TypedDict

import cv2
import pygame
from cv2 import VideoCapture
from pygame import Surface
from pygame.time import Clock


def main (
) -> None:
    pygame.init ()
    game = Game ()
    bg = Bg (game)
    deerjika = Deerjika (game, DeerjikaPattern.RELAXED)
    while True:
        for event in pygame.event.get ():
            if event.type == pygame.QUIT:
                pygame.quit ()
                sys.exit ()
        game.redraw ()


class DeerjikaPattern (Enum):
    NORMAL      = auto ()
    RELAXED     = auto ()
    SLEEPING    = auto ()


class Direction (Enum):
    LEFT    = auto ()
    RIGHT   = auto ()


class Game:
    clock:      Clock
    frame:      int
    redrawers:  list[Redrawer]
    screen:     Surface

    def __init__ (
            self,
    ):
        self.screen = pygame.display.set_mode ((CWindow.WIDTH, CWindow.HEIGHT))
        self.clock = Clock ()
        self.frame = 0
        self.redrawers = []

    def redraw (
            self,
    ) -> None:
        for redrawer in sorted (self.redrawers, key = lambda x: x['layer']):
            redrawer['func'] ()
        pygame.display.update ()
        self.clock.tick (24)


class GameObject:
    frame:      int
    game:       Game
    width:      int
    height:     int
    x:          float
    y:          float
    vx:         float   = 0
    vy:         float   = 0
    ax:         float   = 0
    ay:         float   = 0
    arg:        float   = 0

    def __init__ (
            self,
            game:   Game,
            layer:  int | None  = None,
            x:      float       = 0,
            y:      float       = 0,
    ):
        self.game = game
        self.frame = 0
        if layer is None:
            if self.game.redrawers:
                layer = max (r['layer'] for r in self.game.redrawers) + 10
            else:
                layer = 0
        self.game.redrawers.append ({ 'layer': layer, 'func': self.redraw })
        self.x = x
        self.y = y

    def redraw (
            self,
    ) -> None:
        self.x += self.vx
        self.y += self.vy
        self.vx += self.ax
        self.vy += self.ay
        self.frame += 1


class Bg (GameObject):
    surface:    Surface

    def __init__ (
            self,
            game:   Game,
    ):
        super ().__init__ (game)
        self.surface = pygame.image.load ('assets/bg.jpg')
        self.surface = pygame.transform.scale (self.surface, (CWindow.WIDTH, CWindow.HEIGHT))

    def redraw (
            self,
    ) -> None:
        self.game.screen.blit (self.surface, (self.x, self.y))
        super ().redraw ()


class Deerjika (GameObject):
    surfaces:   list[Surface]
    size:       int

    def __init__ (
            self,
            game:       Game,
            pattern:    DeerjikaPattern = DeerjikaPattern.NORMAL,
            direction:  Direction       = Direction.LEFT,
            layer:      int | None      = None,
            x:          float           = 0,
            y:          float           = 0,
    ):
        super ().__init__ (game, layer, x, y)
        self.pattern = pattern
        self.direction = direction
        match pattern:
            case DeerjikaPattern.NORMAL:
                ...
            case DeerjikaPattern.RELAXED:
                match direction:
                    case Direction.LEFT:
                        self.surfaces = get_surfaces_from_video ('assets/deerjika_relax.mp4')
                    case Direction.RIGHT:
                        ...
        self.size = len (self.surfaces)

    def redraw (
            self,
    ) -> None:
        surface: Surface = self.surfaces[self.frame % self.size]
        self.game.screen.blit (surface, surface.get_rect (center = (self.x, self.y)))
        super ().redraw ()


class CWindow:
    WIDTH   = 1024
    HEIGHT  =  768


class Redrawer (TypedDict):
    layer:  int
    func:   Callable[[], None]


def get_surfaces_from_video (
        video_path: str,
) -> list[Surface]:
    cap = VideoCapture (video_path)
    if not cap.isOpened ():
        return []

    fps = cap.get (cv2.CAP_PROP_FPS)

    surfaces: list[Surface] = []
    while cap.isOpened ():
        (ret, frame) = cap.read ()
        if not ret:
            break
        frame = cv2.cvtColor (frame, cv2.COLOR_BGR2RGB)
        frame_surface = pygame.surfarray.make_surface (frame)
        frame_surface = pygame.transform.rotate (frame_surface, -90)
        surfaces.append (pygame.transform.flip (frame_surface, True, False))

    cap.release ()

    return surfaces


if __name__ == '__main__':
    main ()
