use mineral_protocol::{PropName, PropValue};
use parking_lot::Mutex;
use rustc_hash::FxHashMap;
use crate::player::PlayerCore;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct TerminalReport {
pub(crate) rows: u16,
pub(crate) cols: u16,
pub(crate) fullscreen: bool,
pub(crate) focused: bool,
}
#[derive(Default)]
pub(crate) struct TerminalStates {
reports: FxHashMap<u64, (u64, TerminalReport)>,
seq: u64,
}
impl TerminalStates {
pub(crate) fn set(&mut self, conn: u64, report: TerminalReport) -> Option<TerminalReport> {
self.seq += 1;
self.reports
.insert(conn, (self.seq, report))
.map(|(_seq, prev)| prev)
}
pub(crate) fn remove(&mut self, conn: u64) {
self.reports.remove(&conn);
}
pub(crate) fn current(&self) -> Option<TerminalReport> {
self.reports
.values()
.max_by_key(|(seq, _report)| *seq)
.map(|(_seq, report)| *report)
}
}
#[derive(Default)]
pub(crate) struct PropsWatch {
last: Mutex<FxHashMap<PropName, PropValue>>,
}
impl PlayerCore {
pub(crate) fn set_terminal_state(&self, conn: u64, report: TerminalReport) -> Option<bool> {
let new_fullscreen = report.fullscreen;
let prev = self.inner.ui_state.lock().set(conn, report);
let toggled = matches!(prev, Some(p) if p.fullscreen != new_fullscreen);
toggled.then_some(new_fullscreen)
}
pub(crate) fn clear_terminal_state(&self, conn: u64) {
self.inner.ui_state.lock().remove(conn);
}
pub(crate) fn check_props(&self) {
let snap = self.inner.audio.snapshot();
let (song, mode, queue_len) = self.with_state(|st| {
(
st.current_song.as_ref().map(|s| s.id.qualified()),
st.play_mode,
st.queue.len(),
)
});
let terminal = self
.inner
.ui_state
.lock()
.current()
.map_or(PropValue::None, |t| {
PropValue::Table(vec![
("rows".to_owned(), PropValue::Int(i64::from(t.rows))),
("cols".to_owned(), PropValue::Int(i64::from(t.cols))),
("fullscreen".to_owned(), PropValue::Bool(t.fullscreen)),
("focused".to_owned(), PropValue::Bool(t.focused)),
])
});
let state = if song.is_none() {
"stopped"
} else if snap.playing {
"playing"
} else {
"paused"
};
let entries = [
(
PropName::PLAYER_SONG,
song.map_or(PropValue::None, PropValue::Str),
),
(PropName::PLAYER_STATE, PropValue::Str(state.to_owned())),
(
PropName::PLAYER_VOLUME,
PropValue::Int(i64::from(snap.volume_pct)),
),
(
PropName::PLAYER_POSITION,
PropValue::Int(saturating_i64(snap.position_ms / 1000)),
),
(
PropName::PLAYER_MODE,
PropValue::Str(mode.script_name().to_owned()),
),
(
PropName::QUEUE_LENGTH,
PropValue::Int(saturating_i64(queue_len.try_into().unwrap_or(u64::MAX))),
),
(PropName::TERMINAL, terminal),
];
let mut last = self.inner.props.last.lock();
for (key, value) in entries {
if last.get(&key) != Some(&value) {
self.inner.notify.property_changed(key, &value);
last.insert(key, value);
}
}
}
}
fn saturating_i64(n: u64) -> i64 {
i64::try_from(n).unwrap_or(i64::MAX)
}