1use bevy::{ecs::query::QueryData, prelude::*};
2use moonshine_core::prelude::{MapEntities, ReflectMapEntities};
3
4use crate::{
5 trip::TripQueryItem,
6 units::time::{Duration, TimetableTime},
7};
8
9pub struct EntryPlugin;
10impl Plugin for EntryPlugin {
11 fn build(&self, app: &mut App) {
12 app.add_observer(update_entry_mode)
13 .add_observer(update_entry_stop);
14 }
15}
16
17#[derive(Reflect, Component)]
19#[reflect(Component)]
20pub struct IsDerivedEntry;
21
22#[derive(Reflect, Default, Debug, Clone, Copy)]
24pub enum TravelMode {
25 At(TimetableTime),
26 For(Duration),
27 #[default]
28 Flexible,
29}
30
31#[derive(Default, Reflect, Component, Clone, Copy)]
33#[reflect(Component)]
34pub struct EntryMode {
35 pub arr: Option<TravelMode>,
36 pub dep: TravelMode,
37}
38
39impl EntryMode {
40 pub fn new_derived() -> Self {
41 Self {
42 arr: None,
43 dep: TravelMode::Flexible,
44 }
45 }
46 pub fn shift_arr(&mut self, d: Duration) {
49 match &mut self.arr {
50 Some(TravelMode::At(t)) => *t += d,
51 Some(TravelMode::For(t)) => *t += d,
52 Some(TravelMode::Flexible) | None => (),
53 }
54 }
55 pub fn shift_dep(&mut self, d: Duration) {
57 match &mut self.dep {
58 TravelMode::At(t) => *t += d,
59 TravelMode::For(t) => *t += d,
60 TravelMode::Flexible => (),
61 }
62 }
63}
64
65#[derive(Reflect, Component, MapEntities, Deref, DerefMut)]
67#[reflect(Component, MapEntities)]
68#[relationship(relationship_target = crate::station::PlatformEntries)]
69#[require(EntryMode)]
70pub struct EntryStop(
71 #[relationship]
72 #[entities]
73 pub Entity,
74);
75
76#[derive(Reflect, Component, Clone, Copy)]
78#[reflect(Component)]
79pub struct EntryEstimate {
80 pub arr: TimetableTime,
81 pub dep: TimetableTime,
82}
83
84impl EntryEstimate {
85 pub fn new(arr: TimetableTime, dep: TimetableTime) -> Self {
86 Self { arr, dep }
87 }
88}
89
90#[derive(Bundle)]
92pub struct EntryBundle {
93 time: EntryMode,
94 stop: EntryStop,
95}
96
97impl EntryBundle {
98 pub fn new(arr: Option<TravelMode>, dep: TravelMode, stop: Entity) -> Self {
99 Self {
100 time: EntryMode { arr, dep },
101 stop: EntryStop(stop),
102 }
103 }
104 pub fn new_derived(stop: Entity) -> Self {
105 Self {
106 time: EntryMode::new_derived(),
107 stop: EntryStop(stop),
108 }
109 }
110}
111
112#[derive(Bundle)]
114pub struct DerivedEntryBundle {
115 mode: EntryMode,
116 stop: EntryStop,
117 derived: IsDerivedEntry,
118}
119
120impl DerivedEntryBundle {
121 pub fn new(stop: Entity) -> Self {
122 Self {
123 mode: EntryMode::new_derived(),
124 stop: EntryStop(stop),
125 derived: IsDerivedEntry,
126 }
127 }
128}
129
130#[derive(QueryData)]
131pub struct EntryQuery {
132 pub entity: Entity,
133 pub mode: &'static EntryMode,
134 pub estimate: Option<&'static EntryEstimate>,
135 pub parent_schedule: &'static ChildOf,
136 stop: &'static EntryStop,
137 is_derived: Option<&'static IsDerivedEntry>,
138}
139
140impl<'w, 's> EntryQueryItem<'w, 's> {
141 pub fn is_derived(&self) -> bool {
142 self.is_derived.is_some()
143 }
144 pub fn is_not_derived(&self) -> bool {
145 self.is_derived.is_none()
146 }
147 pub fn stop(&self) -> Entity {
148 self.stop.entity()
149 }
150 pub fn stop_duration(&self) -> Option<Duration> {
151 self.estimate.map(|e| e.dep - e.arr)
152 }
153 pub fn travel_duration(
154 &self,
155 parent_it: &TripQueryItem,
156 entry_q: &Query<(&EntryMode, Option<&EntryEstimate>)>,
157 ) -> Option<Duration> {
158 assert_eq!(parent_it.entity, self.parent_schedule.parent());
159 let arr = self.estimate?.arr;
160 let parent_schedule = parent_it.schedule;
161 let idx = parent_schedule
162 .iter()
163 .copied()
164 .position(|e| e == self.entity)?;
165 if idx == 0 {
166 return Some(arr.as_duration());
167 }
168 let prev_dep = entry_q
169 .iter_many(parent_schedule[0..idx].iter().rev())
170 .find(|(mode, _)| match (mode.arr, mode.dep) {
171 (Some(TravelMode::For(_)), _) => true,
172 (Some(TravelMode::At(_)), _) => true,
173 (_, TravelMode::At(_)) => true,
174 _ => false,
175 })?
176 .1?
177 .dep;
178 Some(arr - prev_dep)
179 }
180}
181
182#[derive(Debug, EntityEvent)]
185pub struct ChangeEntryStop {
186 pub entity: Entity,
187 pub stop: Entity,
188}
189
190#[derive(Reflect, Debug, EntityEvent, Clone, Copy)]
193pub struct AdjustEntryMode {
194 pub entity: Entity,
195 pub adj: EntryModeAdjustment,
196}
197
198#[derive(Reflect, Debug, Clone, Copy)]
199pub enum EntryModeAdjustment {
200 SetArrival(Option<TravelMode>),
201 SetDeparture(TravelMode),
202 ShiftArrival(Duration),
203 ShiftDeparture(Duration),
204}
205
206fn update_entry_stop(event: On<ChangeEntryStop>, mut commands: Commands) {
207 commands.entity(event.entity).insert(EntryStop(event.stop));
208}
209
210fn update_entry_mode(event: On<AdjustEntryMode>, mut entry_modes: Query<&mut EntryMode>) {
211 let mut entry_mode = entry_modes
212 .get_mut(event.entity)
213 .expect("Entity does not carry an EntryMode component");
214 *entry_mode = transform_entry_mode(*entry_mode, event.adj);
215}
216
217pub fn transform_entry_mode(mut old: EntryMode, adjustment: EntryModeAdjustment) -> EntryMode {
218 use EntryModeAdjustment::*;
219 match adjustment {
220 SetArrival(m) => {
221 old.arr = m;
222 }
223 SetDeparture(m) => {
224 old.dep = m;
225 }
226 ShiftArrival(d) => {
227 old.shift_arr(d);
228 }
229 ShiftDeparture(d) => {
230 old.shift_dep(d);
231 }
232 }
233 old
234}