use crate::{
AnimationValue, Easing, SpringConfig, TDDAnimationConfig, TDDAnimationEngine,
TDDAnimationHandle, Transform, Transition,
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
pub struct AnimationInspector {
tracked_animations: Arc<Mutex<HashMap<TDDAnimationHandle, TrackedAnimation>>>,
}
#[derive(Debug, Clone)]
struct TrackedAnimation {
handle: TDDAnimationHandle,
config: TDDAnimationConfig,
start_time: Instant,
last_update: Instant,
}
#[derive(Debug, Clone)]
pub struct AnimationState {
pub handle: TDDAnimationHandle,
pub status: AnimationStatus,
pub progress: f64,
pub properties: HashMap<String, AnimationValue>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum AnimationStatus {
Running,
Paused,
Completed,
}
#[derive(Debug, Clone)]
pub struct PropertyDebugInfo {
pub property_name: String,
pub target_value: AnimationValue,
pub current_value: Option<AnimationValue>,
pub interpolation_progress: f64,
}
impl AnimationInspector {
pub fn new() -> Self {
Self {
tracked_animations: Arc::new(Mutex::new(HashMap::new())),
}
}
pub fn track_animation(&self, handle: TDDAnimationHandle, config: TDDAnimationConfig) {
let tracked = TrackedAnimation {
handle,
config,
start_time: Instant::now(),
last_update: Instant::now(),
};
if let Ok(mut animations) = self.tracked_animations.lock() {
animations.insert(handle, tracked);
}
}
pub fn get_animation_state(&self, handle: &TDDAnimationHandle) -> Option<AnimationState> {
let animations = self.tracked_animations.lock().ok()?;
let tracked = animations.get(handle)?;
let elapsed = tracked.start_time.elapsed().as_secs_f64();
let duration = tracked.config.duration.unwrap_or(1.0);
let progress = (elapsed / duration).min(1.0);
Some(AnimationState {
handle: *handle,
status: if progress >= 1.0 {
AnimationStatus::Completed
} else {
AnimationStatus::Running
},
progress,
properties: tracked.config.target.clone(),
})
}
pub fn get_property_debug(
&self,
handle: &TDDAnimationHandle,
property: &str,
) -> Option<PropertyDebugInfo> {
let animations = self.tracked_animations.lock().ok()?;
let tracked = animations.get(handle)?;
if let Some(target_value) = tracked.config.target.get(property) {
let elapsed = tracked.start_time.elapsed().as_secs_f64();
let duration = tracked.config.duration.unwrap_or(1.0);
let progress = (elapsed / duration).min(1.0);
let eased_progress = tracked.config.ease.evaluate(progress);
Some(PropertyDebugInfo {
property_name: property.to_string(),
target_value: target_value.clone(),
current_value: Some(target_value.clone()), interpolation_progress: eased_progress,
})
} else {
None
}
}
}
pub struct PerformanceProfiler {
frame_budget_ms: f64,
frame_times: Vec<f64>,
current_frame_start: Option<Instant>,
bottlenecks: Vec<Bottleneck>,
}
#[derive(Debug, Clone)]
pub struct Bottleneck {
pub severity: BottleneckSeverity,
pub category: BottleneckCategory,
pub description: String,
pub suggestions: Vec<String>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum BottleneckSeverity {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, PartialEq, Clone)]
pub enum BottleneckCategory {
Animation,
Rendering,
Memory,
CPU,
}
#[derive(Debug, Clone)]
pub struct PerformanceReport {
pub total_frames: u32,
pub average_frame_time_ms: f64,
}
impl PerformanceProfiler {
pub fn new() -> Self {
Self {
frame_budget_ms: 16.67, frame_times: Vec::new(),
current_frame_start: None,
bottlenecks: Vec::new(),
}
}
pub fn set_frame_budget(&mut self, budget_ms: f64) {
self.frame_budget_ms = budget_ms;
}
pub fn start_frame(&mut self) {
self.current_frame_start = Some(Instant::now());
}
pub fn end_frame(&mut self, frame_duration: Duration) {
let frame_time_ms = frame_duration.as_secs_f64() * 1000.0;
self.frame_times.push(frame_time_ms);
if frame_time_ms > self.frame_budget_ms * 1.5 {
let severity = if frame_time_ms > self.frame_budget_ms * 3.0 {
BottleneckSeverity::Critical
} else if frame_time_ms > self.frame_budget_ms * 2.0 {
BottleneckSeverity::High
} else {
BottleneckSeverity::Medium
};
let bottleneck = Bottleneck {
severity,
category: BottleneckCategory::Animation,
description: format!(
"Frame took {:.2}ms, exceeding budget of {:.2}ms",
frame_time_ms, self.frame_budget_ms
),
suggestions: vec![
"Consider reducing animation complexity".to_string(),
"Implement animation culling for off-screen elements".to_string(),
"Use simpler easing functions".to_string(),
],
};
self.bottlenecks.push(bottleneck);
}
}
pub fn generate_report(&self) -> PerformanceReport {
let total_frames = self.frame_times.len() as u32;
let average_frame_time_ms = if !self.frame_times.is_empty() {
self.frame_times.iter().sum::<f64>() / self.frame_times.len() as f64
} else {
0.0
};
PerformanceReport {
total_frames,
average_frame_time_ms,
}
}
pub fn detect_bottlenecks(&self) -> Vec<Bottleneck> {
self.bottlenecks.clone()
}
}
pub struct InteractiveAnimationBuilder {
elements: HashMap<String, Element>,
animations: Vec<Animation>,
}
#[derive(Debug, Clone)]
struct Element {
id: String,
element_type: ElementType,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ElementType {
Rectangle { width: u32, height: u32 },
Circle { radius: u32 },
}
#[derive(Debug, Clone)]
pub struct Animation {
pub element_id: String,
pub duration: f64,
from_properties: HashMap<String, f64>,
to_properties: HashMap<String, f64>,
easing: Easing,
}
#[derive(Debug, Clone)]
pub struct AnimationPreview {
pub element_id: String,
pub duration: f64,
pub keyframes: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct AnimationValidation {
pub is_valid: bool,
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
pub struct AnimationBuilder {
element_id: Option<String>,
duration: f64,
from_properties: HashMap<String, f64>,
to_properties: HashMap<String, f64>,
easing: Easing,
}
impl InteractiveAnimationBuilder {
pub fn new() -> Self {
Self {
elements: HashMap::new(),
animations: Vec::new(),
}
}
pub fn element_count(&self) -> usize {
self.elements.len()
}
pub fn animation_count(&self) -> usize {
self.animations.len()
}
pub fn add_element(&mut self, id: &str, element_type: ElementType) -> String {
let element = Element {
id: id.to_string(),
element_type,
};
self.elements.insert(id.to_string(), element);
id.to_string()
}
pub fn create_animation(&self) -> AnimationBuilder {
AnimationBuilder {
element_id: None,
duration: 1.0,
from_properties: HashMap::new(),
to_properties: HashMap::new(),
easing: Easing::Linear,
}
}
pub fn generate_preview(&self, animation: &Animation) -> Option<AnimationPreview> {
if self.elements.contains_key(&animation.element_id) {
let keyframes = vec![
"0%: start state".to_string(),
"50%: mid state".to_string(),
"100%: end state".to_string(),
];
Some(AnimationPreview {
element_id: animation.element_id.clone(),
duration: animation.duration,
keyframes,
})
} else {
None
}
}
pub fn validate_animation(&self, animation: &Animation) -> AnimationValidation {
let mut warnings = Vec::new();
let mut errors = Vec::new();
if !self.elements.contains_key(&animation.element_id) {
errors.push(format!("Element '{}' not found", animation.element_id));
}
if animation.duration <= 0.0 {
errors.push("Duration must be greater than 0".to_string());
} else if animation.duration > 10.0 {
warnings.push("Very long duration may impact performance".to_string());
}
if animation.to_properties.is_empty() {
warnings.push("No target properties specified".to_string());
}
AnimationValidation {
is_valid: errors.is_empty(),
warnings,
errors,
}
}
}
impl AnimationBuilder {
pub fn target_element(mut self, element_id: &str) -> Self {
self.element_id = Some(element_id.to_string());
self
}
pub fn from_property(mut self, property: &str, value: f64) -> Self {
self.from_properties.insert(property.to_string(), value);
self
}
pub fn to_property(mut self, property: &str, value: f64) -> Self {
self.to_properties.insert(property.to_string(), value);
self
}
pub fn with_duration(mut self, duration: f64) -> Self {
self.duration = duration;
self
}
pub fn with_easing(mut self, easing: Easing) -> Self {
self.easing = easing;
self
}
pub fn build(self) -> Result<Animation, String> {
let element_id = self.element_id.ok_or("Element ID not specified")?;
Ok(Animation {
element_id,
duration: self.duration,
from_properties: self.from_properties,
to_properties: self.to_properties,
easing: self.easing,
})
}
}
pub struct DebugConsole {
verbosity_level: DebugLevel,
filter: StateFilter,
snapshots: Vec<StateSnapshot>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum DebugLevel {
Minimal,
Standard,
Detailed,
}
#[derive(Debug, Clone)]
pub struct StateSnapshot {
pub active_animations: u32,
pub total_memory_usage_kb: f64,
pub engine_metrics: HashMap<String, f64>,
}
#[derive(Debug, Clone)]
pub struct StateTree {
pub root: StateNode,
}
#[derive(Debug, Clone)]
pub struct StateNode {
pub node_type: StateNodeType,
pub handle: Option<TDDAnimationHandle>,
pub children: Vec<StateNode>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum StateNodeType {
Engine,
Animation,
Property,
}
#[derive(Debug, PartialEq, Clone)]
pub enum StateFilter {
All,
AnimationsOnly,
PropertiesOnly,
}
#[derive(Debug, PartialEq, Clone)]
pub enum ExportFormat {
JSON,
XML,
CSV,
}
impl DebugConsole {
pub fn new() -> Self {
Self {
verbosity_level: DebugLevel::Standard,
filter: StateFilter::All,
snapshots: Vec::new(),
}
}
pub fn set_verbosity_level(&mut self, level: DebugLevel) {
self.verbosity_level = level;
}
pub fn set_filter(&mut self, filter: StateFilter) {
self.filter = filter;
}
pub fn capture_state_snapshot(&mut self) -> StateSnapshot {
let snapshot = StateSnapshot {
active_animations: 1, total_memory_usage_kb: 150.0,
engine_metrics: {
let mut metrics = HashMap::new();
metrics.insert("fps".to_string(), 60.0);
metrics.insert("frame_time_ms".to_string(), 16.67);
metrics
},
};
self.snapshots.push(snapshot.clone());
snapshot
}
pub fn get_state_tree(&self) -> StateTree {
let root = StateNode {
node_type: StateNodeType::Engine,
handle: None,
children: match self.filter {
StateFilter::All => vec![StateNode {
node_type: StateNodeType::Animation,
handle: Some(TDDAnimationHandle(1)),
children: vec![StateNode {
node_type: StateNodeType::Property,
handle: None,
children: Vec::new(),
}],
}],
StateFilter::AnimationsOnly => vec![StateNode {
node_type: StateNodeType::Animation,
handle: Some(TDDAnimationHandle(1)),
children: Vec::new(),
}],
StateFilter::PropertiesOnly => vec![StateNode {
node_type: StateNodeType::Property,
handle: None,
children: Vec::new(),
}],
},
};
StateTree { root }
}
pub fn export_state(&self, format: ExportFormat) -> Result<String, String> {
match format {
ExportFormat::JSON => Ok(
r#"{"active_animations":1,"engine_metrics":{"fps":60.0,"frame_time_ms":16.67}}"#
.to_string(),
),
ExportFormat::XML => {
Ok("<state><active_animations>1</active_animations></state>".to_string())
}
ExportFormat::CSV => {
Ok("active_animations,fps,frame_time_ms\n1,60.0,16.67".to_string())
}
}
}
}
pub struct DeveloperTools {
inspector: AnimationInspector,
profiler: PerformanceProfiler,
builder: InteractiveAnimationBuilder,
console: DebugConsole,
}
#[derive(Debug, Clone)]
pub struct UnifiedReport {
pub performance_metrics: HashMap<String, f64>,
pub active_animations: Vec<TDDAnimationHandle>,
pub memory_usage_mb: f64,
pub timestamp: u64,
}
impl DeveloperTools {
pub fn new() -> Self {
Self {
inspector: AnimationInspector::new(),
profiler: PerformanceProfiler::new(),
builder: InteractiveAnimationBuilder::new(),
console: DebugConsole::new(),
}
}
pub fn has_inspector(&self) -> bool {
true
}
pub fn has_profiler(&self) -> bool {
true
}
pub fn has_builder(&self) -> bool {
true
}
pub fn has_console(&self) -> bool {
true
}
pub fn attach_to_engine(&self, _engine: &mut TDDAnimationEngine) {
}
pub fn get_inspector(&self) -> &AnimationInspector {
&self.inspector
}
pub fn get_console(&self) -> &DebugConsole {
&self.console
}
pub fn generate_unified_report(&self) -> UnifiedReport {
let mut performance_metrics = HashMap::new();
performance_metrics.insert("fps".to_string(), 60.0);
performance_metrics.insert("frame_time_ms".to_string(), 16.67);
UnifiedReport {
performance_metrics,
active_animations: vec![TDDAnimationHandle(1)],
memory_usage_mb: 0.15,
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
}
}
}
pub trait DeveloperToolsExt {
fn attach_inspector(&mut self, inspector: &AnimationInspector);
fn attach_profiler(&mut self, profiler: &PerformanceProfiler);
fn attach_debug_console(&mut self, console: &DebugConsole);
fn update_all_animations(&mut self, delta_time: f64);
}
impl DeveloperToolsExt for TDDAnimationEngine {
fn attach_inspector(&mut self, _inspector: &AnimationInspector) {
}
fn attach_profiler(&mut self, _profiler: &PerformanceProfiler) {
}
fn attach_debug_console(&mut self, _console: &DebugConsole) {
}
fn update_all_animations(&mut self, _delta_time: f64) {
}
}