use std::collections::{BTreeMap, HashMap};
use crate::entity::EntityId;
use crate::world::World;
use super::sweep::{self, SweepDirection, SweepMode};
use super::{DispatchManifest, DispatchStrategy, ElevatorGroup, RankContext, pair_is_useful};
#[derive(serde::Serialize, serde::Deserialize)]
pub struct LookDispatch {
direction: BTreeMap<EntityId, SweepDirection>,
#[serde(skip)]
mode: HashMap<EntityId, SweepMode>,
}
impl LookDispatch {
#[must_use]
pub fn new() -> Self {
Self {
direction: BTreeMap::new(),
mode: HashMap::new(),
}
}
fn direction_for(&self, car: EntityId) -> SweepDirection {
self.direction
.get(&car)
.copied()
.unwrap_or(SweepDirection::Up)
}
fn mode_for(&self, car: EntityId) -> SweepMode {
self.mode.get(&car).copied().unwrap_or(SweepMode::Strict)
}
}
impl Default for LookDispatch {
fn default() -> Self {
Self::new()
}
}
impl DispatchStrategy for LookDispatch {
fn prepare_car(
&mut self,
car: EntityId,
car_position: f64,
group: &ElevatorGroup,
manifest: &DispatchManifest,
world: &World,
) {
let current = self.direction_for(car);
if sweep::strict_demand_ahead(current, car_position, group, manifest, world) {
self.mode.insert(car, SweepMode::Strict);
} else {
self.direction.insert(car, current.reversed());
self.mode.insert(car, SweepMode::Lenient);
}
}
fn rank(&self, ctx: &RankContext<'_>) -> Option<f64> {
if !pair_is_useful(ctx, false) {
return None;
}
sweep::rank(
self.mode_for(ctx.car),
self.direction_for(ctx.car),
ctx.car_position,
ctx.stop_position,
)
}
fn notify_removed(&mut self, elevator: EntityId) {
self.direction.remove(&elevator);
self.mode.remove(&elevator);
}
fn builtin_id(&self) -> Option<super::BuiltinStrategy> {
Some(super::BuiltinStrategy::Look)
}
fn snapshot_config(&self) -> Option<String> {
ron::to_string(self).ok()
}
fn restore_config(&mut self, serialized: &str) -> Result<(), String> {
let restored: Self = ron::from_str(serialized).map_err(|e| e.to_string())?;
*self = restored;
Ok(())
}
}