/**
 * @file audio_capture.cpp
 * @brief 音频采集实现 - 基于 ALSA
 * @date 2024-11-24
 * @platform Avaota F1 (V821 / RISC-V)
 */

#include "audio_capture.h"
#include "../utils/logger.h"
#include <cstring>
#include <errno.h>

/**
 * @brief 构造函数
 */
AudioCapture::AudioCapture(const std::string& device, int sample_rate, int channels)
    : m_device(device)
    , m_sample_rate(sample_rate)
    , m_channels(channels)
    , m_pcm_handle(nullptr)
{
}

/**
 * @brief 析构函数
 */
AudioCapture::~AudioCapture() {
    if (m_pcm_handle) {
        snd_pcm_drain(m_pcm_handle);
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        LOG_INFO("[AudioCapture] Device closed");
    }
}

/**
 * @brief 配置混音器以启用麦克风输入
 * @param card_name 声卡名称，如 "hw:0"
 * @return true 如果成功
 */
bool AudioCapture::setup_mixer(const char* card_name) {
    snd_mixer_t *mixer = nullptr;
    snd_mixer_selem_id_t *sid = nullptr;
    int err;
    
    // 打开混音器
    err = snd_mixer_open(&mixer, 0);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot open mixer: %s", snd_strerror(err));
        return false;
    }
    
    // 附加到声卡
    err = snd_mixer_attach(mixer, card_name);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot attach mixer to %s: %s", card_name, snd_strerror(err));
        snd_mixer_close(mixer);
        return false;
    }
    
    // 注册混音器
    err = snd_mixer_selem_register(mixer, NULL, NULL);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot register mixer: %s", snd_strerror(err));
        snd_mixer_close(mixer);
        return false;
    }
    
    // 加载混音器元素
    err = snd_mixer_load(mixer);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot load mixer: %s", snd_strerror(err));
        snd_mixer_close(mixer);
        return false;
    }
    
    LOG_INFO("[AudioCapture] Mixer opened successfully, scanning controls...");
    
    // 遍历所有混音器元素并打印信息
    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_elem_t *elem;
    int control_count = 0;
    
    for (elem = snd_mixer_first_elem(mixer); elem; elem = snd_mixer_elem_next(elem)) {
        if (!snd_mixer_selem_is_active(elem)) continue;
        
        snd_mixer_selem_get_id(elem, sid);
        const char *name = snd_mixer_selem_id_get_name(sid);
        control_count++;
        
        // 检查是否有捕获能力
        bool has_capture = snd_mixer_selem_has_capture_volume(elem) || 
                          snd_mixer_selem_has_capture_switch(elem);
        
        if (has_capture) {
            LOG_INFO("[AudioCapture] Found capture control: '%s'", name);
            
            // 尝试启用捕获开关
            if (snd_mixer_selem_has_capture_switch(elem)) {
                snd_mixer_selem_set_capture_switch_all(elem, 1);
                LOG_INFO("[AudioCapture] Enabled capture switch for '%s'", name);
            }
            
            // 尝试设置捕获音量到最大
            if (snd_mixer_selem_has_capture_volume(elem)) {
                long min, max;
                snd_mixer_selem_get_capture_volume_range(elem, &min, &max);
                snd_mixer_selem_set_capture_volume_all(elem, max);
                LOG_INFO("[AudioCapture] Set capture volume to max (%ld) for '%s'", max, name);
            }
        }
        
        // 尝试匹配常见的麦克风相关控制项
        if (strstr(name, "MIC") || strstr(name, "Mic") || strstr(name, "mic") ||
            strstr(name, "ADC") || strstr(name, "Capture") || strstr(name, "Input") ||
            strstr(name, "Line") || strstr(name, "LINEIN")) {
            LOG_INFO("[AudioCapture] Found potential mic control: '%s'", name);
            
            // Day 23 fix: Restore playback volume to MAX for ADC/LINEOUT.
            // On some codecs (e.g. AC108/ES8388), "Playback Volume" on ADC actually controls 
            // the digital gain of the ADC signal *before* it splits to capture/loopback.
            // Setting it to 0 silences the capture. We will deal with echo via software if needed.
            if (snd_mixer_selem_has_playback_volume(elem)) {
                long min, max;
                snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
                // Day 23: Set to 80% to balance capture signal vs loopback noise
                long target = min + (max - min) * 0.8;
                snd_mixer_selem_set_playback_volume_all(elem, target);
                LOG_INFO("[AudioCapture] Set playback volume to 80%% (%ld/%ld) for '%s'", target, max, name);
            }
            
            // Enable playback switch (to ensure hardware is powered/active)
            if (snd_mixer_selem_has_playback_switch(elem)) {
                snd_mixer_selem_set_playback_switch_all(elem, 1); // Enable
                LOG_INFO("[AudioCapture] Enabled playback switch for '%s' (required for capture)", name);
            }
        }
    }
    
    LOG_INFO("[AudioCapture] Scanned %d mixer controls", control_count);
    
    snd_mixer_close(mixer);
    return true;
}

/**
 * @brief 初始化 ALSA 采集设备
 */
bool AudioCapture::init() {
    int err;
    
    // 0. 首先配置混音器，启用麦克风输入
    LOG_INFO("[AudioCapture] Setting up mixer for microphone...");
    setup_mixer("hw:0");
    
    // 1. 打开 PCM 设备 (CAPTURE 模式，非阻塞)
    // 使用 SND_PCM_NONBLOCK 避免 read 阻塞
    err = snd_pcm_open(&m_pcm_handle, m_device.c_str(), 
                       SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot open device '%s': %s", 
                  m_device.c_str(), snd_strerror(err));
        return false;
    }
    
    LOG_INFO("[AudioCapture] Opened device: %s (non-blocking)", m_device.c_str());
    
    // 2. 分配硬件参数对象
    snd_pcm_hw_params_t* hw_params;
    snd_pcm_hw_params_alloca(&hw_params);
    
    // 3. 初始化硬件参数
    err = snd_pcm_hw_params_any(m_pcm_handle, hw_params);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot initialize hw params: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 4. 设置访问模式 (交织模式)
    err = snd_pcm_hw_params_set_access(m_pcm_handle, hw_params, 
                                        SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot set access type: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 5. 设置采样格式 (16-bit signed little-endian)
    err = snd_pcm_hw_params_set_format(m_pcm_handle, hw_params, 
                                        SND_PCM_FORMAT_S16_LE);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot set sample format: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 6. 设置采样率
    unsigned int actual_rate = m_sample_rate;
    err = snd_pcm_hw_params_set_rate_near(m_pcm_handle, hw_params, 
                                           &actual_rate, 0);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot set sample rate: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    if (actual_rate != (unsigned int)m_sample_rate) {
        LOG_WARN("[AudioCapture] Sample rate %d Hz not supported, using %d Hz", 
                 m_sample_rate, actual_rate);
    }
    
    // 7. 设置声道数
    err = snd_pcm_hw_params_set_channels(m_pcm_handle, hw_params, m_channels);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot set channel count: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 8. 设置缓冲区大小 (增大以提高稳定性)
    // Period: 480 frames (30ms @ 16kHz)
    // Buffer: 4800 frames (300ms @ 16kHz)
    // Day 22 优化: 使用20ms包(320 samples)，与服务器ASR期望一致
    snd_pcm_uframes_t period_size = 320;  // 20ms @ 16kHz = 320 samples
    snd_pcm_uframes_t buffer_size = 4800;
    
    err = snd_pcm_hw_params_set_period_size_near(m_pcm_handle, hw_params, 
                                                   &period_size, 0);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot set period size: %s", snd_strerror(err));
    }
    
    err = snd_pcm_hw_params_set_buffer_size_near(m_pcm_handle, hw_params, 
                                                   &buffer_size);
    if (err < 0) {
        LOG_WARN("[AudioCapture] Cannot set buffer size: %s", snd_strerror(err));
    }
    
    // 9. 应用硬件参数
    err = snd_pcm_hw_params(m_pcm_handle, hw_params);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot apply hw params: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 10. 准备设备
    err = snd_pcm_prepare(m_pcm_handle);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot prepare device: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 11. 启动捕获流 (非阻塞模式必须显式启动)
    err = snd_pcm_start(m_pcm_handle);
    if (err < 0) {
        LOG_ERROR("[AudioCapture] Cannot start capture: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    LOG_INFO("[AudioCapture] Initialized: %d Hz, %d channels, S16_LE", 
             actual_rate, m_channels);
    LOG_INFO("[AudioCapture] Period: %lu frames, Buffer: %lu frames", 
             period_size, buffer_size);
    LOG_INFO("[AudioCapture] Capture stream started");
    
    return true;
}

/**
 * @brief 读取 PCM 数据
 * @param buffer 输出缓冲区 (int16_t 数组)
 * @param frames 需要读取的帧数
 * @return 实际读取的帧数，<0 表示错误
 */
snd_pcm_sframes_t AudioCapture::read(int16_t* buffer, snd_pcm_uframes_t frames) {
    if (!m_pcm_handle) {
        LOG_ERROR("[AudioCapture] Device not initialized");
        return -1;
    }
    
    snd_pcm_sframes_t frames_read = snd_pcm_readi(m_pcm_handle, buffer, frames);
    
    if (frames_read < 0) {
        // 错误处理
        if (frames_read == -EAGAIN) {
            // 非阻塞模式：没有可用数据，返回 0 让调用者等待
            return 0;
        } else if (frames_read == -EPIPE) {
            // Overrun (缓冲区溢出)
            LOG_WARN("[AudioCapture] Overrun occurred, recovering...");
            snd_pcm_prepare(m_pcm_handle);
            return 0;  // 本次读取失败，下次重试
        } else if (frames_read == -ESTRPIPE) {
            // Suspend (设备挂起)
            LOG_WARN("[AudioCapture] Device suspended, resuming...");
            while ((frames_read = snd_pcm_resume(m_pcm_handle)) == -EAGAIN) {
                usleep(100000);  // 等待 100ms
            }
            if (frames_read < 0) {
                // 恢复失败，重新准备
                frames_read = snd_pcm_prepare(m_pcm_handle);
                if (frames_read < 0) {
                    LOG_ERROR("[AudioCapture] Cannot recover from suspend: %s", 
                              snd_strerror(frames_read));
                    return frames_read;
                }
            }
            return 0;
        } else {
            // 其他错误
            LOG_ERROR("[AudioCapture] Read error: %s", snd_strerror(frames_read));
            
            // 尝试自动恢复
            int err = snd_pcm_recover(m_pcm_handle, frames_read, 0);
            if (err < 0) {
                LOG_ERROR("[AudioCapture] Cannot recover: %s, attempting to reinitialize...", snd_strerror(err));
                
                // 关闭设备
                snd_pcm_close(m_pcm_handle);
                m_pcm_handle = nullptr;
                
                // 等待一下再重新初始化
                usleep(500000);  // 500ms
                
                // 重新初始化
                if (!init()) {
                    LOG_ERROR("[AudioCapture] Failed to reinitialize device");
                    return -1;  // 彻底失败
                }
                
                LOG_INFO("[AudioCapture] Device reinitialized successfully");
                return 0;  // 本次读取失败，但设备已恢复
            }
            return 0;
        }
    }
    
    // 成功读取
    if (frames_read != (snd_pcm_sframes_t)frames) {
        LOG_DEBUG("[AudioCapture] Short read: expected %lu, got %ld frames", 
                  frames, frames_read);
    }
    
    return frames_read;
}
