import sounddevice as sd
import numpy as np
import os
from datetime import datetime
import scipy.io.wavfile as wavfile
from metadata_manager import MetaDataManager

class AudioRecorder:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            print("Creating new AudioRecorder instance")
            cls._instance = super().__new__(cls)
            cls._instance.init()
        return cls._instance
    def init(self):
        """
        Initialize audio recorder with configurable parameters.
        
        :param duration: Length of audio buffer in seconds
        :param sample_rate: Audio sample rate (if None, use default device sample rate)
        :param channels: Number of audio channels
        """
        print(f"Initializing AudioRecorder")
        self.duration = 30
        self.sample_rate = 44100
        self.channels = 2
        self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
        self.recordings_dir = "recordings"
        
        self.stream = sd.InputStream(
            samplerate=self.sample_rate, 
            callback=self.record_callback
        )

    def refresh_stream(self):
        """
        Refresh the audio stream with updated parameters.
        """
        was_active = self.stream.active 
        if was_active:
            self.stream.stop()
        
        self.buffer = np.zeros((int(self.duration * self.sample_rate), self.channels), dtype=np.float32)
        print(f"AudioRecorder initialized with duration={self.duration}s, sample_rate={self.sample_rate}Hz, channels={self.channels}")
        self.stream = sd.InputStream(
            samplerate=self.sample_rate, 
            callback=self.record_callback
        )

        if was_active:
            self.stream.start()


        
    
    def record_callback(self, indata, frames, time, status):
        """
        Circular buffer callback for continuous recording.
        
        :param indata: Input audio data
        :param frames: Number of frames
        :param time: Timestamp
        :param status: Recording status
        """
        if status:
            print(f"Recording status: {status}")
        
        # Circular buffer implementation
        self.buffer = np.roll(self.buffer, -frames, axis=0)
        self.buffer[-frames:] = indata
    
    def save_last_n_seconds(self):
        """
        Save the last n seconds of audio to a file.
        
        :param output_dir: Directory to save recordings
        :return: Path to saved audio file
        """
        # Create output directory if it doesn't exist
        os.makedirs(self.recordings_dir, exist_ok=True)
        
        # Generate filename with timestamp
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        filename = os.path.join(self.recordings_dir, f"audio_capture_{timestamp}.wav")
        
        # Normalize audio to prevent clipping
        audio_data = self.buffer / np.max(np.abs(self.buffer)) * .5
        
        # Convert float32 to int16 for WAV file
        audio_data_int16 = (audio_data * 32767).astype(np.int16)
        
        # Write buffer to file
        wavfile.write(filename, int(self.sample_rate), audio_data_int16)

        meta = MetaDataManager()

        clip_metadata = {
            "filename": filename,
            "name": f"Clip {timestamp}",
            "playbackType":"playStop",
            "volume": 1.0,  
        }
        
        meta.add_clip_to_collection("Uncategorized",  clip_metadata )

        
        return clip_metadata
    
    def set_buffer_duration(self, duration):
        """
        Set the duration of the audio buffer.
        
        :param duration: New buffer duration in seconds
        """
        self.duration = duration
        self.buffer = np.zeros((int(duration * self.sample_rate), self.channels), dtype=np.float32)

    def set_recording_directory(self, directory):
        """
        Set the directory where recordings will be saved.
        
        :param directory: Path to the recordings directory
        """
        self.recordings_dir = directory
    
    def start_recording(self):
        """
        Start continuous audio recording with circular buffer.
        """
        if(self.stream.active):
            print("Already recording")
            return
        print('number of channels', self.channels)
        
        self.stream.start()

    def stop_recording(self):
        """
        Stop continuous audio recording with circular buffer.
        """
        if(not self.stream.active):
            print("Already stopped")
            return
        
        self.stream.stop()

    def is_recording(self):
        """
        Check if the audio stream is currently active.
        
        :return: True if recording, False otherwise
        """
        return self.stream.active