using Microsoft.Win32; using System; using System.Threading; using System.Windows; namespace Bedtimer { public sealed class OverlayManager : IDisposable { private Config _cfg = null!; private OverlayWindow? _overlay; private Thread? _cfgThread; private string _cycleId = ""; public void Start() { _cfg = Config.LoadOrCreate(); BuildOverlay(); StartConfigListener(); // React to system time changes / DST SystemEvents.TimeChanged += (_, __) => Application.Current.Dispatcher.Invoke(RecomputeSchedule); } private void BuildOverlay() { Application.Current.Dispatcher.Invoke(() => { _overlay ??= new OverlayWindow(); _overlay.Show(); // safe: collapsed until T-60 RecomputeSchedule(); }); } private void StartConfigListener() { _cfgThread = new Thread(() => { using var ev = Signals.SubscribeConfigChanged(); while (true) { ev.WaitOne(); Application.Current.Dispatcher.Invoke(RecomputeSchedule); } }); _cfgThread.IsBackground = true; _cfgThread.Start(); } private void RecomputeSchedule() { _cfg = Config.LoadOrCreate(); var now = DateTime.Now; var bedToday = now.Date.AddMinutes(_cfg.BedMinutes); var bedT = (now <= bedToday) ? bedToday : bedToday.AddDays(1); var warnT = bedT.AddMinutes(-_cfg.WarnLead); var flashT = bedT.AddMinutes(-_cfg.FlashLead); var lockT = bedT.AddMinutes(+_cfg.LockDelay); // one lock per calendar day (next bedtime’s date) _cycleId = bedT.ToString("yyyy-MM-dd"); _overlay!.SetSchedule(_cycleId, warnT, flashT, bedT, lockT); } public void Dispose() { // overlay lives/dies with app; nothing extra } } }