import asyncio
import logging
import struct

try:
    import pyaudio
except ImportError:
    raise ImportError(
        "Missing optional dependency 'mic'. To fix this, run: "
        "uv add hik_isapi_audio --extra mic "
        "OR "
        "pip install 'hik_isapi_audio[mic]'"
    )

logger = logging.getLogger(__name__)


# --- 1. 纯 Python G.711a 编码器 ---
class ALawEncoder:
    def __init__(self):
        # 预生成查找表
        self.table = bytes([self._linear_to_alaw(i - 32768) for i in range(65536)])

    def _linear_to_alaw(self, pcm_val):
        if pcm_val < 0:
            pcm_val = -pcm_val
            mask = 0x7F
        else:
            mask = 0xFF
        if pcm_val > 32767:
            pcm_val = 32767
        if pcm_val >= 512:
            if pcm_val >= 16384:
                exp = 7
            elif pcm_val >= 8192:
                exp = 6
            elif pcm_val >= 4096:
                exp = 5
            elif pcm_val >= 2048:
                exp = 4
            elif pcm_val >= 1024:
                exp = 3
            else:
                exp = 2
            mantissa = (pcm_val >> (exp + 3)) & 0x0F
            alaw = (exp << 4) | mantissa
        else:
            exp = 1 if pcm_val >= 256 else 0
            mantissa = (pcm_val >> 4) & 0x0F
            alaw = (exp << 4) | mantissa
        return (alaw ^ 0xD5) & mask

    def encode(self, pcm_data):
        count = len(pcm_data) // 2
        short_array = struct.unpack(f"<{count}h", pcm_data)
        return bytes([self.table[val + 32768] for val in short_array])


async def mic_capture_task(queue):
    """麦克风采集任务, 音频数据放置队列中"""

    p = pyaudio.PyAudio()
    stream = p.open(
        format=pyaudio.paInt16, channels=1, rate=8000, input=True, frames_per_buffer=160
    )
    encoder = ALawEncoder()

    logger.info("麦克风已开启")
    try:
        while True:
            data = await asyncio.to_thread(
                stream.read, 160, exception_on_overflow=False
            )
            g711_data = encoder.encode(data)
            if queue.full():
                try:
                    queue.get_nowait()
                except asyncio.QueueEmpty:
                    pass

            queue.put_nowait(g711_data)
    finally:
        stream.stop_stream()
        stream.close()
        p.terminate()


async def mic_data_generator(queue):
    while True:
        chunk = await queue.get()
        yield chunk
