use crate::error::*;
use cpal::SampleRate;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use serde::{Deserialize, Serialize};
use tokio::sync::broadcast;
use tracing::{debug, error, info};

/// 音频数据输入配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AudioInputConfig {
    pub sample_channel_size: usize,
    pub use_device: usize,
    pub sample_rate: u32,
    pub channels: u16,
}

/// 音频数据输入
pub struct AudioInput {
    stream: cpal::Stream,
    sample_sender: broadcast::Sender<f32>,
}

impl AudioInput {
    /// 初始化音频输入实例，传入音频采用发送实例
    pub fn init(config: AudioInputConfig) -> Result<Self> {
        let host = cpal::default_host();
        for (device_index, device) in host.input_devices()?.enumerate() {
            if device_index == config.use_device {
                debug!("using input device {}", device.name()?);
                for supported_input_config_range in device.supported_input_configs()? {
                    if supported_input_config_range.channels().eq(&config.channels)
                        && supported_input_config_range
                            .sample_format()
                            .eq(&cpal::SampleFormat::F32)
                        && let Some(supported_input_config) = supported_input_config_range
                            .try_with_sample_rate(SampleRate(config.sample_rate))
                            .map(|x| x.config())
                    {
                        info!("input stream config: {:?}", supported_input_config);
                        let (sample_sender, _) = broadcast::channel(config.sample_channel_size);
                        let cloned_sample_sender = sample_sender.clone();
                        let input_stream = device.build_input_stream(
                            &supported_input_config,
                            move |data: &[f32], _| {
                                for &sample in data {
                                    if let Err(e) = cloned_sample_sender.send(sample) {
                                        error!("failed to send sample: {}", e);
                                        return;
                                    }
                                }
                            },
                            |e| {
                                error!("Audio input stream error: {}", e);
                            },
                            None,
                        )?;
                        return Ok(Self {
                            stream: input_stream,
                            sample_sender,
                        });
                    }
                }
            }
        }
        Err(NihilityListenerError::Init(
            "could not build audio input".to_string(),
        ))
    }

    pub fn get_sample_receiver(&self) -> broadcast::Receiver<f32> {
        self.sample_sender.subscribe()
    }

    /// 开始接收音频输入
    pub async fn run(&self) -> Result<()> {
        self.stream.play()?;
        Ok(())
    }
}

impl Default for AudioInputConfig {
    fn default() -> Self {
        Self {
            sample_channel_size: 16000 * 3,
            use_device: 0,
            sample_rate: 16000,
            channels: 1,
        }
    }
}
