{-# LANGUAGE AllowAmbiguousTypes #-}

{- HLINT ignore "Use newtype instead of data" -}
module Mischief.ECS.App.Plugins (Plugin (..), Dependency, dep, addPluginRec) where

import Control.Monad
import Data.Foldable
import Data.Kind
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Typeable
import Mischief.ECS.Components
import Mischief.ECS.Components.Common
import Mischief.ECS.World
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Markers
import Mischief.ECS.World.Query.QueryFilter
import Mischief.ECS.World.Spawn

class (Typeable p) => Plugin (p :: Type) where
  deps :: [Dependency]
  deps = []

  init :: System ()
  init = () -> System ()
forall a. a -> System a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

data Dependency where
  Dependency :: (Plugin p) => Proxy p -> Dependency

instance Eq Dependency where
  (Dependency Proxy p
p) == :: Dependency -> Dependency -> Bool
== (Dependency Proxy p
p') = Proxy p -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy p
p TypeRep -> TypeRep -> Bool
forall a. Eq a => a -> a -> Bool
== Proxy p -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy p
p'

instance Ord Dependency where
  compare :: Dependency -> Dependency -> Ordering
compare (Dependency Proxy p
p) (Dependency Proxy p
p') = TypeRep -> TypeRep -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Proxy p -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy p
p) (Proxy p -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy p
p')

instance Show Dependency where
  show :: Dependency -> String
show (Dependency Proxy p
p) = TypeRep -> String
forall a. Show a => a -> String
show (Proxy p -> TypeRep
forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep Proxy p
p)

dep :: forall p. (Plugin p) => Dependency
dep :: forall p. Plugin p => Dependency
dep = Proxy p -> Dependency
forall p. Plugin p => Proxy p -> Dependency
Dependency (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @p)

data PluginMarker p = PluginMarker deriving (Typeable (PluginMarker p)
[HookRel (PluginMarker p)]
[Hook (PluginMarker p)]
Set DefaultComponentType
IsExclusive (IsExclusiveRel (PluginMarker p))
(Typeable (PluginMarker p),
 IsExclusive (IsExclusiveRel (PluginMarker p))) =>
Set DefaultComponentType
-> [Hook (PluginMarker p)]
-> [Hook (PluginMarker p)]
-> [Hook (PluginMarker p)]
-> [HookRel (PluginMarker p)]
-> [HookRel (PluginMarker p)]
-> [HookRel (PluginMarker p)]
-> Component (PluginMarker p)
forall c.
(Typeable c, IsExclusive (IsExclusiveRel c)) =>
Set DefaultComponentType
-> [Hook c]
-> [Hook c]
-> [Hook c]
-> [HookRel c]
-> [HookRel c]
-> [HookRel c]
-> Component c
forall k (p :: k).
(Typeable p, Typeable k) =>
Typeable (PluginMarker p)
forall k (p :: k).
(Typeable p, Typeable k) =>
[HookRel (PluginMarker p)]
forall k (p :: k).
(Typeable p, Typeable k) =>
[Hook (PluginMarker p)]
forall k (p :: k).
(Typeable p, Typeable k) =>
Set DefaultComponentType
forall k (p :: k).
(Typeable p, Typeable k) =>
IsExclusive (IsExclusiveRel (PluginMarker p))
$crequired :: forall k (p :: k).
(Typeable p, Typeable k) =>
Set DefaultComponentType
required :: Set DefaultComponentType
$conAdd :: forall k (p :: k).
(Typeable p, Typeable k) =>
[Hook (PluginMarker p)]
onAdd :: [Hook (PluginMarker p)]
$conSet :: forall k (p :: k).
(Typeable p, Typeable k) =>
[Hook (PluginMarker p)]
onSet :: [Hook (PluginMarker p)]
$conRemove :: forall k (p :: k).
(Typeable p, Typeable k) =>
[Hook (PluginMarker p)]
onRemove :: [Hook (PluginMarker p)]
$conAddRel :: forall k (p :: k).
(Typeable p, Typeable k) =>
[HookRel (PluginMarker p)]
onAddRel :: [HookRel (PluginMarker p)]
$conSetRel :: forall k (p :: k).
(Typeable p, Typeable k) =>
[HookRel (PluginMarker p)]
onSetRel :: [HookRel (PluginMarker p)]
$conRemoveRel :: forall k (p :: k).
(Typeable p, Typeable k) =>
[HookRel (PluginMarker p)]
onRemoveRel :: [HookRel (PluginMarker p)]
Component)

addPluginRec :: forall p. (Plugin p) => System ()
addPluginRec :: forall p. Plugin p => System ()
addPluginRec = forall p. Plugin p => Set Dependency -> System ()
addPluginRec' @p Set Dependency
forall a. Set a
Set.empty

addPluginRec' :: forall p. (Plugin p) => Set Dependency -> System ()
addPluginRec' :: forall p. Plugin p => Set Dependency -> System ()
addPluginRec' Set Dependency
set = do
  x <- Query System Entity -> System [Entity]
forall w (m :: * -> *) out.
MonadSystem w m =>
Query m out -> m [out]
query (Query System Entity -> System [Entity])
-> Query System Entity -> System [Entity]
forall a b. (a -> b) -> a -> b
$ E -> QueryFilter 'ArchetypeFilter -> Query System Entity
forall qd out (m :: * -> *).
Queryable qd out =>
qd -> QueryFilter 'ArchetypeFilter -> Query m out
mkQuery' E
E (C (PluginMarker p) -> QueryFilter 'ArchetypeFilter
forall a (f :: FilterType).
ToFilterComponent a =>
a -> QueryFilter f
With (forall a. C a
forall {k} (a :: k). C a
C @(PluginMarker p)))

  when (null x) $ do
    when (Set.member (Dependency (Proxy @p)) set) $ error $ "Cyclic Plugin Dependency: " ++ show set

    for_ (Mischief.ECS.App.Plugins.deps @p) $ \(Dependency (Proxy p
_ :: Proxy p')) -> do
      forall p. Plugin p => Set Dependency -> System ()
addPluginRec' @p' (Dependency -> Set Dependency -> Set Dependency
forall a. Ord a => a -> Set a -> Set a
Set.insert (Proxy p -> Dependency
forall p. Plugin p => Proxy p -> Dependency
Dependency (forall t. Proxy t
forall {k} (t :: k). Proxy t
Proxy @p)) Set Dependency
set)

    void $ spawn (Name . show . typeRep $ Proxy @p, PluginMarker @p)
    Mischief.ECS.App.Plugins.init @p