use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use crate::link::catch::FilterSet;
use crate::protocol::opcode::{
BTN_COUNT, CLIP_SET_AUTOLOCK, CLIP_SET_LOOP, CLIP_SET_RETAIN, CLIP_SET_RIDE, LOCK_CLS_AXIS,
LOCK_CLS_BTN, LOCK_CLS_MEDIA, LOCK_DIR_AGAINST, LOCK_DIR_BOTH, LOCK_DIR_NEG, LOCK_DIR_POS,
LOCK_DIR_WITH, LOCK_ID_ALL, LOCK_SCALE_BLOCK, LOCK_SCALE_PASS, MAX_BUTTONS,
};
use crate::types::lock::blanket_scope;
use crate::types::{Action, Class, ClipPacketTrigger, ClipSettings, ClipState, ClipStatus, Usage};
pub(crate) type LockKey = (u8, u16, u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) enum Override {
#[default]
None,
Press,
Force,
}
impl Override {
pub(crate) fn as_action(self) -> Option<Action> {
match self {
Override::None => None,
Override::Press => Some(Action::Press),
Override::Force => Some(Action::ForceRelease),
}
}
fn applied(action: Action) -> Override {
match action {
Action::Press => Override::Press,
Action::ForceRelease => Override::Force,
Action::SoftRelease => Override::None,
}
}
}
const SLOT_DIRS: [u8; 4] = [LOCK_DIR_POS, LOCK_DIR_NEG, LOCK_DIR_WITH, LOCK_DIR_AGAINST];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Slots([i16; 4]);
impl Default for Slots {
fn default() -> Slots {
Slots([LOCK_SCALE_PASS; 4])
}
}
impl Slots {
fn write(&mut self, dir: u8, scale: i16) {
if dir == LOCK_DIR_BOTH {
self.0 = [scale, scale, LOCK_SCALE_PASS, LOCK_SCALE_PASS];
} else if let Some(i) = SLOT_DIRS.iter().position(|&d| d == dir) {
self.0[i] = scale;
}
}
fn is_clear(self) -> bool {
self.0.iter().all(|&s| s == LOCK_SCALE_PASS)
}
fn commands(self) -> Vec<(u8, i16)> {
let [p, n, w, a] = self.0;
if p == n && w == LOCK_SCALE_PASS && a == LOCK_SCALE_PASS {
return if p == LOCK_SCALE_PASS {
Vec::new()
} else {
vec![(LOCK_DIR_BOTH, p)]
};
}
(0..4)
.filter(|&i| self.0[i] != LOCK_SCALE_PASS)
.map(|i| (SLOT_DIRS[i], self.0[i]))
.collect()
}
}
#[derive(Debug)]
pub(crate) struct LockUndo {
rows: Vec<((u8, u16), Option<Slots>)>,
media_order: Vec<u16>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct StoredRewrite {
pub(crate) class: u8,
pub(crate) id: u16,
pub(crate) direction: u8,
pub(crate) action: u8,
pub(crate) offset: u16,
pub(crate) match_bytes: Vec<u8>,
pub(crate) mask: Vec<u8>,
pub(crate) payload: Vec<u8>,
}
pub(crate) type RewriteWireKey = (u8, u16, u8, Vec<u8>, Vec<u8>);
impl StoredRewrite {
pub(crate) fn key(&self) -> RewriteWireKey {
(
self.class,
self.id,
self.direction,
self.match_bytes.clone(),
self.mask.clone(),
)
}
}
#[derive(Debug)]
pub(crate) struct RewriteUndo {
key: RewriteWireKey,
prior: Option<StoredRewrite>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct StoredTransform {
pub(crate) op: u8,
pub(crate) sclass: u8,
pub(crate) sid: u16,
pub(crate) dclass: u8,
pub(crate) did: u16,
}
pub(crate) type TransformWireKey = (u8, u16, u8, u16);
impl StoredTransform {
pub(crate) fn key(&self) -> TransformWireKey {
(self.sclass, self.sid, self.dclass, self.did)
}
}
#[derive(Debug)]
pub(crate) struct TransformUndo {
key: TransformWireKey,
prior: Option<(usize, StoredTransform)>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct DesiredState {
overrides: BTreeMap<(u8, u16), Override>, locks: BTreeMap<(u8, u16), Slots>,
media_order: Vec<u16>,
catch: FilterSet,
rewrites: BTreeMap<RewriteWireKey, StoredRewrite>,
transforms: Vec<StoredTransform>,
declared_buttons: Option<u8>,
clip: ClipHeld,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
struct ClipHeld {
loaded: bool,
settings: [u8; 4], triggers: BTreeSet<(u8, u16, u8)>,
packet_triggers: BTreeMap<ClipPacketKey, bool>, }
pub(crate) type ClipPacketKey = (u8, u16, u8, Vec<u8>, Vec<u8>);
pub(crate) fn clip_packet_key(t: &ClipPacketTrigger) -> ClipPacketKey {
(
t.class.as_u8(),
t.id,
t.direction.as_u8(),
t.match_bytes.clone(),
t.mask.clone(),
)
}
impl DesiredState {
pub(crate) fn clip_loaded(&mut self, loaded: bool) {
self.clip.loaded = loaded;
}
pub(crate) fn clip_setting(&mut self, id: u8, value: u8) {
if let Some(s) = self.clip.settings.get_mut(id as usize) {
*s = value;
}
}
pub(crate) fn clip_trigger(&mut self, key: (u8, u16, u8), present: bool) {
if present {
self.clip.triggers.insert(key);
} else {
self.clip.triggers.remove(&key);
}
}
pub(crate) fn clip_packet_bind(&mut self, key: ClipPacketKey, consume: bool) {
self.clip.packet_triggers.insert(key, consume);
}
pub(crate) fn clip_packet_unbind(&mut self, key: &ClipPacketKey) {
self.clip.packet_triggers.remove(key);
}
pub(crate) fn clip_packet_drop_consuming(&mut self) -> Vec<ClipPacketKey> {
let dropped: Vec<ClipPacketKey> = self
.clip
.packet_triggers
.iter()
.filter(|&(_, &consume)| consume)
.map(|(k, _)| k.clone())
.collect();
for k in &dropped {
self.clip.packet_triggers.remove(k);
}
dropped
}
pub(crate) fn clip_triggers_clear(&mut self) {
self.clip.triggers.clear();
self.clip.packet_triggers.clear();
}
pub(crate) fn clip_adopt(&mut self, status: &ClipStatus, settings: &ClipSettings) {
let mut scalars = [0u8; 4];
scalars[CLIP_SET_AUTOLOCK as usize] = blanket_scope(&settings.autolock);
scalars[CLIP_SET_LOOP as usize] = settings.loop_ as u8;
scalars[CLIP_SET_RETAIN as usize] = settings.retain as u8;
scalars[CLIP_SET_RIDE as usize] = settings.ride as u8;
self.clip = ClipHeld {
loaded: status.total != 0 || status.state != ClipState::Idle,
settings: scalars,
triggers: settings
.triggers
.iter()
.map(|t| {
let (class, id) = t.on.class_id();
(class, id, t.edge.as_u8())
})
.collect(),
packet_triggers: settings
.packet_triggers
.iter()
.map(|e| (clip_packet_key(&e.trigger), e.trigger.consume))
.collect(),
};
}
pub(crate) fn apply(&mut self, usage: Usage, action: Action) {
let key = usage.class_id();
match Override::applied(action) {
Override::None => {
self.overrides.remove(&key);
}
ov => {
self.overrides.insert(key, ov);
}
}
}
pub(crate) fn apply_lock(&mut self, key: LockKey, scale: i16) -> LockUndo {
let (class, id, dir) = key;
let scale = if class == LOCK_CLS_AXIS {
scale
} else if scale < LOCK_SCALE_PASS {
LOCK_SCALE_BLOCK
} else {
LOCK_SCALE_PASS
};
let mut undo = LockUndo {
rows: Vec::new(),
media_order: self.media_order.clone(),
};
if class == LOCK_CLS_BTN && id == LOCK_ID_ALL {
self.clear_button_rows(&mut undo);
self.write_lock_row(class, id, dir, scale, &mut undo);
} else if class == LOCK_CLS_BTN && self.locks.contains_key(&(LOCK_CLS_BTN, LOCK_ID_ALL)) {
self.burst_button_blanket(&mut undo);
self.write_lock_row(class, id, dir, scale, &mut undo);
} else {
self.write_lock_row(class, id, dir, scale, &mut undo);
}
undo
}
fn write_lock_row(&mut self, class: u8, id: u16, dir: u8, scale: i16, undo: &mut LockUndo) {
let key = (class, id);
undo.rows.push((key, self.locks.get(&key).copied()));
let row = self.locks.entry(key).or_default();
row.write(dir, scale);
if row.is_clear() {
self.locks.remove(&key);
if is_media_slot(class, id) {
self.media_order.retain(|&m| m != id);
}
} else if is_media_slot(class, id) && !self.media_order.contains(&id) {
self.media_order.push(id);
}
}
fn clear_button_rows(&mut self, undo: &mut LockUndo) {
let ids: Vec<u16> = self
.locks
.keys()
.filter(|(class, id)| *class == LOCK_CLS_BTN && *id != LOCK_ID_ALL)
.map(|&(_, id)| id)
.collect();
for id in ids {
let key = (LOCK_CLS_BTN, id);
undo.rows.push((key, self.locks.get(&key).copied()));
self.locks.remove(&key);
}
}
fn burst_button_blanket(&mut self, undo: &mut LockUndo) {
let key = (LOCK_CLS_BTN, LOCK_ID_ALL);
let Some(blanket) = self.locks.get(&key).copied() else {
return;
};
for b in 0..self.button_count() {
let row = (LOCK_CLS_BTN, b);
if let Entry::Vacant(slot) = self.locks.entry(row) {
undo.rows.push((row, None));
slot.insert(blanket);
}
}
undo.rows.push((key, Some(blanket)));
self.locks.remove(&key);
}
pub(crate) fn note_declared_buttons(&mut self, n_buttons: u8) {
self.declared_buttons = Some(n_buttons.min(MAX_BUTTONS));
}
fn button_count(&self) -> u16 {
self.declared_buttons.unwrap_or(BTN_COUNT) as u16
}
pub(crate) fn restore_lock(&mut self, undo: LockUndo) {
for (key, row) in undo.rows.into_iter().rev() {
match row {
Some(row) => self.locks.insert(key, row),
None => self.locks.remove(&key),
};
}
self.media_order = undo.media_order;
}
pub(crate) fn apply_rewrite(&mut self, rule: StoredRewrite) -> RewriteUndo {
let key = rule.key();
let prior = self.rewrites.insert(key.clone(), rule);
RewriteUndo { key, prior }
}
pub(crate) fn remove_rewrite(&mut self, key: RewriteWireKey) -> RewriteUndo {
let prior = self.rewrites.remove(&key);
RewriteUndo { key, prior }
}
pub(crate) fn restore_rewrite(&mut self, undo: RewriteUndo) {
match undo.prior {
Some(rule) => {
self.rewrites.insert(undo.key, rule);
}
None => {
self.rewrites.remove(&undo.key);
}
}
}
pub(crate) fn clear_rewrites(&mut self) {
self.rewrites.clear();
}
pub(crate) fn held_rewrites(&self) -> Vec<StoredRewrite> {
self.rewrites.values().cloned().collect()
}
pub(crate) fn holds_rewrite(&self, key: &RewriteWireKey) -> bool {
self.rewrites.contains_key(key)
}
pub(crate) fn rewrite_count(&self) -> usize {
self.rewrites.len()
}
pub(crate) fn apply_transform(&mut self, entry: StoredTransform) -> TransformUndo {
let key = entry.key();
match self.transforms.iter().position(|e| e.key() == key) {
Some(i) => {
let prior = core::mem::replace(&mut self.transforms[i], entry);
TransformUndo {
key,
prior: Some((i, prior)),
}
}
None => {
self.transforms.push(entry);
TransformUndo { key, prior: None }
}
}
}
pub(crate) fn remove_transform(&mut self, key: TransformWireKey) -> TransformUndo {
match self.transforms.iter().position(|e| e.key() == key) {
Some(i) => TransformUndo {
key,
prior: Some((i, self.transforms.remove(i))),
},
None => TransformUndo { key, prior: None },
}
}
pub(crate) fn restore_transform(&mut self, undo: TransformUndo) {
match undo.prior {
Some((i, entry)) => {
if let Some(cur) = self.transforms.iter().position(|e| e.key() == undo.key) {
self.transforms[cur] = entry;
} else {
let at = i.min(self.transforms.len());
self.transforms.insert(at, entry);
}
}
None => self.transforms.retain(|e| e.key() != undo.key),
}
}
pub(crate) fn clear_transforms(&mut self) {
self.transforms.clear();
}
pub(crate) fn holds_transform(&self, key: TransformWireKey) -> bool {
self.transforms.iter().any(|e| e.key() == key)
}
pub(crate) fn transform_count(&self) -> usize {
self.transforms.len()
}
pub(crate) fn held_transforms(&self) -> Vec<StoredTransform> {
self.transforms.clone()
}
pub(crate) fn clear(&mut self) {
self.overrides.clear();
self.locks.clear();
self.media_order.clear();
self.rewrites.clear();
self.transforms.clear();
self.clip = ClipHeld::default();
}
pub(crate) fn set_catch(&mut self, filters: FilterSet) {
self.catch = filters;
}
pub(crate) fn catch(&self) -> FilterSet {
self.catch.clone()
}
pub(crate) fn is_idle(&self) -> bool {
self.catch.is_empty()
&& self.overrides.is_empty()
&& self.locks.is_empty()
&& self.rewrites.is_empty()
&& self.transforms.is_empty()
&& !self.clip.loaded
&& self.clip.settings == [0; 4]
&& self.clip.triggers.is_empty()
&& self.clip.packet_triggers.is_empty()
}
pub(crate) fn held(&self) -> impl Iterator<Item = (Usage, Action)> + '_ {
self.overrides.iter().filter_map(|(&(cls, id), ov)| {
let action = ov.as_action()?;
let class = Class::from_u8(cls)?;
Some((Usage::new(class, id), action))
})
}
pub(crate) fn held_locks(&self) -> Vec<(LockKey, i16)> {
let button_count = self.button_count();
let mut rows: Vec<(&(u8, u16), &Slots)> = self.locks.iter().collect();
rows.sort_by_key(|((class, id), _)| (*class, self.media_rank(*class, *id), *id));
rows.into_iter()
.flat_map(|(&(class, id), row)| {
let commands = row.commands();
let ids: Vec<u16> = if class == LOCK_CLS_BTN && id == LOCK_ID_ALL {
(0..button_count).collect()
} else {
vec![id]
};
ids.into_iter().flat_map(move |id| {
commands
.clone()
.into_iter()
.map(move |(dir, scale)| ((class, id, dir), scale))
})
})
.collect()
}
fn media_rank(&self, class: u8, id: u16) -> usize {
if class != LOCK_CLS_MEDIA {
return 0;
}
self.media_order
.iter()
.position(|&m| m == id)
.unwrap_or(usize::MAX)
}
}
fn is_media_slot(class: u8, id: u16) -> bool {
class == LOCK_CLS_MEDIA && id != LOCK_ID_ALL
}