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

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

/**
 * @brief 构造函数
 */
AudioPlayer::AudioPlayer(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 配置混音器以启用扬声器输出
 */
bool AudioPlayer::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("[AudioPlayer] Cannot open mixer: %s", snd_strerror(err));
        return false;
    }
    
    // 附加到声卡
    err = snd_mixer_attach(mixer, card_name);
    if (err < 0) {
        LOG_WARN("[AudioPlayer] 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("[AudioPlayer] Cannot register mixer: %s", snd_strerror(err));
        snd_mixer_close(mixer);
        return false;
    }
    
    // 加载混音器元素
    err = snd_mixer_load(mixer);
    if (err < 0) {
        LOG_WARN("[AudioPlayer] Cannot load mixer: %s", snd_strerror(err));
        snd_mixer_close(mixer);
        return false;
    }
    
    LOG_INFO("[AudioPlayer] Mixer opened, scanning controls for playback...");
    
    snd_mixer_elem_t *elem;
    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_id_alloca(&sid);
        snd_mixer_selem_get_id(elem, sid);
        const char *name = snd_mixer_selem_id_get_name(sid);
        
        if (snd_mixer_selem_has_playback_volume(elem)) {
            long min, max;
            snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
            // Day 22: 跳过 ADC (麦克风) 的 playback volume 设置
            // 避免覆盖 AudioCapture 中设置的静音逻辑，防止严重的回环噪声
            if (strstr(name, "ADC") || strstr(name, "Input") || strstr(name, "Capture")) {
                LOG_INFO("[AudioPlayer] Skipping playback volume for '%s' (handled by AudioCapture)", name);
            } else {
                snd_mixer_selem_set_playback_volume_all(elem, max);
                LOG_INFO("[AudioPlayer] Unmuted playback volume for '%s'", name);
            }
        }
        
        if (snd_mixer_selem_has_playback_switch(elem)) {
            // Day 22 修复: 只禁用 loopback debug 开关
            // 注意：禁用 MIC playback switch 会导致麦克风无数据 (Day 12 发现)
            std::string switch_name(name);
            
            // 检查是否是 loopback 开关
            bool is_loopback = (switch_name.find("loopback") != std::string::npos) ||
                               (switch_name.find("Loopback") != std::string::npos) ||
                               (switch_name.find("LOOPBACK") != std::string::npos);
            
            // Day 23 fix: User confirmed disabling "MIC Playback Switch" kills the microphone capture.
            // We MUST enable it. Loopback/echo will be handled by other means or accepted for now.
            if (is_loopback) {
                snd_mixer_selem_set_playback_switch_all(elem, 0);  // 禁用
                LOG_INFO("[AudioPlayer] DISABLED playback switch for '%s' (noise reduction)", name);
            } else {
                snd_mixer_selem_set_playback_switch_all(elem, 1);  // 启用
                LOG_INFO("[AudioPlayer] Enabled playback switch for '%s'", name);
            }
        }
    }
    
    snd_mixer_close(mixer);
    return true;
}

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

/**
 * @brief 初始化 ALSA 播放设备
 */
bool AudioPlayer::init() {
    int err;
    
    // 0. 配置混音器
    // 尝试配置 hw:1 (可能是 I2S 接口) 和 hw:0 (可能是 Codec)
    setup_mixer("hw:1");
    // 很多板子的扬声器音量其实还是得在 Codec (hw:0) 上调
    if (m_device.find("hw:1") != std::string::npos) {
        setup_mixer("hw:0");
    }

    // 1. 打开 PCM 设备 (PLAYBACK 模式)
    err = snd_pcm_open(&m_pcm_handle, m_device.c_str(), 
                       SND_PCM_STREAM_PLAYBACK, 0);
    if (err < 0) {
        LOG_ERROR("[AudioPlayer] Cannot open device '%s': %s", 
                  m_device.c_str(), snd_strerror(err));
        return false;
    }
    
    LOG_INFO("[AudioPlayer] Opened device: %s", 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("[AudioPlayer] 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("[AudioPlayer] 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("[AudioPlayer] 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("[AudioPlayer] 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("[AudioPlayer] 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("[AudioPlayer] Cannot set channel count: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    // 8. 设置缓冲区大小 (可选，优化延迟)
    // Period: 160 frames (10ms @ 16kHz)
    // Buffer: 1600 frames (100ms @ 16kHz)
    snd_pcm_uframes_t period_size = 160;
    snd_pcm_uframes_t buffer_size = 1600;
    
    err = snd_pcm_hw_params_set_period_size_near(m_pcm_handle, hw_params, 
                                                   &period_size, 0);
    if (err < 0) {
        LOG_WARN("[AudioPlayer] 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("[AudioPlayer] Cannot set buffer size: %s", snd_strerror(err));
    }
    
    // 9. 应用硬件参数
    err = snd_pcm_hw_params(m_pcm_handle, hw_params);
    if (err < 0) {
        LOG_ERROR("[AudioPlayer] 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("[AudioPlayer] Cannot prepare device: %s", snd_strerror(err));
        snd_pcm_close(m_pcm_handle);
        m_pcm_handle = nullptr;
        return false;
    }
    
    LOG_INFO("[AudioPlayer] Initialized: %d Hz, %d channels, S16_LE", 
             actual_rate, m_channels);
    LOG_INFO("[AudioPlayer] Period: %lu frames, Buffer: %lu frames", 
             period_size, buffer_size);
    
    return true;
}

/**
 * @brief 写入 PCM 数据到扬声器
 * @param buffer 输入缓冲区 (int16_t 数组)
 * @param frames 需要写入的帧数
 * @return 实际写入的帧数，<0 表示错误
 */
snd_pcm_sframes_t AudioPlayer::write(const int16_t* buffer, snd_pcm_uframes_t frames) {
    if (!m_pcm_handle) {
        LOG_ERROR("[AudioPlayer] Device not initialized");
        return -1;
    }
    
    snd_pcm_sframes_t frames_written = snd_pcm_writei(m_pcm_handle, buffer, frames);
    
    if (frames_written < 0) {
        // 错误处理
        if (frames_written == -EPIPE) {
            // Underrun (缓冲区欠载)
            LOG_WARN("[AudioPlayer] Underrun occurred, recovering...");
            snd_pcm_prepare(m_pcm_handle);
            
            // 重试写入
            frames_written = snd_pcm_writei(m_pcm_handle, buffer, frames);
            if (frames_written < 0) {
                LOG_ERROR("[AudioPlayer] Write failed after underrun recovery");
                return frames_written;
            }
        } else if (frames_written == -ESTRPIPE) {
            // Suspend (设备挂起)
            LOG_WARN("[AudioPlayer] Device suspended, resuming...");
            while ((frames_written = snd_pcm_resume(m_pcm_handle)) == -EAGAIN) {
                usleep(100000);  // 等待 100ms
            }
            if (frames_written < 0) {
                // 恢复失败，重新准备
                frames_written = snd_pcm_prepare(m_pcm_handle);
                if (frames_written < 0) {
                    LOG_ERROR("[AudioPlayer] Cannot recover from suspend: %s", 
                              snd_strerror(frames_written));
                    return frames_written;
                }
            }
            
            // 重试写入
            frames_written = snd_pcm_writei(m_pcm_handle, buffer, frames);
            if (frames_written < 0) {
                LOG_ERROR("[AudioPlayer] Write failed after suspend recovery");
                return frames_written;
            }
        } else {
            // 其他错误
            LOG_ERROR("[AudioPlayer] Write error: %s", snd_strerror(frames_written));
            
            // 尝试自动恢复
            int err = snd_pcm_recover(m_pcm_handle, frames_written, 0);
            if (err < 0) {
                LOG_ERROR("[AudioPlayer] Cannot recover: %s", snd_strerror(err));
                return frames_written;
            }
            
            // 重试写入
            frames_written = snd_pcm_writei(m_pcm_handle, buffer, frames);
            if (frames_written < 0) {
                LOG_ERROR("[AudioPlayer] Write failed after generic recovery");
                return frames_written;
            }
        }
    }
    
    // 成功写入
    if (frames_written != (snd_pcm_sframes_t)frames) {
        LOG_DEBUG("[AudioPlayer] Short write: expected %lu, wrote %ld frames", 
                  frames, frames_written);
    }
    
    return frames_written;
}
