use crate::core::types::{ChannelState, ChannelType, DeviceCapabilities, DeviceId};
use dashmap::DashMap;
use std::collections::HashSet;
use std::sync::{Arc, RwLock};
#[derive(Debug, Clone)]
pub enum CapabilityChange {
ChannelSupportChanged {
device_id: DeviceId,
channel: ChannelType,
supported: bool,
},
BatteryStateChanged {
device_id: DeviceId,
battery_level: Option<u8>,
is_charging: bool,
},
NetworkTypeChanged {
device_id: DeviceId,
network_type: crate::core::types::NetworkType,
},
CapabilitiesUpdated {
device_id: DeviceId,
new_capabilities: DeviceCapabilities,
},
}
pub type CapabilityChangeHandler = Box<dyn Fn(CapabilityChange) + Send + Sync>;
#[derive(Clone)]
pub struct CapabilityManager {
local_capabilities: Arc<RwLock<DeviceCapabilities>>,
remote_states: Arc<DashMap<DeviceId, DashMap<ChannelType, ChannelState>>>,
remote_caps: Arc<DashMap<DeviceId, DeviceCapabilities>>,
change_handlers: Arc<dashmap::DashMap<String, CapabilityChangeHandler>>,
}
impl CapabilityManager {
pub fn new(local_caps: DeviceCapabilities) -> Self {
Self {
local_capabilities: Arc::new(RwLock::new(local_caps)),
remote_states: Arc::new(DashMap::new()),
remote_caps: Arc::new(DashMap::new()),
change_handlers: Arc::new(dashmap::DashMap::new()),
}
}
pub fn get_local_caps(&self) -> DeviceCapabilities {
self.local_capabilities.read().unwrap().clone()
}
pub fn update_channel_state(
&self,
device: DeviceId,
channel: ChannelType,
state: ChannelState,
) {
let device_entry = self.remote_states.entry(device).or_default();
device_entry.insert(channel, state);
}
pub fn get_channel_state(
&self,
device: &DeviceId,
channel: &ChannelType,
) -> Option<ChannelState> {
self.remote_states
.get(device)
.and_then(|map| map.get(channel).map(|v| v.clone()))
}
pub fn clear_remote_devices(&self) {
crate::utils::remove_keys(
&self.remote_states,
crate::utils::get_all_keys(&self.remote_states),
);
crate::utils::remove_keys(
&self.remote_caps,
crate::utils::get_all_keys(&self.remote_caps),
);
crate::utils::remove_keys(
&self.change_handlers,
crate::utils::get_all_keys(&self.change_handlers),
);
}
pub fn register_remote_device(&self, caps: DeviceCapabilities) {
self.remote_caps.insert(caps.device_id, caps);
}
pub fn get_remote_device(&self, device_id: DeviceId) -> Option<DeviceCapabilities> {
self.remote_caps
.get(&device_id)
.map(|entry| entry.value().clone())
}
pub fn get_all_remote_devices(&self) -> Vec<DeviceId> {
self.remote_caps.iter().map(|r| *r.key()).collect()
}
pub fn watch_capability_changes(&self, handler_id: &str, handler: CapabilityChangeHandler) {
self.change_handlers.insert(handler_id.to_string(), handler);
log::info!("Registered capability change handler: {}", handler_id);
}
pub fn unwatch_capability_changes(&self, handler_id: &str) {
self.change_handlers.remove(handler_id);
log::info!("Removed capability change handler: {}", handler_id);
}
fn notify_capability_change(&self, change: CapabilityChange) {
for entry in self.change_handlers.iter() {
let handler = entry.value();
handler(change.clone());
}
}
pub fn update_local_capabilities(&self, new_capabilities: DeviceCapabilities) {
let current_capabilities = self.local_capabilities.read().unwrap().clone();
let changes = self.detect_capability_changes(¤t_capabilities, &new_capabilities);
*self.local_capabilities.write().unwrap() = new_capabilities.clone();
for change in changes {
self.notify_capability_change(change);
}
}
fn detect_capability_changes(
&self,
old: &DeviceCapabilities,
new: &DeviceCapabilities,
) -> Vec<CapabilityChange> {
let mut changes = Vec::new();
let device_id = new.device_id;
let old_channels: HashSet<_> = old.supported_channels.iter().collect();
let new_channels: HashSet<_> = new.supported_channels.iter().collect();
for &channel in new_channels.difference(&old_channels) {
changes.push(CapabilityChange::ChannelSupportChanged {
device_id,
channel: *channel,
supported: true,
});
}
for &channel in old_channels.difference(&new_channels) {
changes.push(CapabilityChange::ChannelSupportChanged {
device_id,
channel: *channel,
supported: false,
});
}
if old.battery_level != new.battery_level || old.is_charging != new.is_charging {
changes.push(CapabilityChange::BatteryStateChanged {
device_id,
battery_level: new.battery_level,
is_charging: new.is_charging,
});
}
if !changes.is_empty() {
changes.push(CapabilityChange::CapabilitiesUpdated {
device_id,
new_capabilities: new.clone(),
});
}
changes
}
}