1use std::{
6    any::{Any, TypeId},
7    collections::HashMap,
8};
9
10use magma_ecs::{
11    error::EventError,
12    rayon::iter::{IntoParallelRefIterator, ParallelIterator},
13    systems::{Systems, dispatcher::Dispatcher},
14};
15use module::Module;
16
17pub use magma_ecs;
18pub use magma_ecs::{World, entities, rayon, resources, systems};
19pub use schedule::AppSchedule;
20
21use crate::{
22    error::ScheduleError,
23    schedule::{PostUpdate, PreUpdate, Startup, Update},
24};
25
26pub mod error;
27pub mod module;
29pub mod schedule;
31
32type SystemSlice = &'static [(fn(&World), &'static str, &'static [&'static str])];
33
34pub struct App {
36    pub world: World,
37    runner: fn(App),
38    modules: Vec<TypeId>,
39    systems: HashMap<TypeId, (Systems, Dispatcher)>,
40    event_systems: HashMap<TypeId, (Systems, Dispatcher)>,
41}
42
43impl Default for App {
44    fn default() -> Self {
45        let mut app = Self {
46            world: Default::default(),
47            runner: default_runner,
48            modules: vec![],
49            systems: Default::default(),
50            event_systems: Default::default(),
51        };
52
53        app.register_schedule::<Startup>();
54        app.register_schedule::<PreUpdate>();
55        app.register_schedule::<Update>();
56        app.register_schedule::<PostUpdate>();
57
58        app
59    }
60}
61
62impl App {
63    pub fn new() -> Self {
65        Self::default()
66    }
67
68    pub fn add_module(&mut self, module: impl Module + 'static) {
90        let type_id = module.type_id();
91        if !self.modules.contains(&type_id) {
92            self.modules.push(type_id);
93            module.setup(self);
94        }
95    }
96
97    pub fn register_schedule<S: AppSchedule + 'static>(&mut self) {
99        self.systems.insert(TypeId::of::<S>(), Default::default());
100    }
101
102    pub fn run_schedule<S: AppSchedule + 'static>(&self) -> Result<(), ScheduleError> {
104        self.systems
105            .get(&TypeId::of::<S>())
106            .ok_or(ScheduleError::ScheduleNotRegistered)?
107            .1
108            .dispatch(&self.world);
109        Ok(())
110    }
111
112    pub fn add_systems<S: AppSchedule + 'static>(
134        &mut self,
135        systems: SystemSlice,
136    ) -> Result<(), ScheduleError> {
137        let schedule = self
138            .systems
139            .get_mut(&TypeId::of::<S>())
140            .ok_or(ScheduleError::ScheduleNotRegistered)?;
141        for (run, name, deps) in systems {
142            schedule.0.add(*run, name, deps);
143        }
144
145        schedule.1 = schedule.0.to_owned().build_dispatcher();
146        Ok(())
147    }
148
149    pub fn register_event<E: Any + Send + Sync + Clone>(&mut self) {
150        self.world.register_event::<E>();
151        self.event_systems
152            .insert(TypeId::of::<E>(), (Systems::new(), Dispatcher::default()));
153    }
154
155    pub fn add_event_systems<E: Any + Send + Sync + Clone>(
156        &mut self,
157        systems: SystemSlice,
158    ) -> Result<(), EventError> {
159        let event_systems = self
160            .event_systems
161            .get_mut(&TypeId::of::<E>())
162            .ok_or(EventError::EventNotRegistered)?;
163        for (run, name, deps) in systems {
164            event_systems.0.add(*run, name, deps);
165        }
166        event_systems.1 = event_systems.0.to_owned().build_dispatcher();
167        Ok(())
168    }
169
170    pub fn set_runner(&mut self, runner: fn(App)) {
172        self.runner = runner;
173    }
174
175    pub fn process_events(&self) {
177        let events = self.world.get_pending_events();
178        events.par_iter().for_each(|type_id| {
180            self.event_systems
181                .get(type_id)
182                .unwrap()
183                .1
184                .dispatch(&self.world);
185        });
186
187        self.world.clear_events();
188    }
189
190    pub fn run(self) {
192        (self.runner)(self);
193    }
194}
195
196fn default_runner(app: App) {
197    app.run_schedule::<Startup>().unwrap();
198    loop {
199        app.run_schedule::<PreUpdate>().unwrap();
200        app.run_schedule::<Update>().unwrap();
201        app.run_schedule::<PostUpdate>().unwrap();
202        app.process_events();
203    }
204}