import sounddevice as sd
import numpy as np
import json

class WindowsAudioManager:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.init()
        return cls._instance
    def init(self):
        """
        Initialize Windows audio device and volume management.
        """
        host_apis = sd.query_hostapis()
        wasapi_device_indexes = None
        for api in host_apis:
            if api['name'].lower() == 'Windows WASAPI'.lower():
                wasapi_device_indexes = api['devices']
                break
        # print(f"Host APIs: {host_apis}")
        # print(f"WASAPI Device Indexes: {wasapi_device_indexes}")
        wasapi_device_indexes = set(wasapi_device_indexes) if wasapi_device_indexes is not None else set()
        self.devices = [dev for dev in sd.query_devices() if dev['index'] in wasapi_device_indexes]
        # self.devices = sd.query_devices()
        # print(f"devices: {self.devices}")

        self.default_input = sd.default.device[0]
        self.default_output = sd.default.device[1]
    
    def list_audio_devices(self, kind='input'):
        """
        List available audio devices.
        
        :param kind: 'input' or 'output'
        :return: List of audio devices
        """
        if kind == 'input':
            return [
                {
                    'index': dev['index'], 
                    'name': dev['name'], 
                    'channels': dev['max_input_channels'],
                    'default_samplerate': dev['default_samplerate']
                } 
                for dev in self.devices if dev['max_input_channels'] > 0
            ]
        elif kind == 'output':
            return [
                {
                    'index': dev['index'], 
                    'name': dev['name'], 
                    'channels': dev['max_output_channels'],
                    'default_samplerate': dev['default_samplerate']
                } 
                for dev in self.devices if dev['max_output_channels'] > 0
            ]
    def get_default_device(self, kind='input'):
        """
        Get the default audio device.
        
        :param kind: 'input' or 'output'
        :return: Default audio device information
        """
        if kind == 'input':
            dev = self.devices[self.default_input]
            return [
                {
                    'index': dev['index'], 
                    'name': dev['name'], 
                    'max_input_channels': dev['max_input_channels'],
                    'default_samplerate': dev['default_samplerate']
                } 
            ]
        
    def set_default_input_device(self, device):
        if(device is None):
            return 0
        """
        Set the default input audio device.
        
        :param device: Audio device information
        :return: Sample rate of the selected device
        """
        corrected_device = None
        # set corrected device index based on the name of the device matching the provided device name
        for dev in self.devices:
            if dev['name'] == device['name']:
                corrected_device = dev['index']
                break
        sd.default.device[0] = corrected_device
        self.default_input = corrected_device
        
        # Get the sample rate of the selected device
        device_info = sd.query_devices(corrected_device)
        return device_info['default_samplerate']
    
    def set_default_output_device(self, device):
        if(device is None):
            return self.get_current_output_device_sample_rate()
        """
        Set the default output audio device.
        
        :param device: Audio device information
        :return: Sample rate of the selected device
        """
        corrected_device = None
        # set corrected device index based on the name of the device matching the provided device name
        for dev in self.devices:
            if dev['name'] == device['name']:
                corrected_device = dev['index']
                break
        sd.default.device[1] = corrected_device
        self.default_output = corrected_device
        
        # Get the sample rate of the selected device
        device_info = sd.query_devices(corrected_device)
        return device_info['default_samplerate']