#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.

import threading
import time

from fenrirscreenreader.core import debug
from fenrirscreenreader.core.soundDriver import sound_driver

_gstreamerAvailable = False
try:
    import gi
    from gi.repository import GLib

    gi.require_version("Gst", "1.0")
    from gi.repository import Gst

    _gstreamerAvailable, args = Gst.init_check(None)
except Exception as e:
    _gstreamerAvailable = False
    _availableError = str(e)


class driver(sound_driver):
    def __init__(self):
        sound_driver.__init__(self)
        self._source = None
        self._sink = None

    def initialize(self, environment):
        self.env = environment
        global _gstreamerAvailable
        self._initialized = _gstreamerAvailable
        if not self._initialized:
            global _availableError
            self.environment["runtime"]["DebugManager"].write_debug_out(
                "Gstreamer not available " + _availableError,
                debug.DebugLevel.ERROR,
            )
            return
        self._player = Gst.ElementFactory.make("playbin", "player")
        bus = self._player.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self._on_player_message)

        self._pipeline = Gst.Pipeline(name="fenrir-pipeline")
        bus = self._pipeline.get_bus()
        bus.add_signal_watch()
        bus.connect("message", self._on_pipeline_message)

        self._source = Gst.ElementFactory.make("audiotestsrc", "src")
        self._volume = Gst.ElementFactory.make("volume", "volume")
        self._sink = Gst.ElementFactory.make("autoaudiosink", "output")
        self._pipeline.add(self._source)
        self._pipeline.add(self._volume)
        self._pipeline.add(self._sink)
        self._source.link(self._volume)
        self._volume.link(self._sink)
        self.mainloop = GLib.MainLoop()
        self.thread = threading.Thread(target=self.mainloop.run)
        self.thread.start()

    def shutdown(self):
        if not self._initialized:
            return
        self.cancel()
        self.mainloop.quit()
        # Wait for the GLib MainLoop thread to finish to prevent shutdown races
        if hasattr(self, "thread") and self.thread.is_alive():
            # 2 second timeout to prevent hanging
            self.thread.join(timeout=2.0)

    def _on_player_message(self, bus, message):
        if not self._initialized:
            return
        if message.type == Gst.MessageType.EOS:
            self._player.set_state(Gst.State.NULL)
        elif message.type == Gst.MessageType.ERROR:
            self._player.set_state(Gst.State.NULL)
            error, info = message.parse_error()
            self.env["runtime"]["DebugManager"].write_debug_out(
                "GSTREAMER: _on_player_message" + str(error) + str(info),
                debug.DebugLevel.WARNING,
            )

    def _on_pipeline_message(self, bus, message):
        if not self._initialized:
            return
        if message.type == Gst.MessageType.EOS:
            self._pipeline.set_state(Gst.State.NULL)
        elif message.type == Gst.MessageType.ERROR:
            self._pipeline.set_state(Gst.State.NULL)
            error, info = message.parse_error()
            self.env["runtime"]["DebugManager"].write_debug_out(
                "GSTREAMER: _on_pipeline_message" + str(error) + str(info),
                debug.DebugLevel.WARNING,
            )

    def _on_timeout(self, element):
        if not self._initialized:
            return
        element.set_state(Gst.State.NULL)

    def play_sound_file(self, file_name, interrupt=True):
        if not self._initialized:
            return
        if interrupt:
            self.cancel()
        self._player.set_property("volume", self.volume)
        self._player.set_property("uri", "file://%s" % file_name)
        self._player.set_state(Gst.State.PLAYING)

    def play_frequence(
        self, frequence, duration, adjust_volume=0.0, interrupt=True
    ):
        if not self._initialized:
            return
        if interrupt:
            self.cancel()
        # Always reset pipeline to prevent volume accumulation
        self._pipeline.set_state(Gst.State.NULL)
        duration = duration * 1000
        # Use dedicated volume element for better control
        # GStreamer volume property behaves very differently than sox for low frequencies
        if adjust_volume > 0.8:  # This indicates low frequency indentation beeps  
            # Extremely aggressive boost - GStreamer really struggles with low frequencies
            effective_volume = self.volume * adjust_volume * 50.0  # Ridiculous multiplier to match sox
        else:
            effective_volume = self.volume * adjust_volume * 3.0
        self._volume.set_property("volume", effective_volume)
        self._source.set_property("volume", 1.0)  # Set source to full, control via volume element
        self._source.set_property("freq", frequence)
        self._pipeline.set_state(Gst.State.PLAYING)
        GLib.timeout_add(duration, self._on_timeout, self._pipeline)

    def cancel(self, element=None):
        if not self._initialized:
            return
        if element:
            element.set_state(Gst.State.NULL)
            return
        self._player.set_state(Gst.State.NULL)
        self._pipeline.set_state(Gst.State.NULL)
