packages feed

aztecs-hierarchy-0.2.0.0: src/Data/Aztecs/Hierarchy.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Data.Aztecs.Hierarchy where

import Control.Arrow (returnA)
import Data.Aztecs
import qualified Data.Aztecs.Access as A
import qualified Data.Aztecs.Query as Q
import qualified Data.Aztecs.System as S
import Data.Maybe (fromMaybe)
import Data.Set (Set)
import qualified Data.Set as Set

newtype Parent = Parent {unParent :: EntityID}
  deriving (Eq, Ord, Show)

instance Component Parent

newtype ParentState = ParentState {unParentState :: EntityID}
  deriving (Show)

instance Component ParentState

newtype Children = Children {unChildren :: Set EntityID}
  deriving (Eq, Ord, Show, Semigroup, Monoid)

instance Component Children

newtype ChildState = ChildState {unChildState :: Set EntityID}
  deriving (Show)

instance Component ChildState

update :: System () ()
update = proc () -> do
  parents <-
    S.all
      ( proc () -> do
          entity <- Q.entity -< ()
          Parent parent <- Q.fetch -< ()
          maybeParentState <- Q.fetchMaybe @_ @ParentState -< ()
          returnA -< (entity, parent, maybeParentState)
      )
      -<
        ()
  children <-
    S.all
      ( proc () -> do
          entity <- Q.entity -< ()
          Children cs <- Q.fetch -< ()
          maybeChildState <- Q.fetchMaybe @_ @ChildState -< ()
          returnA -< (entity, cs, maybeChildState)
      )
      -<
        ()
  S.queue
    ( \(parents, childRes) -> do
        mapM_
          ( \(entity, parent, maybeParentState) -> case maybeParentState of
              Just (ParentState parentState) -> do
                if parent /= parentState
                  then do
                    A.insert parent $ ParentState parent

                    -- Remove this entity from the previous parent's children.
                    maybeLastChildren <- A.lookup parentState
                    let lastChildren = fromMaybe mempty $ unChildren <$> maybeLastChildren
                    let lastChildren' = Set.filter (/= entity) lastChildren
                    A.insert parentState . Children $ lastChildren'

                    -- Add this entity to the new parent's children.
                    maybeChildren <- A.lookup parent
                    let parentChildren = fromMaybe mempty $ unChildren <$> maybeChildren
                    A.insert parent . Children $ Set.insert entity parentChildren
                  else return ()
              Nothing -> do
                A.spawn_ . bundle $ ParentState parent
                maybeChildren <- A.lookup parent
                let parentChildren = fromMaybe mempty $ unChildren <$> maybeChildren
                A.insert parent . Children $ Set.insert entity parentChildren
          )
          parents
        mapM_
          ( \(entity, children, maybeChildState) -> case maybeChildState of
              Just (ChildState childState) -> do
                if children /= childState
                  then do
                    A.insert entity $ ChildState children
                    let added = Set.difference children childState
                    -- TODO removed = Set.difference childState children
                    mapM_ (\e -> A.insert e . Parent $ entity) added
                  else return ()
              Nothing -> do
                A.insert entity $ ChildState children
                mapM_ (\e -> A.insert e . Parent $ entity) children
          )
          childRes
    )
    -<
      (parents, children)