use anyhow::{anyhow, Result};
use ringbuffer::{ConstGenericRingBuffer, RingBufferExt, RingBufferWrite};
use std::{
    sync::{Arc, Mutex},
    thread::{self, JoinHandle},
    time::Instant,
};

use pulse::sample::{Format, Spec};

use self::dsp::ZTransformFilter;

mod capture;
mod dsp;

const SAMPLE_RATE: usize = 5000;
const PULSE_UPDATES_PER_SECOND: usize = 50;
const BUFFER_SIZE: usize = SAMPLE_RATE / PULSE_UPDATES_PER_SECOND;

const POINTS_PER_SECOND: usize = SAMPLE_RATE / 200;
pub const MILLIS_PER_POINT: usize = 1000 / POINTS_PER_SECOND;
const POINT_MIN_COUNT: usize = 2 * POINTS_PER_SECOND;
pub const POINT_BUFFER_SIZE: usize = POINT_MIN_COUNT.next_power_of_two();

pub struct AudioCaptureThread {
    join_handle: Option<JoinHandle<Result<()>>>,
    shared_state: Arc<Mutex<SharedState>>,
}

impl AudioCaptureThread {
    pub fn new() -> Self {
        let shared_state = Arc::new(Mutex::new(SharedState::new()));
        let join_handle = {
            let shared_state = shared_state.clone();
            Some(thread::spawn(move || audio_capture_thread(shared_state)))
        };

        Self {
            shared_state,
            join_handle,
        }
    }

    pub fn stop(&mut self) -> Result<()> {
        if self.join_handle.is_none() {
            return Err(anyhow!(
                "join_handle was none, was stop called multiple times?"
            ));
        } else {
            let handle = self.join_handle.take().unwrap();

            handle.join().unwrap()
        }
    }
    
    pub fn get_points(&self) -> Vec<f32> {
        let state = self.shared_state.lock().unwrap();
        state.point_buf.to_vec()
    }

    pub fn get_last_update(&self) -> Instant {
        let state = self.shared_state.lock().unwrap();
        state.last_update
    }
}

struct SharedState {
    running: bool,
    point_buf: ConstGenericRingBuffer<f32, POINT_BUFFER_SIZE>,
    last_update: Instant,
}

impl SharedState {
    pub fn new() -> Self {
        let mut point_buf: ConstGenericRingBuffer<f32, POINT_BUFFER_SIZE> = Default::default();
        point_buf.fill_default();

        Self {
            running: true,
            point_buf,
            last_update: Instant::now(),
        }
    }
}

fn audio_capture_thread(state: Arc<Mutex<SharedState>>) -> Result<()> {
    let spec = Spec {
        format: Format::F32le,
        rate: SAMPLE_RATE as u32,
        channels: 1,
    };

    let reader = capture::get_audio_reader(&spec)?;
    let mut buffer = [0u8; 4 * BUFFER_SIZE];

    let mut bass_filter = dsp::BassFilter::default();
    let mut envelope_filter = dsp::EnvelopeFilter::default();
    let mut beat_filter = dsp::BeatFilter::default();
    let mut j = 0;

    loop {
        {
            if state.lock().unwrap().running == false {
                break Ok(());
            }
        }

        reader.read(&mut buffer)?;

        for i in 0..BUFFER_SIZE {
            let mut float_bytes = [0u8; 4];
            float_bytes.copy_from_slice(&buffer[4 * i..4 * i + 4]);

            j += 1;
            let sample = f32::from_le_bytes(float_bytes);
            let mut value = bass_filter.process(sample);

            if value < 0f32 {
                value = -value;
            }

            let envelope = envelope_filter.process(value);

            if j == 200 {
                let beat = beat_filter.process(envelope);

                let mut state = state.lock().unwrap();

                state.point_buf.push(beat);
                state.last_update = Instant::now();

                j = 0;
            }
        }
    }
}
