module Mischief.ECS.Resources
  ( insertRes,
    res,
    resOrInsert,
    resOrInsertM,
    modifyRes,
    modifyRes',
  )
where

import Data.Maybe
import Mischief.ECS.Components
import Mischief.ECS.Components.Bundle
import Mischief.ECS.Components.Spawn
import Mischief.ECS.World
import Mischief.ECS.World.Insert
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers

-- | Insert a resource into the World. If the resource already exists, its value will be overwritten.
insertRes :: forall r. (Component r, Bundle r) => r -> System ()
insertRes :: forall r. (Component r, Bundle r) => r -> System ()
insertRes r
res = do
  entity <- forall c. Component c => System Entity
meta @r
  insert res entity

-- | Get the value of a resource from the World.
res :: forall c m w. (MonadSystem w m) => (Component c) => m (Maybe c)
res :: forall c (m :: * -> *) w.
(MonadSystem w m, Component c) =>
m (Maybe c)
res = do
  meta <- forall c (m :: * -> *) w.
(Component c, MonadSystem w m) =>
m (Maybe Entity)
tryMeta @c
  case meta of
    Maybe Entity
Nothing -> Maybe c -> m (Maybe c)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe c
forall a. Maybe a
Nothing
    Just Entity
meta -> do
      Query m c -> m (Maybe c)
forall w (m :: * -> *) out.
MonadSystem w m =>
Query m out -> m (Maybe out)
single (Query m c -> m (Maybe c)) -> Query m c -> m (Maybe c)
forall a b. (a -> b) -> a -> b
$ Entity -> C c -> Query m c
forall qd out (m :: * -> *).
Queryable qd out =>
Entity -> qd -> Query m out
mkGet Entity
meta (forall a. C a
forall {k} (a :: k). C a
C @c)

-- | Get the value of a resource, or insert a resource if it doesn't exist.
resOrInsert :: forall r. (Component r, Bundle r) => r -> System r
resOrInsert :: forall r. (Component r, Bundle r) => r -> System r
resOrInsert r
r = do
  meta <- forall c. Component c => System Entity
meta @r
  x <- single $ mkGet meta (C @r)
  case x of
    Maybe r
Nothing -> do
      r -> System ()
forall r. (Component r, Bundle r) => r -> System ()
insertRes r
r
      r -> System r
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (r -> System r) -> r -> System r
forall a b. (a -> b) -> a -> b
$! r
r
    Just r
x -> r -> System r
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (r -> System r) -> r -> System r
forall a b. (a -> b) -> a -> b
$! r
x

-- | Same as 'resOrInsert' but allows the default resource to be produced by a System.
resOrInsertM :: forall r. (Component r, Bundle r) => System r -> System r
resOrInsertM :: forall r. (Component r, Bundle r) => System r -> System r
resOrInsertM System r
r = do
  meta <- forall c. Component c => System Entity
meta @r
  x <- single $ mkGet meta (C @r)
  case x of
    Maybe r
Nothing -> do
      r <- System r
r
      insertRes r
      pure r
    Just r
x -> r -> System r
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure r
x

-- | Modify the value of a resource if it exists, otherwise insert the result of the function.
modifyRes :: forall r. (Component r, Bundle r) => (Maybe r -> r) -> System ()
modifyRes :: forall r. (Component r, Bundle r) => (Maybe r -> r) -> System ()
modifyRes Maybe r -> r
f = do
  x <- forall c (m :: * -> *) w.
(MonadSystem w m, Component c) =>
m (Maybe c)
res @r
  insertRes $ f x

-- | Modify the value of a resource if it exists, otherwise do nothing.
modifyRes' :: forall r. (Component r, Bundle r) => (r -> r) -> System ()
modifyRes' :: forall r. (Component r, Bundle r) => (r -> r) -> System ()
modifyRes' r -> r
f = do
  x <- forall c (m :: * -> *) w.
(MonadSystem w m, Component c) =>
m (Maybe c)
res @r
  case x of
    Maybe r
Nothing -> () -> System ()
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    Just r
x -> r -> System ()
forall r. (Component r, Bundle r) => r -> System ()
insertRes (r -> System ()) -> r -> System ()
forall a b. (a -> b) -> a -> b
$ r -> r
f r
x