use std::{
    collections::VecDeque,
    time::{Duration, Instant},
};

use sdl2::{event::Event, keyboard::Keycode, pixels::Color};

use super::OutputLayer;

pub struct TapMetronome {
    max_time: Duration,
    timestamps: VecDeque<Instant>,
    seconds_per_beat: Option<f32>,
    brightness: f32,
    decay: f32,
}

impl TapMetronome {
    pub fn new(max_time: Duration) -> Self {
        Self {
            max_time,
            timestamps: VecDeque::new(),
            seconds_per_beat: None,
            brightness: 0.0,
            decay: 1.0,
        }
    }
}

impl OutputLayer for TapMetronome {
    fn tick(&mut self, _dt: std::time::Duration) {
        let now = Instant::now();
        if let (Some(stamp), Some(spb)) = (self.timestamps.back(), self.seconds_per_beat) {
            let dt = (now - *stamp).as_secs_f32();

            let beat_offset = dt % spb;

            self.brightness = (-1.0 * self.decay * beat_offset).exp()
        } else {
            self.brightness = 0.0;
        }
    }

    fn draw_sdl(&self, canvas: &mut sdl2::render::Canvas<sdl2::video::Window>, _texture_size: u32) {
        let v = (255.0 * self.brightness) as u8;
        canvas.set_draw_color(Color::RGB(v, v, v));
        canvas.clear();
    }

    fn on_sdl_event(&mut self, event: &sdl2::event::Event) {
        match event {
            Event::KeyDown {
                keycode: Some(Keycode::Space),
                ..
            } => {
                let now = Instant::now();
                while let Some(stamp) = self.timestamps.front() {
                    if now - *stamp < self.max_time {
                        break;
                    }
                    self.timestamps.pop_front();
                }
                self.timestamps.push_back(now);

                if self.timestamps.len() >= 2 {
                    let dt = *self.timestamps.back().unwrap() - *self.timestamps.front().unwrap();
                    let spb = dt.as_secs_f32() / (self.timestamps.len() - 1) as f32;

                    println!("Detected BPM: {:.2}", 60.0 / spb);

                    self.seconds_per_beat = Some(spb);
                } else {
                    self.seconds_per_beat = None;
                }
            }

            Event::KeyDown {
                keycode: Some(Keycode::Down),
                ..
            } => {
                self.decay += 0.1;
                println!("Decay: {:.3}", self.decay)
            }

            Event::KeyDown {
                keycode: Some(Keycode::Up),
                ..
            } => {
                self.decay -= 0.1;
                println!("Decay: {:.3}", self.decay)
            }

            _ => {}
        }
    }

    fn update_dmx(&self, output: &mut [crate::fixtures::MovingHead; 4]) {
        for head in output.iter_mut() {
            head.dimmer = 0.2 + 0.8 * self.brightness;
        }
    }
}
