use std::error::Error;
use std::io::{self, Write};
use std::thread;
use std::time::Duration;
use chrono::Local;
use crossterm::{terminal, ExecutableCommand};
use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
use crossterm::event::{self, Event, KeyCode, KeyEventKind};
pub struct Pomodoro{
work_duration: u64,
break_duration: u64,
long_break_duration: u64,
session_until_long: u64,
}
impl Pomodoro{
pub fn new() -> Self{
Self {
work_duration: 25,
break_duration: 5,
long_break_duration: 15,
session_until_long: 4
}
}
pub fn run_timer(&self, minutes: u64, session_type: &str){
let total_seconds = minutes * 60;
println!("\n{} session started at {}",
session_type,
Local::now().format("%H:%M:%S"));
for remaining in (0..=total_seconds).rev(){
let mins = remaining / 60;
let secs = remaining % 60;
print!("\r{} {:02}:{:02}",session_type, mins, secs);
io::stdout().flush().unwrap();
if remaining > 0{
thread::sleep(Duration::from_secs(1));
}
}
print!("\n{} complete!", session_type);
self.play_notification().expect("Failed to play notification :");
}
pub fn play_notification(&self) -> Result<(),Box<dyn Error>>{
let running = Arc::new(AtomicBool::new(true));
println!("\nPress q to stop alarm");
let alarm_handle = {
let running_clone = running.clone();
thread::spawn(move || {
loop{
if !running_clone.load(Ordering::SeqCst) {
break;
}
print!("\x07");
io::stdout().flush().unwrap();
thread::sleep(Duration::from_millis(700));
}
})
};
loop{
if event::poll(Duration::from_millis(100))? {
if let Event::Key(key) = event::read()? {
if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q'){
running.store(false, Ordering::SeqCst);
break;
}
}
}
}
alarm_handle.join().expect("Alarm thread panicked");
println!("Alarm completed!");
Ok(())
}
pub fn run(&self){
let mut session_count = 0;
loop{
self.run_timer(self.work_duration, "Work");
session_count += 1;
if session_count % self.session_until_long == 0{
self.run_timer(self.long_break_duration, "Long Break");
}else{
self.run_timer(self.break_duration, "Break");
}
print!("\n Continue? (y/n): ");
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
if input.trim().to_lowercase() != "y" {
break;
}
io::stdout().execute(terminal::Clear(terminal::ClearType::All)).unwrap();
}
}
}