1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use bevy_ecs::{
    bundle::Bundle,
    entity::Entity,
    system::{Commands, EntityCommands},
    world::{EntityWorldMut, World},
};
use bevy_hierarchy::{BuildChildren, BuildWorldChildren, ChildBuilder, WorldChildBuilder};

use crate::{IntoSpawnable, Spawnable};

/// A type that can spawn [`Bundle`]s.
pub trait AsSpawner<'t, 'a, 'b> {
    /// Convert to a [`Spawner`].
    fn as_spawner(&'t mut self) -> Spawner<'t, 'a, 'b>;
}

impl<'t> AsSpawner<'t, 't, 't> for World {
    fn as_spawner(&'t mut self) -> Spawner<'t, 't, 't> {
        Spawner::World(self)
    }
}

impl<'t, 'a, 'b> AsSpawner<'t, 'a, 'b> for Commands<'a, 'b> {
    fn as_spawner(&'t mut self) -> Spawner<'t, 'a, 'b> {
        Spawner::Commands(self)
    }
}

impl<'t, 'a> AsSpawner<'t, 'a, 'a> for ChildBuilder<'a> {
    fn as_spawner(&'t mut self) -> Spawner<'t, 'a, 'a> {
        Spawner::ChildBuilder(self)
    }
}

impl<'t, 'a> AsSpawner<'t, 'a, 'a> for WorldChildBuilder<'a> {
    fn as_spawner(&'t mut self) -> Spawner<'t, 'a, 'a> {
        Spawner::WorldChildBuilder(self)
    }
}

/// All types that can spawn [`Bundle`]s.
pub enum Spawner<'t, 'a, 'b> {
    World(&'t mut World),
    Commands(&'t mut Commands<'a, 'b>),
    ChildBuilder(&'t mut ChildBuilder<'a>),
    WorldChildBuilder(&'t mut WorldChildBuilder<'a>),
    Scoped(Box<dyn ScopedSpawner>),
}

/// Mutable reference to an [`Entity`].
pub enum EntityMutSpawner<'a> {
    EntityWorldMut(EntityWorldMut<'a>),
    EntityCommands(EntityCommands<'a>),
    Scoped(Box<dyn ScopedEntityMut>),
}

impl<'t> EntityMutSpawner<'t> {
    #[inline]
    pub fn insert<B: Bundle>(&mut self, bundle: B) {
        match self {
            EntityMutSpawner::EntityWorldMut(x) => {
                x.insert(bundle);
            }
            EntityMutSpawner::EntityCommands(x) => {
                x.insert(bundle);
            }
            EntityMutSpawner::Scoped(x) => {
                let mut once = Some(bundle);
                x.entity_mut_scope(&mut |x| x.insert(once.take().unwrap()));
            }
        }
    }

    pub fn spawn_children(&mut self, f: impl FnOnce(Spawner)) {
        match self {
            EntityMutSpawner::EntityWorldMut(x) => {
                x.with_children(|x| f(Spawner::WorldChildBuilder(x)));
            }
            EntityMutSpawner::EntityCommands(x) => {
                x.with_children(|x| f(Spawner::ChildBuilder(x)));
            }
            EntityMutSpawner::Scoped(x) => {
                let mut once = Some(f);
                x.entity_mut_scope(&mut |x| x.spawn_children(once.take().unwrap()));
            }
        }
    }

    pub fn id(&self) -> Entity {
        match self {
            EntityMutSpawner::EntityWorldMut(x) => x.id(),
            EntityMutSpawner::EntityCommands(x) => x.id(),
            EntityMutSpawner::Scoped(x) => x.id(),
        }
    }
}

impl Spawner<'_, '_, '_> {
    /// Spawn a empty [`Entity`] with a spawner.
    pub fn spawn_empty(&mut self) -> EntityMutSpawner {
        match self {
            Spawner::World(w) => EntityMutSpawner::EntityWorldMut(w.spawn_empty()),
            Spawner::Commands(w) => EntityMutSpawner::EntityCommands(w.spawn_empty()),
            Spawner::ChildBuilder(w) => EntityMutSpawner::EntityCommands(w.spawn_empty()),
            Spawner::WorldChildBuilder(w) => EntityMutSpawner::EntityWorldMut(w.spawn_empty()),
            Spawner::Scoped(w) => w.spawner_scope(&mut |w| w.spawn_empty().id()),
        }
    }

    /// Spawn a [`Bundle`] with a spawner.
    pub fn spawn_bundle<B: Bundle>(&mut self, bundle: B) -> EntityMutSpawner {
        match self {
            Spawner::World(w) => EntityMutSpawner::EntityWorldMut(w.spawn(bundle)),
            Spawner::Commands(w) => EntityMutSpawner::EntityCommands(w.spawn(bundle)),
            Spawner::ChildBuilder(w) => EntityMutSpawner::EntityCommands(w.spawn(bundle)),
            Spawner::WorldChildBuilder(w) => EntityMutSpawner::EntityWorldMut(w.spawn(bundle)),
            Spawner::Scoped(w) => {
                let mut once = Some(bundle);
                w.spawner_scope(&mut move |w| w.spawn(once.take().unwrap()))
            }
        }
    }

    /// Spawn a [`IntoSpawnable`] with a spawner.
    pub fn spawn(&mut self, spawned: impl IntoSpawnable) -> Entity {
        let mut spawned = spawned.into_spawnable();
        let mut entity_mut = spawned.spawn_mut(self);
        entity_mut.spawn_children(|mut spawner| spawned.spawn_children(&mut spawner));
        entity_mut.insert(spawned.into_bundle());
        entity_mut.id()
    }
}

/// A global dynamic spawner.
///
/// This is meant to support `bevy_defer`.
pub trait ScopedSpawner {
    fn spawner_scope(&mut self, f: &mut dyn FnMut(&mut Spawner) -> Entity) -> EntityMutSpawner;
}

/// A global dynamic spawner.
///
/// This is meant to support `bevy_defer`,
pub trait ScopedEntityMut {
    fn id(&self) -> Entity;
    fn entity_mut_scope(&mut self, f: &mut dyn FnMut(&mut EntityMutSpawner));
}