{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

module Mischief.Math.Transform.Plugin where

import Mischief.ECS
import Mischief.ECS.App.Schedules (First)
import Mischief.ECS.Relationships.ChildOf qualified as ChildOf
import Mischief.Math.Transform (GlobalTransform (GlobalTransform), Transform, composeTransforms)

data TransformPlugin

instance Plugin TransformPlugin where
  init :: System ()
init = do
    forall sc. Schedule sc => SystemConfig -> System ()
forall {k} (sc :: k). Schedule sc => SystemConfig -> System ()
schedule @First (SystemConfig -> System ()) -> SystemConfig -> System ()
forall a b. (a -> b) -> a -> b
$ System () -> SystemConfig
forall a. ToSystems a => a -> SystemConfig
systems System ()
propTransform

propTransform :: System ()
propTransform :: System ()
propTransform = do
  Query System () -> System ()
forall w (m :: * -> *) out. MonadSystem w m => Query m out -> m ()
query_ Query System ()
qiterTransform

qiterTransform :: Query System ()
qiterTransform :: Query System ()
qiterTransform = do
  root <- [q|Entity / Without ChildOf -> *|]
  dirty <- qlift $ check [qf|Changed Transform|] root
  if dirty
    then do
      trans <- qget root [qd|Transform|]
      qlift $ insert (GlobalTransform trans) root
      child <- qiterE =<< qlift (ChildOf.children root)
      qpropTransformRec child (GlobalTransform trans)
    else
      qiterTransformRec root

-- | Recursively iterates downwards and starts propagating the transform when finding a dirty entity.
qiterTransformRec :: Entity -> Query System ()
qiterTransformRec :: Entity -> Query System ()
qiterTransformRec Entity
entity = do
  child <- [Entity] -> Query System Entity
forall w (m :: * -> *).
MonadSystem w m =>
[Entity] -> Query m Entity
qiterE ([Entity] -> Query System Entity)
-> Query System [Entity] -> Query System Entity
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< System [Entity] -> Query System [Entity]
forall w (m :: * -> *) a. MonadSystem w m => m a -> Query m a
qlift (Entity -> System [Entity]
forall (m :: * -> *) w. MonadSystem w m => Entity -> m [Entity]
ChildOf.children Entity
entity)
  dirty <- qlift $ check [qf|Changed Transform|] child

  if dirty
    then do
      global <- qget entity [qd|GlobalTransform|]
      qpropTransformRec child global
    else do
      qiterTransformRec child

-- | Given an entity and its parent's Global Transform, propagates the Global Transform downwards.
qpropTransformRec :: Entity -> GlobalTransform -> Query System ()
qpropTransformRec :: Entity -> GlobalTransform -> Query System ()
qpropTransformRec Entity
entity (GlobalTransform Transform
global) = do
  transform <- Entity -> C Transform -> Query System Transform
forall w (m :: * -> *) qd out.
(MonadSystem w m, Queryable qd out) =>
Entity -> qd -> Query m out
qget Entity
entity [qd|Transform|]

  let global' = Transform
global Transform -> Transform -> Transform
`composeTransforms` Transform
transform
  qlift $ insert (GlobalTransform global') entity

  child <- qiterE =<< qlift (ChildOf.children entity)
  qpropTransformRec child (GlobalTransform global')