Skip to content
use std::time::Instant;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TimerState {
    Idle,
    Running,
    Paused,
    Finished,
}

pub struct Timer {
    pub state: TimerState,
    pub total_secs: u64,
    remaining_secs_at_pause: u64,
    started_at: Option<Instant>,
}

impl Timer {
    pub fn new() -> Self {
        Self {
            state: TimerState::Idle,
            total_secs: 0,
            remaining_secs_at_pause: 0,
            started_at: None,
        }
    }

    pub fn start(&mut self, total_secs: u64) {
        self.total_secs = total_secs;
        self.remaining_secs_at_pause = total_secs;
        self.started_at = Some(Instant::now());
        self.state = TimerState::Running;
    }

    pub fn resume(&mut self) {
        if self.state == TimerState::Paused {
            self.started_at = Some(Instant::now());
            self.state = TimerState::Running;
        }
    }

    pub fn pause(&mut self) {
        if self.state == TimerState::Running {
            self.remaining_secs_at_pause = self.remaining_secs();
            self.started_at = None;
            self.state = TimerState::Paused;
        }
    }

    pub fn stop(&mut self) {
        self.state = TimerState::Idle;
        self.started_at = None;
        self.remaining_secs_at_pause = 0;
        self.total_secs = 0;
    }

    pub fn remaining_secs(&self) -> u64 {
        match self.state {
            TimerState::Idle | TimerState::Finished => 0,
            TimerState::Paused => self.remaining_secs_at_pause,
            TimerState::Running => {
                let elapsed = self.started_at.map_or(0, |s| s.elapsed().as_secs());
                self.remaining_secs_at_pause.saturating_sub(elapsed)
            }
        }
    }

    pub fn tick(&mut self) -> bool {
        if self.state == TimerState::Running && self.remaining_secs() == 0 {
            self.state = TimerState::Finished;
            return true;
        }
        false
    }
}