import curses from datetime import datetime, date, timedelta import time from threading import Thread import sys import os from pathlib import Path from alert_clients.alarm_api_client.alarm_api_client import AlarmApiClient #from alarm_api_client.alarm_api_client import AlarmApiClient from ncurses_ui import NcursesUI from alert_logic import AlarmLogic from ncurses_threads import NcursesThreads from alarm_logger import AlarmLogger class AlarmClock: def __init__(self, stdscr): # Initialize logging first self.logger = AlarmLogger() try: self.stdscr = stdscr self.api_client = AlarmApiClient("http://localhost:8000") self.alarm_logic = AlarmLogic(self.api_client) self.ncurses_ui = NcursesUI(stdscr) self.ncurses_threads = NcursesThreads(self.alarm_logic) self.alarms = [] self.running = True self.selected_menu = 0 self.new_alarm_hour = 0 self.new_alarm_minute = 0 self.new_alarm_selected = 0 self.new_alarm_date = None self.new_alarm_weekdays = [] self.weekday_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] self.current_alarm = None self.current_alarm_process = False self.snooze_minutes = 5 self.snooze_until = None # Initialize curses self.stdscr.keypad(1) self.stdscr.timeout(100) self.logger.log_system_error("Alarm Clock Initialized Successfully") except Exception as e: self.logger.log_system_error(f"Initialization Error: {str(e)}", exc_info=True) raise def show_error(self, message): self.logger.log_system_error(message) self.ncurses_ui.show_error(message) def handle_add_alarm_input(self, key): try: if key == 27: # Escape self.selected_menu = 0 self.logger.log_system_error("Add Alarm menu cancelled") return if key == 10: # Enter try: alarm_details = { "hour": self.new_alarm_hour, "minute": self.new_alarm_minute, "date": self.new_alarm_date, "weekdays": self.new_alarm_weekdays if self.new_alarm_weekdays else None } self.alarm_logic.create_alarm( self.new_alarm_hour, self.new_alarm_minute, self.new_alarm_date, self.new_alarm_weekdays if self.new_alarm_weekdays else None ) self.logger.log_alarm_created(alarm_details) self.alarms = self.api_client.get_alarms() self.selected_menu = 0 except Exception as e: self.show_error(f"Failed to create alarm: {str(e)}") return if key == curses.KEY_LEFT: self.new_alarm_selected = (self.new_alarm_selected - 1) % 4 self.logger.log_system_error(f"Selected field changed to {self.new_alarm_selected}") elif key == curses.KEY_RIGHT: self.new_alarm_selected = (self.new_alarm_selected + 1) % 4 self.logger.log_system_error(f"Selected field changed to {self.new_alarm_selected}") elif key == 32: # Space if self.new_alarm_selected == 2: # Date self.new_alarm_date = None self.logger.log_system_error(f"Selected field changed to DATE") elif self.new_alarm_selected == 3: # Weekdays current_day = len(self.new_alarm_weekdays) if current_day < 7: if current_day in self.new_alarm_weekdays: self.new_alarm_weekdays.remove(current_day) else: self.new_alarm_weekdays.append(current_day) self.new_alarm_weekdays.sort() elif key == curses.KEY_UP: if self.new_alarm_selected == 0: self.new_alarm_hour = (self.new_alarm_hour + 1) % 24 elif self.new_alarm_selected == 1: self.new_alarm_minute = (self.new_alarm_minute + 1) % 60 elif self.new_alarm_selected == 2: if not self.new_alarm_date: self.new_alarm_date = date.today() else: self.new_alarm_date += timedelta(days=1) elif key == curses.KEY_DOWN: if self.new_alarm_selected == 0: self.new_alarm_hour = (self.new_alarm_hour - 1) % 24 elif self.new_alarm_selected == 1: self.new_alarm_minute = (self.new_alarm_minute - 1) % 60 elif self.new_alarm_selected == 2 and self.new_alarm_date: self.new_alarm_date -= timedelta(days=1) except Exception as e: self.logger.log_system_error(f"Error in add alarm input: {str(e)}", exc_info=True) def delete_selected_alarm(self): try: if self.alarms: alarm = self.alarms[-1] # Get the last alarm if self.api_client.delete_alarm(alarm["id"]): self.logger.log_system_error(f"Deleted Alarm ID: {alarm['id']}") self.alarm_logic.refresh_alarms() else: self.show_error("Failed to delete alarm") except Exception as e: self.logger.log_system_error(f"Delete alarm error: {str(e)}", exc_info=True) self.show_error(f"Delete error: {str(e)}") def handle_list_alarms_input(self, key): try: if key == 27: # Escape self.selected_menu = 0 self.logger.log_system_error("List Alarms menu cancelled") elif key == ord('d'): self.logger.log_system_error("Attempting to delete last alarm") self.delete_selected_alarm() except Exception as e: self.logger.log_system_error(f"Error in list alarms input: {str(e)}", exc_info=True) def draw_main_clock(self): self.ncurses_ui.draw_main_clock() def draw_add_alarm(self): self.ncurses_ui.draw_add_alarm() def draw_list_alarms(self): self.ncurses_ui.draw_list_alarms() def run(self): try: # Start alarm checking thread self.ncurses_threads.start_threads() self.logger.log_system_error("Alarm checking threads started") while self.running: try: # Clear and redraw the screen self.stdscr.erase() if self.selected_menu == 0: self.draw_main_clock() elif self.selected_menu == 1: self.draw_add_alarm() elif self.selected_menu == 2: self.draw_list_alarms() self.stdscr.refresh() # Handle input with timeout key = self.stdscr.getch() if key != -1: # Key was pressed if self.selected_menu == 0: if key == ord('q'): self.logger.log_system_error("Application quit requested") self.alarm_logic.stop_alarm() break elif key == ord('a'): self.selected_menu = 1 self.logger.log_system_error("Switched to Add Alarm menu") elif key == ord('l'): self.selected_menu = 2 self.logger.log_system_error("Switched to List Alarms menu") elif key == ord('s'): self.logger.log_system_error("Alarm stopped") self.alarm_logic.stop_alarm() elif key == ord('z'): self.logger.log_alarm_snoozed( "current_alarm", self.snooze_minutes ) self.alarm_logic.snooze_alarm() elif self.selected_menu == 1: self.handle_add_alarm_input(key) elif self.selected_menu == 2: self.handle_list_alarms_input(key) except curses.error as e: self.logger.log_system_error(f"Curses error: {str(e)}", exc_info=True) self.running = False raise Exception(f"Curses error: {str(e)}") except Exception as e: self.logger.log_system_error(f"Unhandled error in main run loop: {str(e)}", exc_info=True) raise