{-# LANGUAGE ConstraintKinds            #-}
# LANGUAGE FlexibleContexts            #
# LANGUAGE FlexibleInstances           #
# LANGUAGE GeneralizedNewtypeDeriving #
# LANGUAGE MultiParamTypeClasses       #
{-# LANGUAGE RankNTypes                 #-}
# LANGUAGE ScopedTypeVariables         #
# LANGUAGE StandaloneDeriving          #
{-# LANGUAGE TemplateHaskell            #-}
# LANGUAGE TypeFamilies                #

module Apecs.Core where

import           Control.Monad.Catch
import           Control.Monad.IO.Class
import           Control.Monad.Reader
import qualified Data.Vector.Unboxed  as U

-- | An Entity is just an integer, used to index into a component store.
--   In general, use @newEntity@, @cmap@, and component tags instead of manipulating these directly.
--
--   For performance reasons, negative values like (-1) are reserved for stores to represent special values, so avoid using these.
newtype Entity = Entity {unEntity :: Int} deriving (Num, Eq, Ord, Show, Enum)

  | A SystemT is a newtype around ` ReaderT w m a ` , where ` w ` is the game world variable .
--   Systems serve to
--
--   * Allow type-based lookup of a component's store through @getStore@.
--
    * Lift side effects into their host Monad .
newtype SystemT w m a = SystemT {unSystem :: ReaderT w m a} deriving (Functor, Monad, Applicative, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask)
type System w a = SystemT w IO a

deriving instance Monad m => MonadReader w (SystemT w m)

-- | A component is defined by specifying how it is stored.
    The constraint ensures that stores and components are mapped one - to - one .
class (Elem (Storage c) ~ c) => Component c where
  type Storage c

  | @Has w m c@ means that world @w@ can produce a @Storage c@.
--   It is parameterized over @m@ to allow stores to be foreign.
class (Monad m, Component c) => Has w m c where
  getStore :: SystemT w m (Storage c)

  | The type of components stored by a store , e.g. @Elem ( Map c ) = c@.
type family Elem s

-- | Indicates that the store @s@ can be initialized.
    Generally , \"base\ " stores like @Map c@ can be initialized , but composite stores like @MaybeStore s@ can not .
class ExplInit m s where
  -- | Initialize a new empty store.
  explInit :: m s

-- | Stores that we can read using @explGet@ and @explExists@.
    For some entity @e@ , @eplGet s e@ is only guaranteed to be safe if @explExists s e@ returns @True@.
class Monad m => ExplGet m s where
  -- | Reads a component from the store. What happens if the component does not exist is left undefined, and might not necessarily crash.
  explGet :: s -> Int -> m (Elem s)
  -- | Returns whether there is a component for the given index.
  explExists :: s -> Int -> m Bool

-- | Stores that can be written.
class Monad m => ExplSet m s where
  -- | Writes a component to the store.
  explSet :: s -> Int -> Elem s -> m ()

-- | Stores that components can be removed from.
class Monad m => ExplDestroy m s where
  -- | Destroys the component for a given index.
  explDestroy :: s -> Int -> m ()

-- | Stores that we can request a list of member entities for.
class Monad m => ExplMembers m s where
  -- | Returns an unboxed vector of member indices
  explMembers :: s -> m (U.Vector Int)

type Get     w m c = (Has w m c, ExplGet     m (Storage c))
type Set     w m c = (Has w m c, ExplSet     m (Storage c))
type Members w m c = (Has w m c, ExplMembers m (Storage c))
type Destroy w m c = (Has w m c, ExplDestroy m (Storage c))

# LANGUAGE ConstraintKinds            #
# LANGUAGE RankNTypes                 #
# LANGUAGE TemplateHaskell            #
 | An Entity is just an integer, used to index into a component store.
   In general, use @newEntity@, @cmap@, and component tags instead of manipulating these directly.

   For performance reasons, negative values like (-1) are reserved for stores to represent special values, so avoid using these.
   Systems serve to

   * Allow type-based lookup of a component's store through @getStore@.

 | A component is defined by specifying how it is stored.
   It is parameterized over @m@ to allow stores to be foreign.
 | Indicates that the store @s@ can be initialized.
 | Initialize a new empty store.
 | Stores that we can read using @explGet@ and @explExists@.
 | Reads a component from the store. What happens if the component does not exist is left undefined, and might not necessarily crash.
 | Returns whether there is a component for the given index.
 | Stores that can be written.
 | Writes a component to the store.
 | Stores that components can be removed from.
 | Destroys the component for a given index.
 | Stores that we can request a list of member entities for.
 | Returns an unboxed vector of member indices
# LANGUAGE FlexibleContexts            #
# LANGUAGE FlexibleInstances           #
# LANGUAGE GeneralizedNewtypeDeriving #
# LANGUAGE MultiParamTypeClasses       #
# LANGUAGE ScopedTypeVariables         #
# LANGUAGE StandaloneDeriving          #
# LANGUAGE TypeFamilies                #

module Apecs.Core where

import           Control.Monad.Catch
import           Control.Monad.IO.Class
import           Control.Monad.Reader
import qualified Data.Vector.Unboxed  as U

newtype Entity = Entity {unEntity :: Int} deriving (Num, Eq, Ord, Show, Enum)

  | A SystemT is a newtype around ` ReaderT w m a ` , where ` w ` is the game world variable .
    * Lift side effects into their host Monad .
newtype SystemT w m a = SystemT {unSystem :: ReaderT w m a} deriving (Functor, Monad, Applicative, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask)
type System w a = SystemT w IO a

deriving instance Monad m => MonadReader w (SystemT w m)

    The constraint ensures that stores and components are mapped one - to - one .
class (Elem (Storage c) ~ c) => Component c where
  type Storage c

  | @Has w m c@ means that world @w@ can produce a @Storage c@.
class (Monad m, Component c) => Has w m c where
  getStore :: SystemT w m (Storage c)

  | The type of components stored by a store , e.g. @Elem ( Map c ) = c@.
type family Elem s

    Generally , \"base\ " stores like @Map c@ can be initialized , but composite stores like @MaybeStore s@ can not .
class ExplInit m s where
  explInit :: m s

    For some entity @e@ , @eplGet s e@ is only guaranteed to be safe if @explExists s e@ returns @True@.
class Monad m => ExplGet m s where
  explGet :: s -> Int -> m (Elem s)
  explExists :: s -> Int -> m Bool

class Monad m => ExplSet m s where
  explSet :: s -> Int -> Elem s -> m ()

class Monad m => ExplDestroy m s where
  explDestroy :: s -> Int -> m ()

class Monad m => ExplMembers m s where
  explMembers :: s -> m (U.Vector Int)

type Get     w m c = (Has w m c, ExplGet     m (Storage c))
type Set     w m c = (Has w m c, ExplSet     m (Storage c))
type Members w m c = (Has w m c, ExplMembers m (Storage c))
type Destroy w m c = (Has w m c, ExplDestroy m (Storage c))
