use elara_core::{DegradationLevel, NodeId, StateId, StateTime};
use crate::{FaceState, PoseState, SceneState};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VisualStateId(pub u64);
impl VisualStateId {
pub fn new(id: u64) -> Self {
Self(id)
}
}
pub const STATE_TYPE_VISUAL: u16 = 0x0020;
pub const STATE_TYPE_LIVESTREAM: u16 = 0x0021;
pub fn visual_state_id(node_id: NodeId) -> StateId {
StateId::from_type_instance(STATE_TYPE_VISUAL, node_id.0)
}
pub fn stream_visual_state_id(stream_id: u64) -> StateId {
StateId::from_type_instance(STATE_TYPE_VISUAL, stream_id)
}
pub fn livestream_state_id(stream_id: u64) -> StateId {
StateId::from_type_instance(STATE_TYPE_LIVESTREAM, stream_id)
}
#[derive(Debug, Clone)]
pub struct VisualState {
pub id: VisualStateId,
pub source: NodeId,
pub timestamp: StateTime,
pub face: Option<FaceState>,
pub pose: Option<PoseState>,
pub scene: Option<SceneState>,
pub degradation: DegradationLevel,
pub is_keyframe: bool,
pub keyframe_ref: Option<VisualStateId>,
pub sequence: u64,
}
impl VisualState {
pub fn keyframe(source: NodeId, timestamp: StateTime, sequence: u64) -> Self {
Self {
id: VisualStateId::new(sequence),
source,
timestamp,
face: None,
pose: None,
scene: None,
degradation: DegradationLevel::L0_FullPerception,
is_keyframe: true,
keyframe_ref: None,
sequence,
}
}
pub fn delta(
source: NodeId,
timestamp: StateTime,
sequence: u64,
keyframe: VisualStateId,
) -> Self {
Self {
id: VisualStateId::new(sequence),
source,
timestamp,
face: None,
pose: None,
scene: None,
degradation: DegradationLevel::L0_FullPerception,
is_keyframe: false,
keyframe_ref: Some(keyframe),
sequence,
}
}
pub fn with_face(mut self, face: FaceState) -> Self {
self.face = Some(face);
self
}
pub fn with_pose(mut self, pose: PoseState) -> Self {
self.pose = Some(pose);
self
}
pub fn with_scene(mut self, scene: SceneState) -> Self {
self.scene = Some(scene);
self
}
pub fn with_degradation(mut self, level: DegradationLevel) -> Self {
self.degradation = level;
self
}
pub fn complexity(&self) -> f32 {
let mut score: f32 = 0.0;
if self.face.is_some() {
score += 0.3;
}
if self.pose.is_some() {
score += 0.3;
}
if self.scene.is_some() {
score += 0.4;
}
if self.is_keyframe {
score *= 1.5; }
score.min(1.0)
}
pub fn degrade(&self, target: DegradationLevel) -> VisualState {
let mut degraded = self.clone();
degraded.degradation = target;
match target {
DegradationLevel::L0_FullPerception => {
}
DegradationLevel::L1_DistortedPerception => {
if let Some(ref mut scene) = degraded.scene {
scene.reduce_detail(0.7);
}
}
DegradationLevel::L2_FragmentedPerception => {
degraded.scene = None;
}
DegradationLevel::L3_SymbolicPresence => {
degraded.scene = None;
degraded.pose = None;
}
DegradationLevel::L4_MinimalPresence => {
degraded.scene = None;
degraded.pose = None;
if let Some(ref mut face) = degraded.face {
face.reduce_to_minimal();
}
}
DegradationLevel::L5_LatentPresence => {
degraded.scene = None;
degraded.pose = None;
degraded.face = degraded.face.map(|f| f.to_latent());
}
}
degraded
}
}
#[derive(Debug, Clone)]
pub struct LivestreamState {
pub broadcaster: NodeId,
pub visual: VisualState,
pub viewer_count: u32,
pub is_live: bool,
pub title: String,
pub started_at: StateTime,
}
impl LivestreamState {
pub fn new(broadcaster: NodeId, title: String, started_at: StateTime) -> Self {
Self {
broadcaster,
visual: VisualState::keyframe(broadcaster, started_at, 0),
viewer_count: 0,
is_live: true,
title,
started_at,
}
}
pub fn update_visual(&mut self, visual: VisualState) -> Result<(), &'static str> {
if visual.source != self.broadcaster {
return Err("Only broadcaster can update visual state");
}
self.visual = visual;
Ok(())
}
pub fn add_viewer(&mut self) {
self.viewer_count += 1;
}
pub fn remove_viewer(&mut self) {
self.viewer_count = self.viewer_count.saturating_sub(1);
}
pub fn end_stream(&mut self) {
self.is_live = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_visual_state_keyframe() {
let node = NodeId::new(1);
let time = StateTime::from_millis(1000);
let state = VisualState::keyframe(node, time, 1);
assert!(state.is_keyframe);
assert!(state.keyframe_ref.is_none());
assert_eq!(state.sequence, 1);
}
#[test]
fn test_visual_state_delta() {
let node = NodeId::new(1);
let time = StateTime::from_millis(1000);
let keyframe_id = VisualStateId::new(1);
let state = VisualState::delta(node, time, 2, keyframe_id);
assert!(!state.is_keyframe);
assert_eq!(state.keyframe_ref, Some(keyframe_id));
assert_eq!(state.sequence, 2);
}
#[test]
fn test_visual_state_degradation() {
let node = NodeId::new(1);
let time = StateTime::from_millis(1000);
let state = VisualState::keyframe(node, time, 1);
let degraded = state.degrade(DegradationLevel::L3_SymbolicPresence);
assert_eq!(degraded.degradation, DegradationLevel::L3_SymbolicPresence);
assert!(degraded.scene.is_none());
assert!(degraded.pose.is_none());
}
#[test]
fn test_livestream_state() {
let broadcaster = NodeId::new(1);
let time = StateTime::from_millis(0);
let mut stream = LivestreamState::new(broadcaster, "Test Stream".to_string(), time);
assert!(stream.is_live);
assert_eq!(stream.viewer_count, 0);
stream.add_viewer();
stream.add_viewer();
assert_eq!(stream.viewer_count, 2);
stream.remove_viewer();
assert_eq!(stream.viewer_count, 1);
stream.end_stream();
assert!(!stream.is_live);
}
}