{-# OPTIONS_GHC -Wno-unused-imports #-}

-- |
-- Module: Queries Tutorial
-- Description: How To Mischief.
--
-- [Previous Chapter: Events and Messages]("Mischief.ECS.Tutorial.Guides.Events")
--
-- [Main Page]("Mischief.ECS")
module Mischief.ECS.Tutorial.Guides.Change
  ( -- * Learn You an ECS for Great Mischief! - 2.11. Change Detection
    -- $intro

    -- * Events
    -- $events

    -- * Query Filters
    -- $filters

    -- * Avoiding False Changes
    -- $false

    -- * Component Hooks
    -- $hooks
  )
where

import Mischief.ECS

-- $intro
-- This chapter will show you ways of listening and reacting to changes in the World.

-- $events
-- There are various pre-defined events that can be used to listen to when a component is added, inserted, or removed:
--
-- @
-- onAddName :: 'OnAdd' Name -> 'System' ()
-- onAddName OnAdd {entity} = do
--   Just name <- 'single' ['q'|entity. Name|]
--   'info' ['i'|#{entity} was just added the name #{name}|]
-- @
--
-- For insertion and removal you can use @OnSet@ and @OnRemove@ respectively.
--
-- There are also @OnAddRel@, @OnSetRel@, @OnRemoveRel@ which act the same but can listen to relationship changes.
--
-- For instance:
--
-- @
-- onRemoveChildOf :: 'OnRemoveRel' ChildOf -> 'System' ()
-- onRemoveChildOf OnRemoveRel {entity, target} =
--   'info' ['i'|#{entiy} is no longer the child of #{target}|]
-- @

-- $filters
-- You can use the @Changed@ filter in a query to select the entities that have had a component changed.
--
-- With the following query we can get the name of all entities that have had the @Health@ component changed in the last frame:
--
-- @
-- ['q'|Name / Changed Health|]
--   & 'query'
-- @
--
-- Note however, that the changed marker is set per-archetype table. This means that you might get extra entities, not just those which have had that component changed.
-- This is intended to be used for optimization purposes, and not for reacting to individual changes. Use events for that instead.

-- $false
-- One essential detail to be aware of here is that listening to insertion (through @OnSet@ or @Changed@) doesn't necessarily mean a component's value has been changed!
--
-- The following @insert@ /will/ trigger change detection:
--
-- @
-- p <- 'spawn' (Health 100)
-- 'insert' (Health 100) p
-- @
--
-- To avoid this, you can derive 'Eq' on your components and use @'insertIfNeq'@, which will only perform insertion if the value of the component is different
-- from the current one.

-- $hooks
-- Hooks are specialized events associated directly to a Component instance.
--
-- @
-- instance 'Component' Foo where
--   onAdd = [hook onAddFoo]
--   onSet = [hook onSetFoo]
--   onRemove = [hook onRemoveFoo]
--
-- onAddFoo :: 'HookContext' -> 'System' ()
-- onAddFoo = ...
--
-- onSetFoo :: 'HookContext' -> 'System' ()
-- onSetFoo = ...
--
-- onRemoveFoo :: 'HookContext' -> 'System' ()
-- onRemoveFoo = ...
-- @
--
-- Similar to change events, @HookContext@ has an @.entity@ field you can use to access the entity the hook was triggered on.
--
-- Relationship support dedicated hooks, named @onAddRel@, @onSetRel@, @onRemoveRel@, following the same rules as the normal hooks.
--
-- This is how we can log a message each time @Likes@ is added between two entities:
--
-- @
-- instance 'Component' Likes where
--   onAddRel = ['hookRel' onLikesAdd]
--
-- onLikesAdd :: 'HookContextRel' -> 'System' ()
-- onLikesAdd HookContextRel {entity, target} = 'info' ['i'|#{entity} now likes #{target}!|]
-- @
--
-- There are a number of predefined hooks that are useful when working with relationships, which can be found in "Mischief.ECS.HooksRel".
-- For instance, @addOther@ can be used to automate adding a complementary relationship on the target of a relationship.
--
-- As a quick example of why this is useful, this is how you'd create a @Before@/@After@ relationship between entities:
--
-- @
-- data Before = Before
-- data After = After
--
-- instance 'Component' Before where
--   onAddRel =  [HooksRel.'Mischief.ECS.HooksRel.addOther' ('const' After)]
--
-- instance 'Component' After where
--   'hooks' = [HooksRel.'Mischief.ECS.HooksRel.addOther' ('const' Before)]
-- @
--
-- Now, when you do:
--
-- @
-- 'insert' ('Rel' Before a) b
-- @
--
-- A @Rel After b@ will be inserted automatically on @a@.
--
-- And vice versa.
--
-- There are other interesting hooks, such as ones for automatically cleaning up a relationship when an entity is despawned. Check out "Mischief.ECS.HooksRel" for details!