1use std::borrow::Cow;
2
3use crate::{
4 graph::{Graph, Node, NodeCoor},
5 trip::class::DisplayedStroke,
6};
7use bevy::{ecs::query::QueryData, prelude::*};
8use moonshine_core::prelude::{MapEntities, ReflectMapEntities};
9
10mod fetch_name;
11pub use fetch_name::StationNamePending;
12
13pub struct StationPlugin;
14impl Plugin for StationPlugin {
15 fn build(&self, app: &mut App) {
16 app.add_plugins(fetch_name::FetchNamePlugin)
17 .add_observer(add_new_station);
18 }
19}
20
21pub type StationEntries = PlatformEntries;
27pub type Platforms = Children;
29
30#[derive(Reflect, Component)]
34#[reflect(Component)]
35#[require(Name, Station)]
36pub struct IsExternalStation;
37
38#[derive(Reflect, Component)]
40#[reflect(Component)]
41#[require(Name, Station)]
42pub struct IsDepot;
43
44#[derive(Reflect, Component, Default)]
46#[reflect(Component)]
47#[require(Name, Platform, PlatformEntries, Platforms, DisplayedStroke)]
48pub struct Station;
49
50#[derive(Bundle)]
52pub struct StationBundle {
53 station: Station,
54 name: Name,
55 node: Node,
56}
57
58impl StationBundle {
59 pub fn new(name: Cow<'static, str>, node: Node) -> Self {
60 Self {
61 station: Station,
62 name: Name::new(name),
63 node,
64 }
65 }
66}
67
68#[derive(Default, Reflect, Component, MapEntities)]
70#[reflect(Component, MapEntities)]
71#[relationship_target(relationship = crate::entry::EntryStop)]
72pub struct PlatformEntries(#[entities] Vec<Entity>);
73
74#[derive(Default, Reflect, Component)]
75#[reflect(Component)]
76#[require(Name, PlatformEntries, crate::graph::Node)]
77pub struct Platform;
78
79#[derive(QueryData)]
80pub struct StationQuery {
81 entity: Entity,
82 station: &'static Station,
83 platforms: &'static Platforms,
84 entries: &'static StationEntries,
85 pub name: &'static Name,
86 is_external_station: Option<&'static IsExternalStation>,
87 pub position: &'static crate::graph::Node,
88 stroke: &'static DisplayedStroke,
89}
90
91impl<'w, 's> StationQueryItem<'w, 's> {
92 pub fn is_external_station(&self) -> bool {
94 self.is_external_station.is_some()
95 }
96 pub fn is_movable_by_user(&self) -> bool {
98 !self.is_external_station() && self.platforms.is_empty()
99 }
100 pub fn passing_entries<'a>(
103 &self,
104 platform_entries: &Query<'a, 'a, &PlatformEntries>,
105 ) -> impl Iterator<Item = Entity> {
106 let platform_entries = platform_entries
107 .iter_many(self.platforms)
108 .flat_map(|p| p.iter());
109 self.entries.iter().chain(platform_entries)
110 }
111 pub fn passing_entries_by_platform<'a>(
113 &self,
114 platform_entries: &'a Query<'a, 'a, &PlatformEntries>,
115 ) -> impl Iterator<Item = (Entity, impl Iterator<Item = Entity> + 'a)> + 'a
116 where
117 'w: 'a,
118 {
119 let platform_entries = self.platforms.iter().filter_map(|pe| {
120 let Ok(entries) = platform_entries.get(pe) else {
121 return None;
122 };
123 Some((pe, entries.iter()))
124 });
125 std::iter::once((self.entity, self.entries.iter())).chain(platform_entries)
126 }
127}
128
129#[derive(QueryData)]
130pub struct ParentStationOrStation {
131 entity: Entity,
132 station: AnyOf<(
133 &'static Station,
134 (&'static ChildOf, &'static PlatformEntries),
135 )>,
136}
137
138impl<'w, 'q> ParentStationOrStationItem<'w, 'q> {
139 pub fn parent(&self) -> Entity {
140 match self.station {
141 (Some(_), _) => self.entity,
142 (None, Some((parent, _))) => parent.parent(),
143 (None, None) => unreachable!(),
144 }
145 }
146}
147
148#[derive(QueryData)]
149pub struct PlatformQuery {
150 entity: Entity,
151 pub name: &'static Name,
152 station: AnyOf<(&'static Station, &'static ChildOf)>,
153 pub entries: &'static PlatformEntries,
154}
155
156impl<'w, 'q> PlatformQueryItem<'w, 'q> {
157 pub fn station<'a>(&self, station_q: &'a Query<StationQuery>) -> StationQueryItem<'a, 'a> {
158 match self.station {
159 (Some(_), _) => station_q.get(self.entity).unwrap(),
160 (None, Some(parent)) => station_q.get(parent.parent()).unwrap(),
161 (None, None) => unreachable!(),
162 }
163 }
164}
165
166#[derive(Event)]
167pub struct CreateNewStation {
168 pub name: Option<String>,
169 pub coor: NodeCoor,
170}
171
172fn add_new_station(msg: On<CreateNewStation>, mut commands: Commands, mut graph: ResMut<Graph>) {
173 let entity = if let Some(s) = msg.name.as_ref() {
174 commands
175 .spawn(StationBundle::new(
176 s.clone().into(),
177 Node { coor: msg.coor },
178 ))
179 .id()
180 } else {
181 commands
182 .spawn((
183 StationBundle::new("Name Pending".into(), Node { coor: msg.coor }),
184 StationNamePending::new(msg.coor),
185 ))
186 .id()
187 };
188 graph.add_node(entity);
189}