from __future__ import annotations

import os
import sys
from enum import Enum, auto
from pathlib import Path
from typing import Optional, overload

from gi.repository import Gio, GObject


class FileType(Enum):
    DIRECTORY = auto()
    VIDEO = auto()


class FileItem(GObject.Object):
    file_type: FileType
    full_path: Path
    thumbnail: bytes
    _has_thumbnail: bool

    __gtype_name__ = "FileItem"

    def __init__(self, name: str, file_type: FileType, full_path: Path):
        super().__init__()
        self.name = name
        self.file_type = file_type
        self.full_path = full_path
        self.thumbnail = b""
        self._has_thumbnail = False

    @GObject.Property(type=GObject.TYPE_UINT64)
    def saved_position(self) -> int:
        return self._load_attribute("position", 0)

    @saved_position.setter
    def set_saved_position(self, value: int):
        self._save_attribute("position", value if value else None)
        self.notify("saved-position")

    @GObject.Property(type=GObject.TYPE_UINT64)
    def saved_duration(self):
        return self._load_attribute("duration", 1) or 1

    @saved_duration.setter
    def set_saved_duration(self, value: int):
        self._save_attribute("duration", value if value > 0 else None)
        self.notify("saved-duration")

    @GObject.Property(type=int)
    def saved_subtitle_track(self):
        return self._load_attribute("subtitle_track", -2)

    @saved_subtitle_track.setter
    def set_saved_subtitle_track(self, value: int):
        self._save_attribute("subtitle_track", value if value >= -1 else None)
        self.notify("saved-subtitle-track")

    @GObject.Property(type=int)
    def saved_audio_track(self):
        return self._load_attribute("audio_track", 0)

    @saved_audio_track.setter
    def set_saved_audio_track(self, value: int):
        self._save_attribute("audio_track", value if value > 0 else None)
        self.notify("saved-audio-track")

    @GObject.Property(type=bool, default=False)
    def has_thumbnail(self):
        return self._has_thumbnail

    @has_thumbnail.setter
    def set_has_thumbnail(self, value: bool):
        self._has_thumbnail = value
        self.notify("has-thumbnail")

    @overload
    def _load_attribute(self, name: str, dfl: str) -> str: ...

    @overload
    def _load_attribute(self, name: str, dfl: bytes) -> bytes: ...

    @overload
    def _load_attribute(self, name: str, dfl: int) -> int: ...

    def _load_attribute(self, name: str, dfl: str | bytes | int) -> str | bytes | int:
        try:
            strval = os.getxattr(self.full_path, f"user.lazy_player.{name}")
            return type(dfl)(strval)
        except OSError:
            return dfl

    def _save_attribute(self, name: str, value: str | bytes | float | int | None) -> None:
        try:
            if value is None:
                os.removexattr(self.full_path, f"user.lazy_player.{name}")
            else:
                if isinstance(value, bytes):
                    os.setxattr(self.full_path, f"user.lazy_player.{name}", value)
                else:
                    os.setxattr(
                        self.full_path, f"user.lazy_player.{name}", str(value).encode("utf8")
                    )
        except OSError as err:
            print(err, file=sys.stderr)


class FileListModel(GObject.Object, Gio.ListModel):
    """A ListModel implementation for FileItems"""

    __gtype_name__ = "FileListModel"

    items: list[FileItem]

    def __init__(self):
        super().__init__()
        self.items = []

    def do_get_item_type(self) -> GObject.GType:
        return GObject.type_from_name("FileItem")

    def do_get_n_items(self) -> int:
        return len(self.items)

    def do_get_item(self, position: int) -> Optional[FileItem]:
        if 0 <= position < len(self.items):
            return self.items[position]
        return None

    def remove_all(self) -> None:
        removed = len(self.items)
        self.items = []
        self.items_changed(0, removed, 0)

    def set_items(self, items: list[FileItem]):
        removed = len(self.items)
        self.items = list(items)
        self.items_changed(0, removed, len(self.items))

    def append(self, item: FileItem) -> None:
        pos = len(self.items)
        self.items.append(item)
        self.items_changed(pos, 0, 1)

    def remove(self, position: int) -> None:
        if 0 <= position < len(self.items):
            self.items.pop(position)
            self.items_changed(position, 1, 0)