1use crate::backend::load_config;
15use jiff::Zoned;
16use sioul_core::attention::{Attention, Column, Kind, Level, Now, Row, Slots};
17use sioul_core::quiet::{self, Blocks, Mode, Overrides};
18use sioul_core::reminders::Holds;
19use std::path::Path;
20use std::sync::Mutex;
21use std::time::SystemTime;
22
23fn changed(path: &Path) -> Option<SystemTime> {
25 std::fs::metadata(path).and_then(|m| m.modified()).ok()
26}
27
28fn held(now: &Zoned) -> Vec<(i64, i64)> {
31 static KEPT: Mutex<Option<(i64, jiff::civil::Date, Vec<(i64, i64)>)>> = Mutex::new(None);
32 let stamp = now.timestamp().as_second();
33 let mut kept = KEPT.lock().unwrap_or_else(|e| e.into_inner());
34 if let Some((at, day, held)) = kept.as_ref()
35 && *day == now.date()
36 && (0..300).contains(&(stamp - at))
37 {
38 return held.clone();
39 }
40 let midnight = now.date().to_zoned(now.time_zone().clone()).map_or(stamp, |z| z.timestamp().as_second());
41 let held = sioul_core::plan::event_spans(&sioul_core::agenda::occurrences(midnight - 86_400, midnight + 2 * 86_400), 0);
43 *kept = Some((stamp, now.date(), held.clone()));
44 held
45}
46
47pub(crate) fn blocks(now: &Zoned) -> Blocks {
50 type Key = (i64, Option<SystemTime>, Option<SystemTime>);
51 static KEPT: Mutex<Option<(Key, Blocks)>> = Mutex::new(None);
52 let (health, days) = (sioul_core::health::Health::default_path(), sioul_core::needs::Days::default_path());
53 let key = (now.timestamp().as_second() / 60, changed(&health), changed(&days));
54 if let Some((known, blocks)) = KEPT.lock().unwrap_or_else(|e| e.into_inner()).as_ref()
55 && *known == key
56 {
57 return blocks.clone();
58 }
59 let needs = sioul_core::health::Health::load(&health).needs;
60 let blocks = Blocks::of(&needs, &sioul_core::needs::Days::load(&days), &held(now), now);
61 *KEPT.lock().unwrap_or_else(|e| e.into_inner()) = Some((key, blocks.clone()));
62 blocks
63}
64
65pub(crate) fn mode_at(now: &Zoned) -> Mode {
67 let config = load_config();
68 let overrides = Overrides::load(&Overrides::default_path());
69 quiet::mode(&config.week_hours(), &config.time_off, &overrides, &blocks(now), now)
70}
71
72pub(crate) fn mode_now() -> Mode {
73 mode_at(&Zoned::now())
74}
75
76pub(crate) fn slots() -> Slots {
81 static KEPT: Mutex<Option<(Option<SystemTime>, Slots)>> = Mutex::new(None);
82 let path = Slots::default_path();
83 let stamp = changed(&path);
84 let mut kept = KEPT.lock().unwrap_or_else(|e| e.into_inner());
85 if let Some((known, slots)) = kept.as_ref()
86 && *known == stamp
87 && stamp.is_some()
88 {
89 return slots.clone();
90 }
91 let slots = Slots::load(&path);
92 *kept = Some((stamp, slots.clone()));
93 slots
94}
95
96pub(crate) fn in_slot(at: &Zoned) -> bool {
100 slots().at(at)
101}
102
103pub(crate) fn quiet_slot() -> bool {
105 in_slot(&Zoned::now())
106}
107
108pub(crate) fn attention() -> Attention {
110 Attention::of(&load_config())
111}
112
113pub(crate) fn attention_at(mode: &Mode, now: &Zoned) -> Now {
118 let config = load_config();
119 let overrides = Overrides::load(&Overrides::default_path());
120 let mut moment = Now::of(mode).layers(in_slot(now), crate::everywhere::holds_others());
121 moment.nothing = mode.free() && sioul_core::pause::nothing_now(&overrides, &config.free_time);
122 moment.holds.porch_rests = overrides.porch_rests(now.timestamp().as_second());
123 moment
124}
125
126pub(crate) fn attention_now() -> Now {
128 let now = Zoned::now();
129 attention_at(&mode_at(&now), &now)
130}
131
132pub(crate) fn level(kind: Kind) -> Level {
134 attention().level(Row::Own(kind), &attention_now())
135}
136
137pub(crate) fn comes(kind: Kind) -> bool {
139 level(kind) == Level::Now
140}
141
142pub(crate) fn with_layers(mut holds: Holds) -> Holds {
145 holds.now.slot = quiet_slot();
146 holds.now.dnd = crate::everywhere::holds_others();
147 holds
148}
149
150pub(crate) fn doses_silent() -> bool {
154 !comes(Kind::Doses)
155}
156
157pub(crate) fn waking() -> Option<i64> {
160 let mode = mode_now();
161 if mode.paused() {
162 return Some(Zoned::now().timestamp().as_second() + 5 * 60);
163 }
164 let sleeps = mode.sleeps();
165 mode.until.filter(|_| sleeps).map(|until| until.timestamp().as_second())
166}
167
168pub(crate) fn woke_from() -> Option<i64> {
173 let attention = attention();
174 let held_in = |column: Column| attention.cell(Row::Own(Kind::Doses), column) != Level::Now;
175 let now = Zoned::now();
176 let stamp = now.timestamp().as_second();
177 let overrides = Overrides::load(&Overrides::default_path());
178 let paused = overrides.paused_since.zip(overrides.paused_ended).filter(|(since, ended)| held_in(Column::Pause) && ended > since && *ended <= stamp && stamp - ended < 30 * 60).map(|(since, _)| since);
179 if !held_in(Column::Sleep) {
180 return paused;
181 }
182 let blocks = blocks(&now);
183 if blocks.at(stamp, &["sleep", "nap"]).is_some() {
185 return None;
186 }
187 blocks.kept.iter().filter(|k| (k.kind == "sleep" || k.kind == "nap") && k.end <= stamp && stamp - k.end < 30 * 60).map(|k| k.start).chain(paused).min()
188}
189
190pub(crate) fn night_set() -> bool {
192 blocks(&Zoned::now()).night
193}