module Mischief.ECS.World.Change where

import Control.Monad.Reader (MonadIO (liftIO), ask)
import Data.Foldable
import Data.IORef
import Data.Map qualified as Map
import Data.Maybe
import Data.Set qualified as Set
import GHC.Base (Int (..))
import GHC.Stack (HasCallStack)
import Mischief.ECS.App.SystemDef
import Mischief.ECS.Archetypes
import Mischief.ECS.Components
import Mischief.ECS.Components.Bundle
import Mischief.ECS.Entities
import Mischief.ECS.EntityDef
import Mischief.ECS.Events
import Mischief.ECS.Log
import Mischief.ECS.Tables
import Mischief.ECS.Vec qualified as Vec
import Mischief.ECS.World
import Mischief.ECS.World.Query
import Mischief.ECS.World.Query.Queryable
import Mischief.ECS.World.Utils

data ChangeResult = ChangeResult
  { ChangeResult -> [ProcessedBundleElement]
requiredComponentsAdded :: [ProcessedBundleElement],
    ChangeResult -> [ProcessedBundleElement]
newComponents :: [ProcessedBundleElement]
  }

changeArchetype :: (HasCallStack) => Entity -> ArchetypeData -> Maybe ProcessedBundleData -> System ChangeResult
changeArchetype :: HasCallStack =>
Entity
-> ArchetypeData
-> Maybe ProcessedBundleData
-> System ChangeResult
changeArchetype Entity
entity ArchetypeData
newArchetype Maybe ProcessedBundleData
insertedBundle = do
  world <- System World
forall w (m :: * -> *). MonadSystem w m => m World
unsafeGetWorld
  currentTick <- liftIO $ readIORef world.tick

  reqAdded <- liftIO $ newIORef []

  Just pointer <- liftIO $ getPointer entity world.entities
  (EntityPointer (# archetypeId', rowIndex' #)) <- liftIO $ readIORef pointer

  table <- Vec.read world.tables.inner (I# archetypeId')

  collected <- liftIO $ takeComponentsFromTable (EntityPointer (# archetypeId', rowIndex' #)) table

  newElements'' <-
    mapM
      ( \ComponentId
component -> do
          case Maybe ProcessedBundleData
insertedBundle of
            Maybe ProcessedBundleData
Nothing -> System ProcessedBundleElement
-> (ProcessedBundleElement -> System ProcessedBundleElement)
-> Maybe ProcessedBundleElement
-> System ProcessedBundleElement
forall b a. b -> (a -> b) -> Maybe a -> b
maybe System ProcessedBundleElement
forall a. HasCallStack => a
undefined ProcessedBundleElement -> System ProcessedBundleElement
forall a. a -> System a
forall (m :: * -> *) a. Monad m => a -> m a
return ((ProcessedBundleElement -> Bool)
-> [ProcessedBundleElement] -> Maybe ProcessedBundleElement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\ProcessedBundleElement
x -> ProcessedBundleElement
x.id ComponentId -> ComponentId -> Bool
forall a. Eq a => a -> a -> Bool
== ComponentId
component) ProcessedBundleData
collected.elements)
            Just ProcessedBundleData
bundle ->
              case (ProcessedBundleElement -> Bool)
-> [ProcessedBundleElement] -> Maybe ProcessedBundleElement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\ProcessedBundleElement
x -> ProcessedBundleElement
x.id ComponentId -> ComponentId -> Bool
forall a. Eq a => a -> a -> Bool
== ComponentId
component) ProcessedBundleData
bundle.elements of
                Just ProcessedBundleElement
x -> ProcessedBundleElement -> System ProcessedBundleElement
forall a. a -> System a
forall (m :: * -> *) a. Monad m => a -> m a
return ProcessedBundleElement
x
                Maybe ProcessedBundleElement
Nothing ->
                  case (ProcessedBundleElement -> Bool)
-> [ProcessedBundleElement] -> Maybe ProcessedBundleElement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\ProcessedBundleElement
x -> ProcessedBundleElement
x.id ComponentId -> ComponentId -> Bool
forall a. Eq a => a -> a -> Bool
== ComponentId
component) ProcessedBundleData
collected.elements of
                    Just ProcessedBundleElement
x -> ProcessedBundleElement -> System ProcessedBundleElement
forall a. a -> System a
forall (m :: * -> *) a. Monad m => a -> m a
return ProcessedBundleElement
x
                    Maybe ProcessedBundleElement
Nothing -> do
                      r <- ComponentId -> System ProcessedBundleElement
getDefault ComponentId
component
                      liftIO $ modifyIORef' reqAdded (++ [r])
                      return r
      )
      $ Set.toList newArchetype.components

  let newElements = case Maybe ProcessedBundleData
insertedBundle of
        Maybe ProcessedBundleData
Nothing -> [ProcessedBundleElement] -> ProcessedBundleData
ProcessedBundleData [ProcessedBundleElement]
newElements''
        Just ProcessedBundleData
bundle ->
          let newElements' :: ProcessedBundleData
newElements' = ProcessedBundleData
-> (ComponentId -> Bool) -> Tick -> ProcessedBundleData
setChangedTickOfComponents ([ProcessedBundleElement] -> ProcessedBundleData
ProcessedBundleData [ProcessedBundleElement]
newElements'') (\ComponentId
id -> ProcessedBundleData -> ComponentId -> Bool
isInProcessedBundle ProcessedBundleData
bundle ComponentId
id Bool -> Bool -> Bool
&& ProcessedBundleData -> ComponentId -> Bool
isInProcessedBundle ProcessedBundleData
collected ComponentId
id) Tick
currentTick
           in ProcessedBundleData
-> (ComponentId -> Bool) -> Tick -> ProcessedBundleData
setAddedTickOfComponents ProcessedBundleData
newElements' (\ComponentId
id -> ProcessedBundleData -> ComponentId -> Bool
isInProcessedBundle ProcessedBundleData
bundle ComponentId
id Bool -> Bool -> Bool
&& Bool -> Bool
not (ProcessedBundleData -> ComponentId -> Bool
isInProcessedBundle ProcessedBundleData
collected ComponentId
id)) Tick
currentTick

  liftIO $ insertEntityIntoTables newElements world.tables newArchetype.id (entity, pointer)

  requiredComponentsAdded <- liftIO $ readIORef reqAdded
  return ChangeResult {requiredComponentsAdded}

getDefault :: ComponentId -> System ProcessedBundleElement
getDefault :: ComponentId -> System ProcessedBundleElement
getDefault (ComponentId (# Word#
id, Maybe Entity
e #)) = do
  world <- System World
forall w (m :: * -> *). MonadSystem w m => m World
unsafeGetWorld

  Just x <- get (C @DefaultValue) (Entity (# id, 0## #))
  let dv = Result DefaultValue -> DefaultValue
forall c. Result c -> c
value Result DefaultValue
x
  let (DefaultValue value) = dv

  let (SystemId sys) = world.systemId
  currentSystemTick <- fromMaybe (SystemTick $ Tick (0, 0)) <$> get (Val $ C @SystemTick) sys

  return
    ProcessedBundleElement
      { id = ComponentId (# id, e #),
        component =
          ComponentData
            { value,
              ticks = ComponentTicks {changed = currentSystemTick.inner, added = currentSystemTick.inner}
            }
      }