{-# LANGUAGE AllowAmbiguousTypes #-}

module Mischief.ECS.World.Remove (remove, removeRel, triggerRemoveEvent) where

import Control.Monad
import Control.Monad.Reader
import Data.Data
import Data.Foldable
import Data.IORef
import Data.Text qualified as T
import GHC.Base (Int (..), eqWord#, isTrue#)
import Mischief.ECS.Archetypes.Graph
import Mischief.ECS.Collectable
import Mischief.ECS.Components
import Mischief.ECS.Components.HooksDef (HookContext (..), HookContextRel (..))
import Mischief.ECS.Components.Spawn
import Mischief.ECS.Entities
import Mischief.ECS.EventDef
import Mischief.ECS.Events
import Mischief.ECS.Log
import Mischief.ECS.World
import Mischief.ECS.World.Change (changeArchetype)
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers
import Mischief.ECS.World.Query.Queryable

newtype ToRemove = ToRemove {ToRemove -> [(ComponentType, Maybe Entity, Maybe Any)]
inner :: [(ComponentType, Maybe Entity, Maybe Any)]} deriving newtype (NonEmpty ToRemove -> ToRemove
ToRemove -> ToRemove -> ToRemove
(ToRemove -> ToRemove -> ToRemove)
-> (NonEmpty ToRemove -> ToRemove)
-> (forall b. Integral b => b -> ToRemove -> ToRemove)
-> Semigroup ToRemove
forall b. Integral b => b -> ToRemove -> ToRemove
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
$c<> :: ToRemove -> ToRemove -> ToRemove
<> :: ToRemove -> ToRemove -> ToRemove
$csconcat :: NonEmpty ToRemove -> ToRemove
sconcat :: NonEmpty ToRemove -> ToRemove
$cstimes :: forall b. Integral b => b -> ToRemove -> ToRemove
stimes :: forall b. Integral b => b -> ToRemove -> ToRemove
Semigroup)

instance (Component c) => EraseIntoStorage (C c) ToRemove where
  erase :: C c -> ToRemove
erase C c
_ = [(ComponentType, Maybe Entity, Maybe Any)] -> ToRemove
ToRemove [(Proxy c -> ComponentType
forall c. Component c => Proxy c -> ComponentType
ComponentType (Proxy c -> ComponentType) -> Proxy c -> ComponentType
forall a b. (a -> b) -> a -> b
$ forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @c, Maybe Entity
forall a. Maybe a
Nothing, Maybe Any
forall a. Maybe a
Nothing)]

instance (Component c) => EraseIntoStorage (R c Entity) ToRemove where
  erase :: R c Entity -> ToRemove
erase (R Entity
e) = [(ComponentType, Maybe Entity, Maybe Any)] -> ToRemove
ToRemove [(Proxy c -> ComponentType
forall c. Component c => Proxy c -> ComponentType
ComponentType (Proxy c -> ComponentType) -> Proxy c -> ComponentType
forall a b. (a -> b) -> a -> b
$ forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @c, Entity -> Maybe Entity
forall a. a -> Maybe a
Just Entity
e, Maybe Any
forall a. Maybe a
Nothing)]

instance (Component c) => EraseIntoStorage (R c Any) ToRemove where
  erase :: R c Any -> ToRemove
erase R c Any
_ = [(ComponentType, Maybe Entity, Maybe Any)] -> ToRemove
ToRemove [(Proxy c -> ComponentType
forall c. Component c => Proxy c -> ComponentType
ComponentType (Proxy c -> ComponentType) -> Proxy c -> ComponentType
forall a b. (a -> b) -> a -> b
$ forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @c, Maybe Entity
forall a. Maybe a
Nothing, Any -> Maybe Any
forall a. a -> Maybe a
Just Any
Any)]

remove :: (Collectable c ToRemove) => c -> Entity -> System ()
remove :: forall c. Collectable c ToRemove => c -> Entity -> System ()
remove c
c Entity
entity = do
  let ToRemove
list :: ToRemove = c -> ToRemove
forall v storage. Collectable v storage => v -> storage
collect c
c
  [(ComponentType, Maybe Entity, Maybe Any)]
-> ((ComponentType, Maybe Entity, Maybe Any) -> System ())
-> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ ToRemove
list.inner (((ComponentType, Maybe Entity, Maybe Any) -> System ())
 -> System ())
-> ((ComponentType, Maybe Entity, Maybe Any) -> System ())
-> System ()
forall a b. (a -> b) -> a -> b
$ \case
    (ComponentType
x, Maybe Entity
Nothing, Maybe Any
Nothing) -> do
      comp <- ComponentType -> System ComponentId
getOrAddComponentId ComponentType
x
      removeFromEntity [comp] entity
    (ComponentType
x, Just Entity
target, Maybe Any
_) -> do
      comp <- Pair -> System ComponentId
getOrAddPairId ((ComponentType, Entity) -> Pair
Pair (ComponentType
x, Entity
target))
      removeFromEntity [comp] entity
    (ComponentType
x, Maybe Entity
_, Just Any
_) -> do
      ComponentType -> Entity -> System ()
removeRelationshipsFromEntity ComponentType
x Entity
entity

removeRel :: forall c. (Component c) => Entity -> Entity -> System ()
removeRel :: forall c. Component c => Entity -> Entity -> System ()
removeRel = forall c. Component c => Entity -> Entity -> System ()
removeRelationshipFromEntity @c

removeRelationshipFromEntity :: forall c. (Component c) => Entity -> Entity -> System ()
removeRelationshipFromEntity :: forall c. Component c => Entity -> Entity -> System ()
removeRelationshipFromEntity Entity
target Entity
entity = do
  componentId <- Pair -> System ComponentId
getOrAddPairId ((ComponentType, Entity) -> Pair
Pair (Proxy c -> ComponentType
forall c. Component c => Proxy c -> ComponentType
ComponentType (Proxy c -> ComponentType) -> Proxy c -> ComponentType
forall a b. (a -> b) -> a -> b
$ forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @c, Entity
target))
  removeFromEntity [componentId] entity

removeRelationshipsFromEntity :: ComponentType -> Entity -> System ()
removeRelationshipsFromEntity :: ComponentType -> Entity -> System ()
removeRelationshipsFromEntity ComponentType
x Entity
entity = do
  world <- System World
forall w (m :: * -> *). MonadSystem w m => m World
unsafeGetWorld
  ids <- liftIO $ findComponentsOfEntity world entity
  (ComponentId (# id, _ #)) <- getOrAddComponentId x
  for_ ids $ \[ComponentId]
ids' -> do
    let ids :: [ComponentId]
ids = (ComponentId -> Bool) -> [ComponentId] -> [ComponentId]
forall a. (a -> Bool) -> [a] -> [a]
filter (\(ComponentId (# Word#
id', Maybe Entity
_ #)) -> Int# -> Bool
isTrue# (Int# -> Bool) -> Int# -> Bool
forall a b. (a -> b) -> a -> b
$ Word# -> Word# -> Int#
eqWord# Word#
id Word#
id') [ComponentId]
ids'
    [ComponentId] -> Entity -> System ()
removeFromEntity [ComponentId]
ids Entity
entity

removeFromEntity :: [ComponentId] -> Entity -> System ()
removeFromEntity :: [ComponentId] -> Entity -> System ()
removeFromEntity [ComponentId]
components Entity
entity = do
  world <- System World
forall w (m :: * -> *). MonadSystem w m => m World
unsafeGetWorld
  pointer <- liftIO $ getPointer entity world.entities

  case pointer of
    Maybe (IORef EntityPointer)
Nothing -> Text -> System ()
forall w (m :: * -> *).
(HasCallStack, MonadSystem w m) =>
Text -> m ()
warn (Text -> System ()) -> Text -> System ()
forall a b. (a -> b) -> a -> b
$ Text
"Removal failed: Entity " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Entity -> Text
forall a. Show a => a -> Text
T.show Entity
entity Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" is not alive."
    Just IORef EntityPointer
pointer -> do
      (EntityPointer (# archetypeId, _ #)) <- IO EntityPointer -> System EntityPointer
forall a. IO a -> System a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO EntityPointer -> System EntityPointer)
-> IO EntityPointer -> System EntityPointer
forall a b. (a -> b) -> a -> b
$ IORef EntityPointer -> IO EntityPointer
forall a. IORef a -> IO a
readIORef IORef EntityPointer
pointer

      (newArchetype, removedComponents) <- getArchetypeOnRemove (ArchetypeId $ I# archetypeId) components
      triggerRemoveEvent removedComponents entity

      void $ changeArchetype entity newArchetype Nothing

triggerRemoveEvent :: [ComponentId] -> Entity -> System ()
triggerRemoveEvent :: [ComponentId] -> Entity -> System ()
triggerRemoveEvent [ComponentId]
components Entity
entity = do
  [ComponentId] -> (ComponentId -> System ()) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [ComponentId]
components ((ComponentId -> System ()) -> System ())
-> (ComponentId -> System ()) -> System ()
forall a b. (a -> b) -> a -> b
$ \(ComponentId (# Word#
id, Maybe Entity
target #)) -> do
    Just t <- Query System ComponentType -> System (Maybe ComponentType)
forall w (m :: * -> *) out.
MonadSystem w m =>
Query m out -> m (Maybe out)
single (Query System ComponentType -> System (Maybe ComponentType))
-> Query System ComponentType -> System (Maybe ComponentType)
forall a b. (a -> b) -> a -> b
$ Entity -> C ComponentType -> Query System ComponentType
forall qd out (m :: * -> *).
Queryable qd out =>
Entity -> qd -> Query m out
mkGet ((# Word#, Word# #) -> Entity
Entity (# Word#
id, Word#
0## #)) (forall a. C a
forall {k} (a :: k). C a
C @ComponentType)
    case target of
      Maybe Entity
Nothing -> ComponentType -> Entity -> System ()
triggerRemoveEventC ComponentType
t Entity
entity
      Just Entity
target -> ComponentType -> Entity -> Entity -> System ()
triggerRemoveEventR ComponentType
t Entity
target Entity
entity

triggerRemoveEventC :: ComponentType -> Entity -> System ()
triggerRemoveEventC :: ComponentType -> Entity -> System ()
triggerRemoveEventC (ComponentType (Proxy c
_ :: Proxy t)) Entity
entity = do
  ErasedEvent -> System ()
runEvent (ErasedEvent -> System ()) -> ErasedEvent -> System ()
forall a b. (a -> b) -> a -> b
$ OnRemove c -> ErasedEvent
forall e. Event e => e -> ErasedEvent
eraseEvent (OnRemove c -> ErasedEvent) -> OnRemove c -> ErasedEvent
forall a b. (a -> b) -> a -> b
$ forall c. Entity -> OnRemove c
forall {k} (c :: k). Entity -> OnRemove c
OnRemove @t Entity
entity

  let context :: HookContext
context = HookContext {Entity
entity :: Entity
entity :: Entity
entity}
  m <- forall c. Component c => System Entity
meta @t
  Just hooks <- single $ mkGet m (M @ComponentRemoveHooks)
  for_ hooks $ \(ComponentRemoveHooks [HookContext -> System ()]
h) -> do
    [HookContext -> System ()]
-> ((HookContext -> System ()) -> System ()) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [HookContext -> System ()]
h (((HookContext -> System ()) -> System ()) -> System ())
-> ((HookContext -> System ()) -> System ()) -> System ()
forall a b. (a -> b) -> a -> b
$ \HookContext -> System ()
h -> HookContext -> System ()
h HookContext
context

triggerRemoveEventR :: ComponentType -> Entity -> Entity -> System ()
triggerRemoveEventR :: ComponentType -> Entity -> Entity -> System ()
triggerRemoveEventR (ComponentType (Proxy c
_ :: Proxy t)) Entity
target Entity
entity = do
  ErasedEvent -> System ()
runEvent (ErasedEvent -> System ()) -> ErasedEvent -> System ()
forall a b. (a -> b) -> a -> b
$ OnRemoveRel c -> ErasedEvent
forall e. Event e => e -> ErasedEvent
eraseEvent (OnRemoveRel c -> ErasedEvent) -> OnRemoveRel c -> ErasedEvent
forall a b. (a -> b) -> a -> b
$ forall c. Entity -> Entity -> OnRemoveRel c
forall {k} (c :: k). Entity -> Entity -> OnRemoveRel c
OnRemoveRel @t Entity
entity Entity
target

  let context :: HookContextRel
context = HookContextRel {Entity
entity :: Entity
entity :: Entity
entity, Entity
target :: Entity
target :: Entity
target}

  m <- forall c. Component c => System Entity
meta @t
  Just hooks <- single $ mkGet m (M @ComponentRemoveHooksRel)
  for_ hooks $ \(ComponentRemoveHooksRel [HookContextRel -> System ()]
h) -> do
    [HookContextRel -> System ()]
-> ((HookContextRel -> System ()) -> System ()) -> System ()
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [HookContextRel -> System ()]
h (((HookContextRel -> System ()) -> System ()) -> System ())
-> ((HookContextRel -> System ()) -> System ()) -> System ()
forall a b. (a -> b) -> a -> b
$ \HookContextRel -> System ()
h -> HookContextRel -> System ()
h HookContextRel
context