using Contime.data; using System.Diagnostics; namespace Contime.model { public class TimeTrackerModel { // --- Public State & Data --- public enum AppState { Idle, WorkdayActive, TaskActive, OutOfContext } public AppState CurrentState { get; private set; } = AppState.Idle; public List Tasks { get; private set; } = new List(); public TaskItem SelectedTask { get; private set; } public string OutOfContextReason { get; set; } = ""; public DataAccess DataAccess => _dataAccess; // --- Public Read-only Timer Properties --- public TimeSpan WorkdayTime => _workdayStopwatch.Elapsed; public TimeSpan ActiveTaskTime => _activeTaskStopwatch.Elapsed; public TimeSpan IdleTime => _idleStopwatch.Elapsed; public TimeSpan SelectedTaskTotalTime => SelectedTask?.ElapsedTime ?? TimeSpan.Zero; public TimeSpan DayTotal { get; private set; } = TimeSpan.Zero; public TimeSpan WeekTotal { get; private set; } = TimeSpan.Zero; // --- Events --- public event Action StateChanged; // For high-frequency updates (timers) public event Action MajorStateChanged; // For low-frequency updates (task list) // --- Private Fields --- private readonly DataAccess _dataAccess; private readonly Stopwatch _workdayStopwatch = new Stopwatch(); private readonly Stopwatch _activeTaskStopwatch = new Stopwatch(); private readonly Stopwatch _idleStopwatch = new Stopwatch(); private long _currentWorkdayId; private long _currentTaskTimeId; public TimeTrackerModel(DataAccess dataAccess) { _dataAccess = dataAccess; ReloadAllData(); // Initial load } private void ReloadAllData() { long? selectedTaskId = SelectedTask?.Id; Tasks = _dataAccess.GetAllTasks(); DayTotal = _dataAccess.GetTotalTimeForDate(DateTime.Today); WeekTotal = _dataAccess.GetTotalTimeForCurrentWeek(); if (selectedTaskId.HasValue) { SelectedTask = Tasks.FirstOrDefault(t => t.Id == selectedTaskId.Value); } MajorStateChanged?.Invoke(); } // --- Public Methods (State Changers) --- public void StartStopWorkday() { if (CurrentState == AppState.Idle) { _currentWorkdayId = _dataAccess.GetOrCreateWorkday(DateTime.Today); CurrentState = AppState.WorkdayActive; _workdayStopwatch.Restart(); _idleStopwatch.Restart(); } else { if (CurrentState == AppState.TaskActive) StopTask(); CurrentState = AppState.Idle; _workdayStopwatch.Stop(); _idleStopwatch.Stop(); SelectedTask = null; } ReloadAllData(); } public void SelectTask(long taskId) { if (SelectedTask?.Id == taskId) return; // Prevent re-selection loop SelectedTask = taskId == -1 ? null : Tasks.FirstOrDefault(t => t.Id == taskId); // A selection change is a major state change that affects buttons MajorStateChanged?.Invoke(); } public void StartStopTask() { if (CurrentState == AppState.TaskActive) // Stopping { StopTask(); } else if (CurrentState == AppState.WorkdayActive && SelectedTask != null && SelectedTask.Status == "open") // Starting { _currentTaskTimeId = _dataAccess.StartTaskTime(SelectedTask.Id, _currentWorkdayId); CurrentState = AppState.TaskActive; _activeTaskStopwatch.Restart(); _idleStopwatch.Stop(); _idleStopwatch.Reset(); } ReloadAllData(); } public void SwitchContext() { if (CurrentState == AppState.OutOfContext) // Returning { CurrentState = _currentTaskTimeId > 0 ? AppState.TaskActive : AppState.WorkdayActive; _workdayStopwatch.Start(); if (CurrentState == AppState.TaskActive) _activeTaskStopwatch.Start(); else _idleStopwatch.Start(); } else if (CurrentState != AppState.Idle) // Going { _workdayStopwatch.Stop(); if (CurrentState == AppState.TaskActive) _activeTaskStopwatch.Stop(); if (CurrentState == AppState.WorkdayActive) _idleStopwatch.Stop(); CurrentState = AppState.OutOfContext; } StateChanged?.Invoke(); } public void AddTask(string name) { if (string.IsNullOrWhiteSpace(name)) return; _dataAccess.AddTask(name); ReloadAllData(); } public void ToggleSelectedTaskStatus() { if (SelectedTask == null || CurrentState == AppState.TaskActive) return; string newStatus = SelectedTask.Status == "open" ? "closed" : "open"; _dataAccess.UpdateTaskStatus(SelectedTask.Id, newStatus, newStatus == "closed"); ReloadAllData(); } private void StopTask() { if (!_activeTaskStopwatch.IsRunning) return; _dataAccess.EndTaskTime(_currentTaskTimeId); _currentTaskTimeId = 0; CurrentState = AppState.WorkdayActive; _activeTaskStopwatch.Stop(); _activeTaskStopwatch.Reset(); _idleStopwatch.Restart(); } } }