
use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, StreamInstant, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}};

use crate::audio::{AudioOutput, SAMPLE_RATE};

#[allow(dead_code)]
pub struct CpalAudioOutput {
    stream: Stream,
}

impl CpalAudioOutput {
    pub fn create_audio_output(output: AudioOutput) -> CpalAudioOutput {
        let device = cpal::default_host()
            .default_output_device()
            .expect("No sound output device available");

        let config: StreamConfig = device
            .supported_output_configs()
            .expect("error while querying configs")
            .find(|config| config.sample_format() == SampleFormat::F32 && config.channels() == 2)
            .expect("no supported config?!")
            .with_sample_rate(SampleRate(SAMPLE_RATE as u32))
            .into();

        let data_callback = move |data: &mut [f32], info: &OutputCallbackInfo| {
            let mut index = 0;
            while index < data.len() {
                if let Some((clock, mut frame)) = output.receive() {
                    let size = (frame.data.len() * 2).min(data.len() - index);
                    frame.data.iter()
                        .zip(data[index..index + size].chunks_mut(2))
                        .for_each(|(sample, location)| {
                            location[0] = sample.0;
                            location[1] = sample.1;
                        });
                    index += size;
                    if size < frame.data.len() * 2 {
                        frame.data.drain(0..size / 2);
                        output.put_back(clock, frame);
                    }
                } else {
                    log::debug!("missed an audio frame");
                    break;
                }
            }
        };

        let stream = device.build_output_stream(
            &config,
            data_callback,
            move |err| {
                log::error!("ERROR: {:?}", err);
            },
        ).unwrap();

        stream.play().unwrap();

        CpalAudioOutput {
            stream,
        }
    }

    pub fn set_mute(&self, mute: bool) {
        if mute {
            self.stream.pause().unwrap();
        } else {
            self.stream.play().unwrap();
        }
    }
}

