//! Scheduler for emitting events at a specific time in the future.

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

use glutin::event::Event as GlutinEvent;

use crate::event::Event as AlacrittyEvent;

type Event = GlutinEvent<'static, AlacrittyEvent>;

/// ID uniquely identifying a timer.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TimerId {
    SelectionScrolling,
    DelayedSearch,
}

/// Event scheduled to be emitted at a specific time.
pub struct Timer {
    pub deadline: Instant,
    pub event: Event,

    interval: Option<Duration>,
    id: TimerId,
}

/// Scheduler tracking all pending timers.
pub struct Scheduler {
    timers: VecDeque<Timer>,
}

impl Default for Scheduler {
    fn default() -> Self {
        Self { timers: VecDeque::new() }
    }
}

impl Scheduler {
    pub fn new() -> Self {
        Self::default()
    }

    /// Process all pending timers.
    ///
    /// If there are still timers pending after all ready events have been processed, the closest
    /// pending deadline will be returned.
    pub fn update(&mut self, event_queue: &mut Vec<Event>) -> Option<Instant> {
        let now = Instant::now();
        while !self.timers.is_empty() && self.timers[0].deadline <= now {
            if let Some(timer) = self.timers.pop_front() {
                // Automatically repeat the event.
                if let Some(interval) = timer.interval {
                    self.schedule(timer.event.clone(), interval, true, timer.id);
                }

                event_queue.push(timer.event);
            }
        }

        self.timers.get(0).map(|timer| timer.deadline)
    }

    /// Schedule a new event.
    pub fn schedule(&mut self, event: Event, interval: Duration, repeat: bool, timer_id: TimerId) {
        let deadline = Instant::now() + interval;

        // Get insert position in the schedule.
        let mut index = self.timers.len();
        loop {
            if index == 0 {
                break;
            }
            index -= 1;

            if self.timers[index].deadline < deadline {
                break;
            }
        }

        // Set the automatic event repeat rate.
        let interval = if repeat { Some(interval) } else { None };

        self.timers.insert(index, Timer { interval, deadline, event, id: timer_id });
    }

    /// Cancel a scheduled event.
    pub fn unschedule(&mut self, id: TimerId) -> Option<Event> {
        let index = self.timers.iter().position(|timer| timer.id == id)?;
        self.timers.remove(index).map(|timer| timer.event)
    }

    /// Check if a timer is already scheduled.
    pub fn scheduled(&mut self, id: TimerId) -> bool {
        self.timers.iter().any(|timer| timer.id == id)
    }

    /// Access a staged event by ID.
    pub fn get_mut(&mut self, id: TimerId) -> Option<&mut Timer> {
        self.timers.iter_mut().find(|timer| timer.id == id)
    }
}
