-- |
-- Copyright  : (c) Ivan Perez & Henrik Nilsson, 2014.
-- License    : See LICENSE file.
-- Maintainer : Ivan Perez <ivan.perez@keera.co.uk>
--
-- Objects as signal functions.
--
-- Live objects in the game take user input and the game universe and define
-- their state in terms of that. They can remember what happened (see Yampa's
-- Arrow combinators, which hide continuations), change their behaviour (see
-- switches in Yampa).
--
-- They cannot affect other objects, but they can kill themselves (see
-- 'harakiri'). Should you need to spawn new game elements upon events, you
-- might want to change 'harakiri' to something more general.
module ObjectSF where

-- External imports
import FRP.Yampa

-- Internal imports
import Data.IdentityList
import Input
import Objects

-- | Objects are defined as transformations that take 'ObjectInput' signals and
-- return 'ObjectOutput' signals.
type ObjectSF = SF ObjectInput ObjectOutput

-- | In order to determine its new output, an object needs to know the user's
-- desires ('userInput'), whether there have been any collisions
-- ('collisions'), and the presence of any pre-existing objects
-- ('knownObjects').
--
-- The reason for depending on 'Collisions' is that objects may ``die'' when
-- hit.
--
-- The reason for depending on 'Objects' is that objects may choose to follow
-- other objects.
--
-- TODO: Would it be possible to depend on the specific object SFs internally
-- and remove the explicit 'knownObjects'? I guess so, so long as it's possible
-- to always provide the same input to those SFs that they will have in the
-- game: because they are different instances, we need the exact same input to
-- guarantee the exact same behaviour.
data ObjectInput = ObjectInput
  { userInput    :: Controller
  , collisions   :: Collisions
  , knownObjects :: Objects
  }

-- | What we can see about each live object at each time. It's a snapshot of
-- the object.
data ObjectOutput = ObjectOutput
  { outputObject :: Object   -- ^ The object's state (position, shape, etc.).
  , harakiri     :: Event () -- ^ Whether the object has died (killed itself).
  }

-- | Handy function to create an object that is currently alive.
livingObject :: Object -> ObjectOutput
livingObject o = ObjectOutput o noEvent

-- | List of identifiable objects. Used to work with dynamic object
-- collections.
type ObjectSFs = IL ObjectSF

extractObjects :: Functor f => SF (f ObjectOutput) (f Object)
extractObjects = arr (fmap outputObject)

-- | A list of object outputs
type ObjectOutputs = [ObjectOutput]
