from __future__ import annotations

from datetime import datetime
from typing import Any

from gi.repository import Gdk, Gtk, Pango

from .reactive import Watcher
from .video_player import VideoPlayer


class VideoOverlay(Gtk.Overlay, Watcher):
    player: VideoPlayer

    message: Gtk.Label
    message_expiration: float

    grid: Gtk.Grid
    grid_expiration: float

    clock: Gtk.Label
    progressbar: Gtk.ProgressBar

    now: float

    def __init__(self, player: VideoPlayer):
        super().__init__()

        self.now = 0.0
        self.player = player

        # Message is appears at the center of the screen,
        # above everything else. Usually to indicate change
        # of subtitle or audio track or something similar.
        self.message = Gtk.Label()
        self.message.set_name("overlay-message")
        self.message.set_valign(Gtk.Align.CENTER)
        self.message.set_halign(Gtk.Align.CENTER)
        self.message.set_visible(False)
        self.message.set_wrap(True)
        self.message.set_wrap_mode(Pango.WrapMode.WORD_CHAR)

        # Once specific time passes, message disappears.
        self.message_expiration = 0.0

        # Grid overlay is between the video at the bottom and
        # the message at the top. It is only shown when user
        # interacts with the player.
        self.grid = Gtk.Grid()
        self.grid.set_hexpand(True)
        self.grid.set_vexpand(True)
        self.grid.set_column_homogeneous(True)
        self.grid.set_row_homogeneous(True)

        # Grid visibility can also expire after a while.
        self.grid_expiration = 0.0

        # Create grid boxes.
        clock_box = Gtk.Box(hexpand=True, vexpand=True)
        self.grid.attach(clock_box, 0, 0, 1, 1)

        self.grid.attach(Gtk.Box(), 1, 0, 2, 1)
        self.grid.attach(Gtk.Box(), 0, 1, 3, 1)

        progressbar_box = Gtk.Box(
            name="progressbar-box",
            hexpand=True,
            vexpand=True,
        )
        self.grid.attach(progressbar_box, 0, 2, 3, 1)

        # Add clock to the top-left grid box.
        self.clock = Gtk.Label(
            name="overlay-clock",
            halign=Gtk.Align.START,
            valign=Gtk.Align.START,
        )
        clock_box.append(self.clock)

        # Create progressbar.
        self.progressbar = Gtk.ProgressBar(
            name="progressbar",
            hexpand=True,
            halign=Gtk.Align.FILL,
            valign=Gtk.Align.END,
            focusable=False,
        )
        progressbar_box.append(self.progressbar)

        # Add children.
        self.set_child(self.player.picture)
        self.add_overlay(self.grid)
        self.add_overlay(self.message)

        # Consume ticks for the clock and overlay expiration.
        self.add_tick_callback(self._on_tick, None)

        # Register all watches.
        self.watch_all()

    def _on_tick(self, widget: Gtk.Widget, frame_clock: Gdk.FrameClock, data: Any) -> bool:
        self.clock.set_text(datetime.now().strftime("%H:%M"))

        self.now = frame_clock.get_frame_time() / 1_000_000

        if self.grid_expiration <= self.now:
            self.grid.hide()

        if self.message_expiration <= self.now:
            self.message.hide()

        position = self.player.get_position()
        duration = self.player.get_duration()

        if position is not None and duration is not None:
            self.progressbar.set_fraction(position / duration)

        return True

    def show_message(self, text: str, timeout: float = 1.0) -> None:
        """Show text in a centered overlay that disappears after timeout."""

        self.message.set_text(text)
        self.message.show()
        self.message_expiration = self.now + timeout

    def _watch_player_state(self):
        is_playing = self.player.is_playing.value
        is_paused = self.player.is_paused.value

        # Just to track other user interactions.
        self.player.last_user_input.value

        self.grid.show()

        if is_playing and is_paused:
            self.grid_expiration = 1e20
        else:
            self.grid_expiration = self.now + 1.0