{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}

-- |
-- Module      : Aztecs.ECS.Query.Dynamic.Reader
-- Copyright   : (c) Matt Hunzinger, 2025
-- License     : BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  : matt@hunzinger.me
-- Stability   : provisional
-- Portability : non-portable (GHC extensions)
module Aztecs.ECS.Query.Dynamic.Reader
  ( -- * Dynamic queries
    DynamicQueryReader (..),
    DynamicQueryReaderF (..),

    -- ** Running
    allDyn,
    filterDyn,
    singleDyn,
    singleMaybeDyn,

    -- * Dynamic query filters
    DynamicQueryFilter (..),
  )
where

import Aztecs.ECS.Component
import Aztecs.ECS.Query.Dynamic.Reader.Class
import Aztecs.ECS.World.Archetype (Archetype)
import qualified Aztecs.ECS.World.Archetype as A
import Aztecs.ECS.World.Archetypes (Node)
import qualified Aztecs.ECS.World.Archetypes as AS
import Aztecs.ECS.World.Entities (Entities (..))
import qualified Data.Map as Map
import Data.Set (Set)
import qualified Data.Set as Set
import GHC.Stack

-- | Dynamic query reader.
--
-- @since 0.10
newtype DynamicQueryReader a = DynamicQueryReader
  { -- | Run a dynamic query reader.
    --
    -- @since 0.10
    forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader :: Archetype -> [a]
  }
  deriving ((forall a b.
 (a -> b) -> DynamicQueryReader a -> DynamicQueryReader b)
-> (forall a b. a -> DynamicQueryReader b -> DynamicQueryReader a)
-> Functor DynamicQueryReader
forall a b. a -> DynamicQueryReader b -> DynamicQueryReader a
forall a b.
(a -> b) -> DynamicQueryReader a -> DynamicQueryReader b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
$cfmap :: forall a b.
(a -> b) -> DynamicQueryReader a -> DynamicQueryReader b
fmap :: forall a b.
(a -> b) -> DynamicQueryReader a -> DynamicQueryReader b
$c<$ :: forall a b. a -> DynamicQueryReader b -> DynamicQueryReader a
<$ :: forall a b. a -> DynamicQueryReader b -> DynamicQueryReader a
Functor)

-- | @since 0.10
instance Applicative DynamicQueryReader where
  {-# INLINE pure #-}
  pure :: forall a. a -> DynamicQueryReader a
pure a
a = (Archetype -> [a]) -> DynamicQueryReader a
forall a. (Archetype -> [a]) -> DynamicQueryReader a
DynamicQueryReader ((Archetype -> [a]) -> DynamicQueryReader a)
-> (Archetype -> [a]) -> DynamicQueryReader a
forall a b. (a -> b) -> a -> b
$ \Archetype
arch -> Int -> a -> [a]
forall a. Int -> a -> [a]
replicate (Set EntityID -> Int
forall a. Set a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Set EntityID -> Int) -> Set EntityID -> Int
forall a b. (a -> b) -> a -> b
$ Archetype -> Set EntityID
A.entities Archetype
arch) a
a

  {-# INLINE (<*>) #-}
  DynamicQueryReader (a -> b)
f <*> :: forall a b.
DynamicQueryReader (a -> b)
-> DynamicQueryReader a -> DynamicQueryReader b
<*> DynamicQueryReader a
g = (Archetype -> [b]) -> DynamicQueryReader b
forall a. (Archetype -> [a]) -> DynamicQueryReader a
DynamicQueryReader ((Archetype -> [b]) -> DynamicQueryReader b)
-> (Archetype -> [b]) -> DynamicQueryReader b
forall a b. (a -> b) -> a -> b
$ \Archetype
arch ->
    ((a -> b) -> a -> b) -> [a -> b] -> [a] -> [b]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (a -> b) -> a -> b
forall a b. (a -> b) -> a -> b
($) (DynamicQueryReader (a -> b) -> Archetype -> [a -> b]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader (a -> b)
f Archetype
arch) ([a] -> [b]) -> [a] -> [b]
forall a b. (a -> b) -> a -> b
$ DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
g Archetype
arch

-- | @since 0.10
instance DynamicQueryReaderF DynamicQueryReader where
  {-# INLINE entity #-}
  entity :: DynamicQueryReader EntityID
entity = (Archetype -> [EntityID]) -> DynamicQueryReader EntityID
forall a. (Archetype -> [a]) -> DynamicQueryReader a
DynamicQueryReader ((Archetype -> [EntityID]) -> DynamicQueryReader EntityID)
-> (Archetype -> [EntityID]) -> DynamicQueryReader EntityID
forall a b. (a -> b) -> a -> b
$ \Archetype
arch -> Set EntityID -> [EntityID]
forall a. Set a -> [a]
Set.toList (Set EntityID -> [EntityID]) -> Set EntityID -> [EntityID]
forall a b. (a -> b) -> a -> b
$ Archetype -> Set EntityID
A.entities Archetype
arch

  {-# INLINE fetchDyn #-}
  fetchDyn :: forall a. Component a => ComponentID -> DynamicQueryReader a
fetchDyn ComponentID
cId = (Archetype -> [a]) -> DynamicQueryReader a
forall a. (Archetype -> [a]) -> DynamicQueryReader a
DynamicQueryReader ((Archetype -> [a]) -> DynamicQueryReader a)
-> (Archetype -> [a]) -> DynamicQueryReader a
forall a b. (a -> b) -> a -> b
$ \Archetype
arch -> ComponentID -> Archetype -> [a]
forall a. Component a => ComponentID -> Archetype -> [a]
A.lookupComponentsAsc ComponentID
cId Archetype
arch

  {-# INLINE fetchMaybeDyn #-}
  fetchMaybeDyn :: forall a.
Component a =>
ComponentID -> DynamicQueryReader (Maybe a)
fetchMaybeDyn ComponentID
cId = (Archetype -> [Maybe a]) -> DynamicQueryReader (Maybe a)
forall a. (Archetype -> [a]) -> DynamicQueryReader a
DynamicQueryReader ((Archetype -> [Maybe a]) -> DynamicQueryReader (Maybe a))
-> (Archetype -> [Maybe a]) -> DynamicQueryReader (Maybe a)
forall a b. (a -> b) -> a -> b
$ \Archetype
arch -> case ComponentID -> Archetype -> Maybe [a]
forall a. Component a => ComponentID -> Archetype -> Maybe [a]
A.lookupComponentsAscMaybe ComponentID
cId Archetype
arch of
    Just [a]
as -> (a -> Maybe a) -> [a] -> [Maybe a]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Maybe a
forall a. a -> Maybe a
Just [a]
as
    Maybe [a]
Nothing -> Int -> Maybe a -> [Maybe a]
forall a. Int -> a -> [a]
replicate (Set EntityID -> Int
forall a. Set a -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length (Set EntityID -> Int) -> Set EntityID -> Int
forall a b. (a -> b) -> a -> b
$ Archetype -> Set EntityID
A.entities Archetype
arch) Maybe a
forall a. Maybe a
Nothing

-- | Dynamic query for components by ID.
--
-- @since 0.9

-- | Dynamic query filter.
--
-- @since 0.9
data DynamicQueryFilter = DynamicQueryFilter
  { -- | `ComponentID`s to include.
    --
    -- @since 0.9
    DynamicQueryFilter -> Set ComponentID
filterWith :: !(Set ComponentID),
    -- | `ComponentID`s to exclude.
    --
    -- @since 0.9
    DynamicQueryFilter -> Set ComponentID
filterWithout :: !(Set ComponentID)
  }

-- | @since 0.9
instance Semigroup DynamicQueryFilter where
  DynamicQueryFilter Set ComponentID
withA Set ComponentID
withoutA <> :: DynamicQueryFilter -> DynamicQueryFilter -> DynamicQueryFilter
<> DynamicQueryFilter Set ComponentID
withB Set ComponentID
withoutB =
    Set ComponentID -> Set ComponentID -> DynamicQueryFilter
DynamicQueryFilter (Set ComponentID
withA Set ComponentID -> Set ComponentID -> Set ComponentID
forall a. Semigroup a => a -> a -> a
<> Set ComponentID
withB) (Set ComponentID
withoutA Set ComponentID -> Set ComponentID -> Set ComponentID
forall a. Semigroup a => a -> a -> a
<> Set ComponentID
withoutB)

-- | @since 0.9
instance Monoid DynamicQueryFilter where
  mempty :: DynamicQueryFilter
mempty = Set ComponentID -> Set ComponentID -> DynamicQueryFilter
DynamicQueryFilter Set ComponentID
forall a. Monoid a => a
mempty Set ComponentID
forall a. Monoid a => a
mempty

-- | Match all entities.
--
-- @since 0.10
allDyn :: Set ComponentID -> DynamicQueryReader a -> Entities -> [a]
allDyn :: forall a.
Set ComponentID -> DynamicQueryReader a -> Entities -> [a]
allDyn Set ComponentID
cIds DynamicQueryReader a
q Entities
es =
  if Set ComponentID -> Bool
forall a. Set a -> Bool
Set.null Set ComponentID
cIds
    then DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q Archetype
A.empty {A.entities = Map.keysSet $ entities es}
    else
      let go :: Node -> [a]
go Node
n = DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q (Archetype -> [a]) -> Archetype -> [a]
forall a b. (a -> b) -> a -> b
$ Node -> Archetype
AS.nodeArchetype Node
n
       in (Node -> [a]) -> Map ArchetypeID Node -> [a]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Node -> [a]
go (Set ComponentID -> Archetypes -> Map ArchetypeID Node
AS.find Set ComponentID
cIds (Archetypes -> Map ArchetypeID Node)
-> Archetypes -> Map ArchetypeID Node
forall a b. (a -> b) -> a -> b
$ Entities -> Archetypes
archetypes Entities
es)

-- | Match all entities with a filter.
--
-- @since 0.10
filterDyn :: Set ComponentID -> (Node -> Bool) -> DynamicQueryReader a -> Entities -> [a]
filterDyn :: forall a.
Set ComponentID
-> (Node -> Bool) -> DynamicQueryReader a -> Entities -> [a]
filterDyn Set ComponentID
cIds Node -> Bool
f DynamicQueryReader a
q Entities
es =
  if Set ComponentID -> Bool
forall a. Set a -> Bool
Set.null Set ComponentID
cIds
    then DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q Archetype
A.empty {A.entities = Map.keysSet $ entities es}
    else
      let go :: Node -> [a]
go Node
n = DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q (Archetype -> [a]) -> Archetype -> [a]
forall a b. (a -> b) -> a -> b
$ Node -> Archetype
AS.nodeArchetype Node
n
       in (Node -> [a]) -> Map ArchetypeID Node -> [a]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Node -> [a]
go ((Node -> Bool) -> Map ArchetypeID Node -> Map ArchetypeID Node
forall a k. (a -> Bool) -> Map k a -> Map k a
Map.filter Node -> Bool
f (Map ArchetypeID Node -> Map ArchetypeID Node)
-> Map ArchetypeID Node -> Map ArchetypeID Node
forall a b. (a -> b) -> a -> b
$ Set ComponentID -> Archetypes -> Map ArchetypeID Node
AS.find Set ComponentID
cIds (Archetypes -> Map ArchetypeID Node)
-> Archetypes -> Map ArchetypeID Node
forall a b. (a -> b) -> a -> b
$ Entities -> Archetypes
archetypes Entities
es)

-- | Match a single entity.
--
-- @since 0.10
singleDyn :: (HasCallStack) => Set ComponentID -> DynamicQueryReader a -> Entities -> a
singleDyn :: forall a.
HasCallStack =>
Set ComponentID -> DynamicQueryReader a -> Entities -> a
singleDyn Set ComponentID
cIds DynamicQueryReader a
q Entities
es = case Set ComponentID -> DynamicQueryReader a -> Entities -> Maybe a
forall a.
Set ComponentID -> DynamicQueryReader a -> Entities -> Maybe a
singleMaybeDyn Set ComponentID
cIds DynamicQueryReader a
q Entities
es of
  Just a
a -> a
a
  Maybe a
_ -> [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"singleDyn: expected a single entity"

-- | Match a single entity, or `Nothing`.
--
-- @since 0.10
singleMaybeDyn :: Set ComponentID -> DynamicQueryReader a -> Entities -> Maybe a
singleMaybeDyn :: forall a.
Set ComponentID -> DynamicQueryReader a -> Entities -> Maybe a
singleMaybeDyn Set ComponentID
cIds DynamicQueryReader a
q Entities
es =
  if Set ComponentID -> Bool
forall a. Set a -> Bool
Set.null Set ComponentID
cIds
    then case Map EntityID ArchetypeID -> [EntityID]
forall k a. Map k a -> [k]
Map.keys (Map EntityID ArchetypeID -> [EntityID])
-> Map EntityID ArchetypeID -> [EntityID]
forall a b. (a -> b) -> a -> b
$ Entities -> Map EntityID ArchetypeID
entities Entities
es of
      [EntityID
eId] -> case DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q (Archetype -> [a]) -> Archetype -> [a]
forall a b. (a -> b) -> a -> b
$ EntityID -> Archetype
A.singleton EntityID
eId of
        [a
a] -> a -> Maybe a
forall a. a -> Maybe a
Just a
a
        [a]
_ -> Maybe a
forall a. Maybe a
Nothing
      [EntityID]
_ -> Maybe a
forall a. Maybe a
Nothing
    else case Map ArchetypeID Node -> [Node]
forall k a. Map k a -> [a]
Map.elems (Map ArchetypeID Node -> [Node]) -> Map ArchetypeID Node -> [Node]
forall a b. (a -> b) -> a -> b
$ Set ComponentID -> Archetypes -> Map ArchetypeID Node
AS.find Set ComponentID
cIds (Archetypes -> Map ArchetypeID Node)
-> Archetypes -> Map ArchetypeID Node
forall a b. (a -> b) -> a -> b
$ Entities -> Archetypes
archetypes Entities
es of
      [Node
n] -> case DynamicQueryReader a -> Archetype -> [a]
forall a. DynamicQueryReader a -> Archetype -> [a]
runDynQueryReader DynamicQueryReader a
q (Archetype -> [a]) -> Archetype -> [a]
forall a b. (a -> b) -> a -> b
$ Node -> Archetype
AS.nodeArchetype Node
n of
        [a
a] -> a -> Maybe a
forall a. a -> Maybe a
Just a
a
        [a]
_ -> Maybe a
forall a. Maybe a
Nothing
      [Node]
_ -> Maybe a
forall a. Maybe a
Nothing