use crate::{Actor, ActorBehavior, Message, Port};
use anyhow::{Error, Result};
use reflow_actor::{message::EncodableValue, ActorContext};
use reflow_actor_macro::actor;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::sync::Arc;
#[actor(
AnimationEventActor,
inports::<10>(time),
outports::<1>(event, events, metadata),
state(MemoryState),
await_inports(time)
)]
pub async fn animation_event_actor(ctx: ActorContext) -> Result<HashMap<String, Message>, Error> {
let payload = ctx.get_payload();
let config = ctx.get_config_hashmap();
let event_defs = config
.get("events")
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
let duration = config
.get("duration")
.and_then(|v| v.as_f64())
.unwrap_or(1.0);
let do_loop = config.get("loop").and_then(|v| v.as_bool()).unwrap_or(true);
let current_time = match payload.get("time") {
Some(Message::Float(f)) => *f,
Some(Message::Integer(i)) => *i as f64,
_ => 0.0,
};
let t = if do_loop && duration > 0.0 {
current_time % duration
} else {
current_time
};
let pool: HashMap<String, Value> = ctx.get_pool("_evt").into_iter().collect();
let prev_t = pool
.get("prev_time")
.and_then(|v| v.as_f64())
.unwrap_or(-1.0);
ctx.pool_upsert("_evt", "prev_time", json!(t));
let mut fired: Vec<Value> = Vec::new();
for evt in &event_defs {
let evt_time = evt.get("time").and_then(|v| v.as_f64()).unwrap_or(0.0);
let name = evt.get("name").and_then(|v| v.as_str()).unwrap_or("");
let data = evt.get("data").cloned().unwrap_or(json!(null));
let fires = if t >= prev_t {
evt_time > prev_t && evt_time <= t
} else {
evt_time > prev_t || evt_time <= t
};
if fires && prev_t >= 0.0 {
fired.push(json!({
"name": name,
"time": evt_time,
"data": data,
}));
}
}
let mut out = HashMap::new();
if let Some(first) = fired.first() {
out.insert(
"event".to_string(),
Message::object(EncodableValue::from(first.clone())),
);
}
if !fired.is_empty() {
let arr: Vec<EncodableValue> = fired
.iter()
.map(|v| EncodableValue::from(v.clone()))
.collect();
out.insert("events".to_string(), Message::Array(Arc::new(arr)));
}
out.insert(
"metadata".to_string(),
Message::object(EncodableValue::from(json!({
"time": t,
"firedCount": fired.len(),
}))),
);
Ok(out)
}