-- |
-- Module with utility functions for creating
-- and scheduling systems.
module Mischief.ECS.Systems where

import Data.Foldable
import Data.Kind
import GHC.Stack.Types
import Mischief.ECS.App.Schedules
import Mischief.ECS.App.SystemConfig
import Mischief.ECS.App.Systems (ScheduledIn (ScheduledIn), SystemFunction (SystemFunction), removeSystemFromMap, systemEntity)
import Mischief.ECS.Collectable
import Mischief.ECS.Components
import Mischief.ECS.Entities
import Mischief.ECS.Mappable
import Mischief.ECS.Relationships.Order
import Mischief.ECS.World
import Mischief.ECS.World.Insert
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.QueryFilter
import Mischief.ECS.World.Query.Queryable
import Mischief.ECS.World.Remove
import Mischief.ECS.World.Spawn
import Mischief.ECS.World.Spawn qualified as Spawn
import Mischief.ECS.World.Utils

add :: (HasCallStack, Schedule sc, SystemConfig s) => sc -> s -> System ()
add :: forall sc s.
(HasCallStack, Schedule sc, SystemConfig s) =>
sc -> s -> System ()
add sc
schedule s
system = do
  let SystemConfigData {[System ()]
systems :: [System ()]
systems :: SystemConfigData -> [System ()]
systems, [(System (), System ())]
edges :: [(System (), System ())]
edges :: SystemConfigData -> [(System (), System ())]
edges} = s -> SystemConfigData
forall s. SystemConfig s => s -> SystemConfigData
systemConfigData s
system

  [System ()] -> (System () -> System Entity) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [System ()]
systems ((System () -> System Entity) -> System ())
-> (System () -> System Entity) -> System ()
forall a b. (a -> b) -> a -> b
$ \System ()
system -> sc -> System () -> System Entity
forall sch.
(HasCallStack, Schedule sch) =>
sch -> System () -> System Entity
systemEntity sc
schedule System ()
system

  [(System (), System ())]
-> ((System (), System ()) -> System ()) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [(System (), System ())]
edges (((System (), System ()) -> System ()) -> System ())
-> ((System (), System ()) -> System ()) -> System ()
forall a b. (a -> b) -> a -> b
$ \(System ()
s1, System ()
s2) -> do
    id1 <- sc -> System () -> System Entity
forall sch.
(HasCallStack, Schedule sch) =>
sch -> System () -> System Entity
systemEntity sc
schedule System ()
s1
    id2 <- systemEntity schedule s2
    insert (Rel Before id2) id1

remove :: (Schedule sc, Collectable a [System ()]) => sc -> a -> System ()
remove :: forall sc a.
(Schedule sc, Collectable a [System ()]) =>
sc -> a -> System ()
remove sc
schedule a
systems = do
  let [System ()]
y :: [System ()] = a -> [System ()]
forall v storage. Collectable v storage => v -> storage
collect a
systems
  [System ()] -> (System () -> System ()) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [System ()]
y (sc -> System () -> System ()
forall sc. Schedule sc => sc -> System () -> System ()
removeOne sc
schedule)

removeOne :: (Schedule sc) => sc -> System () -> System ()
removeOne :: forall sc. Schedule sc => sc -> System () -> System ()
removeOne sc
schedule System ()
system = do
  s <- sc -> System () -> System Entity
forall sc. Schedule sc => sc -> System () -> System Entity
Mischief.ECS.Systems.get sc
schedule System ()
system
  sch <- scheduleEntity schedule
  removeSystemFromMap (ScheduleId sch) system

  despawn s

  query' E (With (R @Before s))
    >>= traverse_ (removeRel @Before s)

spawn :: System () -> System Entity
spawn :: System () -> System Entity
spawn = SystemFunction -> System Entity
forall b. (HasCallStack, Bundle b) => b -> System Entity
Spawn.spawn (SystemFunction -> System Entity)
-> (System () -> SystemFunction) -> System () -> System Entity
forall b c a. (b -> c) -> (a -> b) -> a -> c
. System () -> SystemFunction
SystemFunction

get :: (Schedule sc) => sc -> System () -> System Entity
get :: forall sc. Schedule sc => sc -> System () -> System Entity
get = sc -> System () -> System Entity
forall sch.
(HasCallStack, Schedule sch) =>
sch -> System () -> System Entity
systemEntity

schedule :: (Schedule sc) => sc -> System () -> System ()
schedule :: forall sc. Schedule sc => sc -> System () -> System ()
schedule sc
sch System ()
s = do
  s' <- sc -> System () -> System Entity
forall sc. Schedule sc => sc -> System () -> System Entity
Mischief.ECS.Systems.get sc
sch System ()
s
  sch' <- scheduleEntity sch

  insert (Rel ScheduledIn sch') s'

unschedule :: (Schedule sc) => sc -> System () -> System ()
unschedule :: forall sc. Schedule sc => sc -> System () -> System ()
unschedule sc
sch System ()
s = do
  s' <- sc -> System () -> System Entity
forall sc. Schedule sc => sc -> System () -> System Entity
Mischief.ECS.Systems.get sc
sch System ()
s
  sch' <- scheduleEntity sch

  removeRel @ScheduledIn sch' s'