letsFocus 0.0.2

Pomodoro Timer application
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>>{
        // Shared flag to signal the stopping of the notification sound
        let running  = Arc::new(AtomicBool::new(true));

        println!("\nPress q to stop alarm");

        // Spawning a thread for alarm loop
        let alarm_handle = {
            let running_clone = running.clone();
            thread::spawn(move || {
                loop{
                    if !running_clone.load(Ordering::SeqCst) {
                        break;
                    }

                    // Beep once
                    print!("\x07");
                    io::stdout().flush().unwrap();

                    // Pausing between beeps
                    thread::sleep(Duration::from_millis(700));
                }
            })
        };

        // Polling for key events in the main thread which is non-blocking
        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;
                    }
                }
            }
        }



        // Waiting for alarm thread to finish
        alarm_handle.join().expect("Alarm thread panicked");

        println!("Alarm completed!");

        // Continue with other functions
        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();
            
            
        }
    }
}