use std::{collections::VecDeque, rc::Rc};

use crate::prelude::*;

pub struct TriggerArgs<'a, W> {
    pub w: &'a mut W,
    pub t: &'a mut Trigger<W>,
}
pub type TriggerFunc<W> = Rc<dyn Fn(TriggerArgs<W>) -> Result<()>>;

pub struct Trigger<W> {
    // Timer
    pub timer: f32,
    // Delay
    pub delay: f32,
    // Set to true to terminate trigger
    pub done: bool,
    // Repeat
    pub repeat: bool,
    // Trigger Func
    pub func: TriggerFunc<W>,
}

impl<W> Trigger<W> {
    pub fn played(&self) -> f32 {
        self.timer - self.delay
    }

    pub fn with_func<F: Fn(TriggerArgs<W>) -> Result<()> + 'static>(mut self, func: F) -> Self {
        self.func = Rc::new(func);
        self
    }

    pub fn with_delay(mut self, delay: f32) -> Self {
        self.delay = delay;
        self
    }

    pub fn with_repeat(mut self, repeat: bool) -> Self {
        self.repeat = repeat;
        self
    }
}

impl<W> Default for Trigger<W> {
    fn default() -> Self {
        Self {
            timer: 0.0,
            delay: 0.0,
            done: false,
            repeat: false,
            func: Rc::new(|_| Ok(())),
        }
    }
}

#[derive(Default)]
pub struct TriggerManager<W> {
    queue: VecDeque<Trigger<W>>,
}

impl<W> TriggerManager<W> {
    pub fn new() -> Self {
        Self {
            queue: VecDeque::new(),
        }
    }

    pub fn add(&mut self, trigger: Trigger<W>) {
        self.queue.push_back(trigger);
    }

    /// Take the queue
    pub fn take_queue(&mut self) -> VecDeque<Trigger<W>> {
        std::mem::take(&mut self.queue)
    }

    /// Prepend the queue
    pub fn prepend_queue(&mut self, mut queue: VecDeque<Trigger<W>>) {
        if !queue.is_empty() {
            std::mem::swap(&mut self.queue, &mut queue);
            self.queue.extend(queue);
        }
    }

    pub fn update(w: &mut W, tick: f32, mut queue: VecDeque<Trigger<W>>) -> VecDeque<Trigger<W>> {
        let mut remove_triggers = false;

        for t in queue.iter_mut() {
            if t.done {
                continue;
            }
            t.timer += tick;
            if t.timer < t.delay {
                continue;
            }

            // ready
            let func = t.func.clone();
            let args = TriggerArgs { w, t };
            if let Err(err) = func(args) {
                log::error!("Trigger: {err:?}");
                // remove trigger
                t.done = true;
            }
            if !t.repeat {
                t.done = true;
            }

            if t.done {
                remove_triggers = true;
            }
        }

        if remove_triggers {
            queue.retain(|trigger| !trigger.done);
        }

        queue
    }
}
