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

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

import shlex
import subprocess

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


class driver(sound_driver):
    """Generic sound driver for Fenrir screen reader.
    
    This driver provides sound playback through external command-line tools
    like sox, aplay, or other audio utilities. It supports both sound file
    playback and frequency/tone generation.
    
    Features:
    - Configurable external command execution
    - Sound file playback (WAV, OGG, etc.)
    - Frequency/tone generation
    - Process management and cancellation
    
    Attributes:
        proc: Currently running subprocess for sound playback
        soundFileCommand (str): Command template for playing sound files
        frequenceCommand (str): Command template for generating frequencies
    """
    def __init__(self):
        sound_driver.__init__(self)
        self.proc = None
        self.soundType = ""
        self.soundFileCommand = ""
        self.frequenceCommand = ""

    def initialize(self, environment):
        """Initialize the generic sound driver.
        
        Loads command templates from configuration for sound file playback
        and frequency generation.
        
        Args:
            environment: Fenrir environment dictionary with settings
        """
        self.env = environment
        self.soundFileCommand = self.env["runtime"][
            "SettingsManager"
        ].get_setting("sound", "genericPlayFileCommand")
        self.frequenceCommand = self.env["runtime"][
            "SettingsManager"
        ].get_setting("sound", "genericFrequencyCommand")
        if self.soundFileCommand == "":
            self.soundFileCommand = "play -q -v fenrirVolume fenrirSoundFile"
        if self.frequenceCommand == "":
            self.frequenceCommand = (
                "play -q -v fenrirVolume -n -c1 synth fenrirDuration sine fenrirFrequence"
            )
        self._initialized = True

    def play_frequence(
        self, frequence, duration, adjust_volume=0.0, interrupt=True
    ):
        """Play a tone at the specified frequency.
        
        Args:
            frequence (float): Frequency in Hz
            duration (float): Duration in seconds
            adjust_volume (float): Volume adjustment
            interrupt (bool): Whether to interrupt current sound
        """
        if not self._initialized:
            return
        if interrupt:
            self.cancel()
        popen_frequence_command = shlex.split(self.frequenceCommand)
        for idx, word in enumerate(popen_frequence_command):
            word = word.replace(
                "fenrirVolume", str(self.volume * adjust_volume)
            )
            word = word.replace("fenrirDuration", str(duration))
            word = word.replace("fenrirFrequence", str(frequence))
            popen_frequence_command[idx] = word
        self.proc = subprocess.Popen(
            popen_frequence_command,
            stdin=None,
            stdout=None,
            stderr=None,
            shell=False,
        )
        self.soundType = "frequence"

    def play_sound_file(self, file_path, interrupt=True):
        """Play a sound file.
        
        Args:
            file_path (str): Path to the sound file to play
            interrupt (bool): Whether to interrupt current sound
        """
        if not self._initialized:
            return
        if interrupt:
            self.cancel()
        # Validate file path to prevent injection
        import os

        if not os.path.isfile(file_path) or ".." in file_path:
            return
        popen_sound_file_command = shlex.split(self.soundFileCommand)
        for idx, word in enumerate(popen_sound_file_command):
            word = word.replace("fenrirVolume", str(self.volume))
            word = word.replace("fenrirSoundFile", shlex.quote(str(file_path)))
            popen_sound_file_command[idx] = word
        self.proc = subprocess.Popen(popen_sound_file_command, shell=False)
        self.soundType = "file"

    def cancel(self):
        """Cancel currently playing sound.
        
        Terminates the subprocess playing sound and cleans up resources.
        """
        if not self._initialized:
            return
        if self.soundType == "":
            return
        if self.soundType == "file":
            self.proc.kill()
            try:
                # Wait for process to finish to prevent zombies
                self.proc.wait(timeout=1.0)
            except subprocess.TimeoutExpired:
                pass  # Process already terminated
            except Exception as e:
                pass  # Handle any other wait errors
        if self.soundType == "frequence":
            self.proc.kill()
            try:
                # Wait for process to finish to prevent zombies
                self.proc.wait(timeout=1.0)
            except subprocess.TimeoutExpired:
                pass  # Process already terminated
            except Exception as e:
                pass  # Handle any other wait errors
        self.soundType = ""
