use std::collections::VecDeque;
use std::mem;
use std::num::NonZeroUsize;

use crate::Document;

#[derive(Clone, Debug)]
pub struct UndoStack {
    past: VecDeque<Document>,
    future: VecDeque<Document>,
    capacity: NonZeroUsize,
}

impl UndoStack {
    #[must_use]
    pub fn with_capacity(capacity: NonZeroUsize) -> Self {
        Self {
            past: VecDeque::new(),
            future: VecDeque::new(),
            capacity,
        }
    }

    pub fn record(&mut self, previous: Document) {
        self.future.clear();
        if self.past.len() == self.capacity.get() {
            self.past.pop_front();
        }
        self.past.push_back(previous);
    }

    pub fn undo(&mut self, current: &mut Document) -> bool {
        let Some(previous) = self.past.pop_back() else {
            return false;
        };
        let old = mem::replace(current, previous);
        self.future.push_back(old);
        true
    }

    pub fn redo(&mut self, current: &mut Document) -> bool {
        let Some(next) = self.future.pop_back() else {
            return false;
        };
        let old = mem::replace(current, next);
        self.past.push_back(old);
        true
    }

    #[must_use]
    pub fn can_undo(&self) -> bool {
        !self.past.is_empty()
    }

    #[must_use]
    pub fn can_redo(&self) -> bool {
        !self.future.is_empty()
    }

    #[must_use]
    pub fn capacity(&self) -> NonZeroUsize {
        self.capacity
    }

    #[must_use]
    pub fn past_len(&self) -> usize {
        self.past.len()
    }

    #[must_use]
    pub fn future_len(&self) -> usize {
        self.future.len()
    }
}
