"""
player.py — VLC-baseret afspiller med PyQt6 signals.

Sender signals til GUI:
  position_changed(float)   — 0.0–1.0 progress
  time_changed(int, int)    — (current_sec, total_sec)
  levels_changed(float, float) — VU-meter L/R 0.0–1.0
  song_ended()              — sang færdig
  state_changed(str)        — 'playing'|'paused'|'stopped'
"""

from PyQt6.QtCore import QObject, pyqtSignal, QTimer
import random
import math

try:
    import vlc
    VLC_AVAILABLE = True
except ImportError:
    VLC_AVAILABLE = False
    print("Advarsel: python-vlc ikke installeret — afspilning deaktiveret")


class Player(QObject):
    position_changed  = pyqtSignal(float)
    time_changed      = pyqtSignal(int, int)
    levels_changed    = pyqtSignal(float, float)
    song_ended        = pyqtSignal()
    state_changed     = pyqtSignal(str)

    def __init__(self, parent=None):
        super().__init__(parent)
        self._path: str | None = None
        self._duration: int = 0
        self._demo_mode = False
        self._demo_stop_sec = 10
        self._demo_fade_sec = 5
        self._demo_fading = False
        self._volume = 78

        if VLC_AVAILABLE:
            self._instance = vlc.Instance("--no-video", "--quiet")
            self._media_player = self._instance.media_player_new()
            self._events = self._media_player.event_manager()
            self._events.event_attach(
                vlc.EventType.MediaPlayerEndReached,
                self._on_end_reached,
            )
        else:
            self._media_player = None

        # Timer til polling af position + VU-simulation
        self._poll_timer = QTimer(self)
        self._poll_timer.setInterval(80)
        self._poll_timer.timeout.connect(self._poll)

    # ── Indlæsning ────────────────────────────────────────────────────────────

    def load(self, path: str, duration_sec: int = 0):
        """Indlæs en lydfil uden at starte afspilning."""
        self._path = path
        self._duration = duration_sec
        self._demo_mode = False

        if VLC_AVAILABLE and self._media_player:
            # Konverter GVFS SMB-sti til SMB URL som VLC kan tilgå direkte
            vlc_path = self._resolve_path(path)
            media = self._instance.media_new(vlc_path)
            self._media_player.set_media(media)
            self._media_player.audio_set_volume(self._volume)

        self.position_changed.emit(0.0)
        self.time_changed.emit(0, self._duration)
        self.state_changed.emit("stopped")

    def _resolve_path(self, path: str) -> str:
        """Konverter platform-specifikke netværksstier til URL'er VLC kan bruge."""
        import re, sys

        # Linux GVFS SMB: /run/user/1000/gvfs/smb-share:server=X,share=Y/sti/fil.mp3
        m = re.match(r".*/gvfs/smb-share:server=([^,]+),share=([^/]+)(/.+)$", path)
        if m:
            server, share, rest = m.group(1), m.group(2), m.group(3)
            return f"smb://{server}/{share}{rest}"

        # Windows UNC: \\server\share\sti\fil.mp3
        if path.startswith("\\\\"):
            # \\server\share\rest → smb://server/share/rest
            parts = path.replace("\\", "/").lstrip("/").split("/", 2)
            if len(parts) >= 2:
                server = parts[0]
                share  = parts[1]
                rest   = "/" + parts[2] if len(parts) > 2 else ""
                return f"smb://{server}/{share}{rest}"

        # Lokale stier og drevbogstaver (C:\...) — VLC håndterer dem fint
        return path

        self.position_changed.emit(0.0)
        self.time_changed.emit(0, self._duration)
        self.state_changed.emit("stopped")

    # ── Transport ─────────────────────────────────────────────────────────────

    def play(self):
        self._demo_mode = False
        if VLC_AVAILABLE and self._media_player:
            self._media_player.play()
        self._poll_timer.start()
        self.state_changed.emit("playing")

    def play_demo(self, stop_at_sec: int = 10, fade_sec: int = 5):
        """
        Afspil fra start, fade ud over fade_sec sekunder og stop.
        Total afspilningstid = stop_at_sec + fade_sec.
        fade_sec=0 giver ingen fade.
        """
        self._demo_mode = True
        self._demo_stop_sec = stop_at_sec + fade_sec   # total tid inkl. fade
        self._demo_fade_sec = fade_sec
        self._demo_fading = False
        if VLC_AVAILABLE and self._media_player:
            self._media_player.set_time(0)
            self._media_player.audio_set_volume(self._volume)
            self._media_player.play()
        self._poll_timer.start()
        self.state_changed.emit("playing")

    def pause(self):
        if VLC_AVAILABLE and self._media_player:
            self._media_player.pause()
        self.state_changed.emit("paused")

    def stop(self):
        self._demo_mode = False
        self._demo_fading = False
        if VLC_AVAILABLE and self._media_player:
            self._media_player.audio_set_volume(self._volume)
            self._media_player.stop()
        self._poll_timer.stop()
        self.position_changed.emit(0.0)
        self.time_changed.emit(0, self._duration)
        self.state_changed.emit("stopped")

    def is_playing(self) -> bool:
        if VLC_AVAILABLE and self._media_player:
            return self._media_player.is_playing()
        return False

    def set_volume(self, volume: int):
        """0–100"""
        self._volume = volume
        if VLC_AVAILABLE and self._media_player:
            self._media_player.audio_set_volume(volume)

    def set_position(self, fraction: float):
        """Søg til position 0.0–1.0"""
        if VLC_AVAILABLE and self._media_player:
            self._media_player.set_position(fraction)

    # ── Intern polling ────────────────────────────────────────────────────────

    def _poll(self):
        """Køres ~12 gange per sekund — opdaterer position og VU-meter."""
        if VLC_AVAILABLE and self._media_player:
            pos = self._media_player.get_position()
            ms  = self._media_player.get_time()
            cur = max(0, ms // 1000)
        else:
            # Simuleret tilstand (til UI-test uden VLC)
            pos = getattr(self, "_sim_pos", 0.0)
            self._sim_pos = min(1.0, pos + 0.001)
            cur = int(self._sim_pos * self._duration)
            pos = self._sim_pos
            if self._sim_pos >= 1.0:
                self._on_end_reached(None)
                return

        self.position_changed.emit(pos)
        self.time_changed.emit(cur, self._duration)

        # Demo fade-out og stop
        if self._demo_mode and cur >= self._demo_stop_sec:
            # Færdig — gendan volumen og stop
            if VLC_AVAILABLE and self._media_player:
                self._media_player.audio_set_volume(self._volume)
            self.stop()
            self._demo_mode = False
            self._demo_fading = False
            self.position_changed.emit(0.0)
            self.time_changed.emit(0, self._duration)
            self.state_changed.emit("demo_ended")
            return

        # Demo fade-out — de sidste _demo_fade_sec sekunder (0 = ingen fade)
        if self._demo_mode and VLC_AVAILABLE and self._media_player and self._demo_fade_sec > 0:
            secs_left = self._demo_stop_sec - cur
            if secs_left <= self._demo_fade_sec and secs_left > 0:
                fade_fraction = secs_left / self._demo_fade_sec   # 1.0 → 0.0
                log_fraction = math.log10(1 + fade_fraction * 9) / math.log10(10)
                faded_vol = int(self._volume * log_fraction)
                self._media_player.audio_set_volume(max(0, faded_vol))
                self._demo_fading = True
            elif not self._demo_fading:
                self._media_player.audio_set_volume(self._volume)

        # VU-meter: brug VLC's audio-amplitude hvis tilgængelig, ellers simulér
        if VLC_AVAILABLE and self._media_player and self._media_player.is_playing():
            # VLC eksponerer ikke amplitude direkte — vi bruger en blød simulation
            # der er baseret på position så det ser organisk ud
            base = 0.55 + 0.3 * abs(pos - 0.5)
            l = min(1.0, base + random.gauss(0, 0.12))
            r = min(1.0, base + random.gauss(0, 0.12))
        else:
            l = r = 0.0

        self.levels_changed.emit(max(0.0, l), max(0.0, r))

    def set_audio_device(self, device_id: str):
        """Sæt lydoutput-enhed. device_id fra get_audio_devices()."""
        if VLC_AVAILABLE and self._media_player:
            self._media_player.audio_output_device_set(None, device_id)

    @staticmethod
    def get_audio_devices() -> list[dict]:
        """Returner liste af tilgængelige lydenheder."""
        if not VLC_AVAILABLE:
            return []
        try:
            instance = vlc.Instance("--no-video", "--quiet")
            mp = instance.media_player_new()
            devices = []
            d = mp.audio_output_device_enum()
            if d:
                node = d
                while node:
                    devices.append({
                        "id":   node.contents.device.decode("utf-8", errors="replace"),
                        "name": node.contents.description.decode("utf-8", errors="replace"),
                    })
                    node = node.contents.next
                vlc.libvlc_audio_output_device_list_release(d)
            mp.release()
            instance.release()
            return devices
        except Exception:
            return []

    def _on_end_reached(self, event):
        """Kaldes fra VLC's event-tråd — må IKKE røre Qt-objekter direkte."""
        from PyQt6.QtCore import QTimer as _QTimer
        _QTimer.singleShot(0, self._handle_end_in_main_thread)

    def _handle_end_in_main_thread(self):
        """Kaldes i main thread — her er det sikkert at røre Qt."""
        self._poll_timer.stop()
        self.song_ended.emit()
        self.state_changed.emit("stopped")


class PreviewPlayer(QObject):
    """Simpel preview-afspiller til bibliotek — ingen signals, bare play/stop."""

    def __init__(self, parent=None):
        super().__init__(parent)
        self._volume = 78
        self._device_id = ""
        if VLC_AVAILABLE:
            self._instance = vlc.Instance("--no-video", "--quiet")
            self._mp = self._instance.media_player_new()
        else:
            self._mp = None

    def play(self, path: str):
        if not VLC_AVAILABLE or not self._mp:
            return
        from player.player import Player
        vlc_path = Player._resolve_path(self, path)
        media = self._instance.media_new(vlc_path)
        self._mp.set_media(media)
        self._mp.audio_set_volume(self._volume)
        self._mp.play()
        # Sæt lydenhed efter play — VLC nulstiller den ved ny media
        if self._device_id:
            self._mp.audio_output_device_set(None, self._device_id)

    def pause(self):
        if VLC_AVAILABLE and self._mp:
            self._mp.pause()

    def resume(self):
        if VLC_AVAILABLE and self._mp:
            self._mp.play()

    def stop(self):
        if VLC_AVAILABLE and self._mp:
            self._mp.stop()

    def seek(self, fraction: float):
        if VLC_AVAILABLE and self._mp:
            self._mp.set_position(fraction)

    def is_playing(self) -> bool:
        if VLC_AVAILABLE and self._mp:
            return bool(self._mp.is_playing())
        return False

    def get_position(self) -> float:
        if VLC_AVAILABLE and self._mp:
            return max(0.0, self._mp.get_position())
        return 0.0

    def get_time(self) -> int:
        if VLC_AVAILABLE and self._mp:
            return max(0, self._mp.get_time() // 1000)
        return 0

    def get_duration(self) -> int:
        if VLC_AVAILABLE and self._mp:
            ms = self._mp.get_length()
            return max(0, ms // 1000)
        return 0

    def set_volume(self, volume: int):
        self._volume = volume
        if VLC_AVAILABLE and self._mp:
            self._mp.audio_set_volume(volume)

    def set_audio_device(self, device_id: str):
        self._device_id = device_id
        if VLC_AVAILABLE and self._mp:
            self._mp.audio_output_device_set(None, device_id)
