use std::sync::{
        atomic::AtomicBool, Arc
    };

use cpal::{
    Device, FrameCount, Host, Stream,
    traits::{DeviceTrait, HostTrait, StreamTrait},
};
use ringbuf::{
    HeapRb, SharedRb,
    traits::{Consumer, Observer, Producer, Split},
    wrap::caching::Caching,
};

type SampleTx = Caching<Arc<HeapRb<u8>>, true, false>;

// TODO: Audio should be run through a low-pass filter,
// a high-pass filter, as well as some kind of envelope
// around pause events
pub struct Audio {
    _host: Host,
    _device: Device,
    _stream: Stream,
    rb: SampleTx,
    last: usize,
    max: usize,
    paused: Arc<AtomicBool>,
}

impl Audio {
    pub fn init() -> Self {
        const BUFFER_SIZE: usize = 1 << 10;
        let host = cpal::default_host();
        let device = host.default_output_device().unwrap();
        // let mut configs = device.supported_output_configs().unwrap();
        // dbg!(configs.find(|c| c.sample_format() == SampleFormat::U8));
        // let v = dbg!(configs.next().unwrap().buffer_size());
        // let mut t = 0;
        let (prod, mut cons) = SharedRb::new(BUFFER_SIZE * 1024 * 1024).split();
        let paused = Arc::new(AtomicBool::new(true));
        let paused_inner = Arc::clone(&paused);

        let stream = device
            .build_output_stream(
                &cpal::StreamConfig {
                    channels: 1,
                    sample_rate: 60 * 3723,
                    buffer_size: cpal::BufferSize::Fixed(BUFFER_SIZE as FrameCount),
                },
                move |a: &mut [u8], _b| {
                    if !paused_inner.load(std::sync::atomic::Ordering::Acquire) {
                        let taken = cons.pop_slice(a);
                        a[taken..].fill(128);
                    }
                },
                |e| eprintln!("Audio: {e}"),
                None,
            )
            .unwrap();
        stream.play().unwrap();
        Self {
            _host: host,
            _device: device,
            _stream: stream,
            rb: prod,
            paused,
            last: 0,
            max: 0,
        }
    }

    pub fn pause(&mut self) {
        self.paused.store(true, std::sync::atomic::Ordering::Release);
        let _ = self._stream.pause();
    }

    pub fn submit(&mut self, samples: &[u8]) {
        let start = self.rb.occupied_len();
        self.max = self.max.max(self.last - start);
        println!("Buffer size: {:07}, Max: {:07}", start, self.max);
        println!(
            "Adding: {:07}, Played: {:07}",
            samples.len(),
            self.last - start
        );
        self.rb.push_slice(samples);
        self.last = self.rb.occupied_len();
        if self.last > 9000 {
            self.paused.store(false, std::sync::atomic::Ordering::Release);
            let _ = self._stream.play();
        }
    }
}
