1use bevy::ecs::query::QueryData;
4use bevy::prelude::*;
5use moonshine_core::prelude::{MapEntities, ReflectMapEntities};
6
7use crate::trip::TripQueryItem;
8use crate::units::time::{Duration, TimetableTime};
9
10pub struct EntryPlugin;
12impl Plugin for EntryPlugin {
13 fn build(&self, app: &mut App) {
14 app.add_observer(update_entry_mode)
15 .add_observer(update_entry_stop);
16 }
17}
18
19#[derive(Reflect, Component)]
21#[reflect(Component)]
22pub struct IsDerivedEntry;
23
24#[derive(Reflect, Default, Debug, Clone, Copy)]
26pub enum TravelMode {
27 At(TimetableTime),
29 For(Duration),
31 #[default]
33 Flexible,
34}
35
36#[derive(Default, Reflect, Component, Clone, Copy)]
38#[reflect(Component)]
39pub struct EntryMode {
40 pub arr: Option<TravelMode>,
43 pub dep: TravelMode,
45}
46
47impl EntryMode {
48 pub fn new_derived() -> Self {
51 Self {
52 arr: None,
53 dep: TravelMode::Flexible,
54 }
55 }
56 pub fn shift_arr(&mut self, d: Duration) {
59 match &mut self.arr {
60 Some(TravelMode::At(t)) => *t += d,
61 Some(TravelMode::For(t)) => *t += d,
62 Some(TravelMode::Flexible) | None => (),
63 }
64 }
65 pub fn shift_dep(&mut self, d: Duration) {
68 match &mut self.dep {
69 TravelMode::At(t) => *t += d,
70 TravelMode::For(t) => *t += d,
71 TravelMode::Flexible => (),
72 }
73 }
74}
75
76#[derive(Reflect, Component, MapEntities, Deref, DerefMut)]
79#[reflect(Component, MapEntities)]
80#[relationship(relationship_target = crate::station::PlatformEntries)]
81#[require(EntryMode)]
82pub struct EntryStop(
83 #[relationship]
84 #[entities]
85 pub Entity,
86);
87
88#[derive(Reflect, Component, Clone, Copy)]
91#[reflect(Component)]
92pub struct EntryEstimate {
93 pub arr: TimetableTime,
95 pub dep: TimetableTime,
97}
98
99impl EntryEstimate {
100 pub fn new(arr: TimetableTime, dep: TimetableTime) -> Self {
102 Self { arr, dep }
103 }
104}
105
106#[derive(Bundle)]
108pub struct EntryBundle {
109 time: EntryMode,
111 stop: EntryStop,
113}
114
115impl EntryBundle {
116 pub fn new(arr: Option<TravelMode>, dep: TravelMode, stop: Entity) -> Self {
118 Self {
119 time: EntryMode { arr, dep },
120 stop: EntryStop(stop),
121 }
122 }
123}
124
125#[derive(Bundle)]
127pub struct DerivedEntryBundle {
128 mode: EntryMode,
130 stop: EntryStop,
132 derived: IsDerivedEntry,
134}
135
136impl DerivedEntryBundle {
137 pub fn new(stop: Entity) -> Self {
139 Self {
140 mode: EntryMode::new_derived(),
141 stop: EntryStop(stop),
142 derived: IsDerivedEntry,
143 }
144 }
145}
146
147#[derive(QueryData)]
152pub struct EntryQuery {
153 pub entity: Entity,
154 pub mode: &'static EntryMode,
155 pub estimate: Option<&'static EntryEstimate>,
156 pub parent_schedule: &'static ChildOf,
157 stop: &'static EntryStop,
158 is_derived: Option<&'static IsDerivedEntry>,
159}
160
161impl<'w, 's> EntryQueryItem<'w, 's> {
162 pub fn is_derived(&self) -> bool {
164 self.is_derived.is_some()
165 }
166 pub fn stop(&self) -> Entity {
168 self.stop.entity()
169 }
170 pub fn stop_duration(&self) -> Option<Duration> {
173 self.estimate.map(|e| e.dep - e.arr)
174 }
175 pub fn travel_duration(
178 &self,
179 parent_it: &TripQueryItem,
180 entry_q: &Query<(&EntryMode, Option<&EntryEstimate>)>,
181 ) -> Option<Duration> {
182 assert_eq!(parent_it.entity, self.parent_schedule.parent());
183 let arr = self.estimate?.arr;
184 let parent_schedule = parent_it.schedule;
185 let idx = parent_schedule
186 .iter()
187 .copied()
188 .position(|e| e == self.entity)?;
189 if idx == 0 {
190 return Some(arr.as_duration());
191 }
192 let prev_dep = entry_q
193 .iter_many(parent_schedule[0..idx].iter().rev())
194 .find(|(mode, _)| match (mode.arr, mode.dep) {
195 (Some(TravelMode::For(_)), _) => true,
196 (Some(TravelMode::At(_)), _) => true,
197 (_, TravelMode::At(_)) => true,
198 _ => false,
199 })?
200 .1?
201 .dep;
202 Some(arr - prev_dep)
203 }
204}
205
206#[derive(Debug, EntityEvent)]
209pub struct ChangeEntryStop {
210 pub entity: Entity,
212 pub stop: Entity,
214}
215
216#[derive(Reflect, Debug, EntityEvent, Clone, Copy)]
219pub struct AdjustEntryMode {
220 pub entity: Entity,
222 pub adj: EntryModeAdjustment,
224}
225
226#[derive(Reflect, Debug, Clone, Copy)]
228pub enum EntryModeAdjustment {
229 SetArrival(Option<TravelMode>),
231 SetDeparture(TravelMode),
233 ShiftArrival(Duration),
236 ShiftDeparture(Duration),
239}
240
241fn update_entry_stop(event: On<ChangeEntryStop>, mut commands: Commands) {
242 commands.entity(event.entity).insert(EntryStop(event.stop));
243}
244
245fn update_entry_mode(event: On<AdjustEntryMode>, mut entry_modes: Query<&mut EntryMode>) {
246 let mut entry_mode = entry_modes
247 .get_mut(event.entity)
248 .expect("Entity does not carry an EntryMode component");
249 *entry_mode = transform_entry_mode(*entry_mode, event.adj);
250}
251
252pub fn transform_entry_mode(mut old: EntryMode, adjustment: EntryModeAdjustment) -> EntryMode {
253 use EntryModeAdjustment::*;
254 match adjustment {
255 SetArrival(m) => {
256 old.arr = m;
257 }
258 SetDeparture(m) => {
259 old.dep = m;
260 }
261 ShiftArrival(d) => {
262 old.shift_arr(d);
263 }
264 ShiftDeparture(d) => {
265 old.shift_dep(d);
266 }
267 }
268 old
269}