packages feed

roguestar-engine-0.6.0.0: src/DBData.hs

{-# LANGUAGE FlexibleInstances,
             MultiParamTypeClasses,
             FunctionalDependencies,
             UndecidableInstances,
             ScopedTypeVariables #-}

module DBData
    (Reference,
     toUID,
     CreatureRef,
     PlaneRef,
     ToolRef,
     BuildingRef,
     TheUniverse(..),
     the_universe,
     (=:=), (=/=),
     GenericReference(..),
     ReferenceType(..),
     LocationChild(..),
     LocationParent(..),
     Location,
     Position(..),
     Standing(..),
     Dropped(..),
     Inventory(..),
     Wielded(..),
     Constructed(..),
     Subsequent(..),
     Beneath(..),
     Type,
     _nullary,
     _creature,
     _tool,
     _plane,
     _building,
     _standing,
     _dropped,
     _inventory,
     _wielded,
     _constructed,
     _subsequent,
     _beneath,
     _position,
     _multiposition,
     _facing,
     _the_universe,
     asLocationTyped,
     asType,
     parent,
     child,
     coerceReferenceTyped,
     isReferenceTyped,
     coerceParentTyped,
     isParentTyped,
     coerceChildTyped,
     isChildTyped,
     coerceLocationRecord,
     coerceParent,
     coerceChild,
     genericParent,
     genericChild,
     generalizeParent,
     generalizeChild,
     generalizeLocation,
     toStanding,
     toDropped,
     toInventory,
     toWielded,
     returnToInventory,
     shuntToTheUniverse)
    where

import Facing
import DBPrivate
import ToolData
import CreatureData
import PlaneData
import BuildingData
import Data.Maybe
import Control.Monad
import Position

--
-- Type Instances
--
newtype Type a = Type a

_nullary :: Type (Reference ())
_nullary = Type $ error "_nullary: undefined"

_creature :: Type CreatureRef
_creature = Type $ error "_creature: undefined"

_tool :: Type ToolRef
_tool = Type $ error "_tool: undefined"

_plane :: Type PlaneRef
_plane = Type $ error "_plane: undefined"

_building :: Type BuildingRef
_building = Type $ error "_building: undefined"

_standing :: Type Standing
_standing = Type $ error "_standing: undefined"

_dropped :: Type Dropped
_dropped = Type $ error "_dropped: undefined"

_inventory :: Type Inventory
_inventory = Type $ error "_inventory: undefined"

_wielded :: Type Wielded
_wielded = Type $ error "_wielded: undefined"

_constructed :: Type Constructed
_constructed = Type $ error "_constructed: undefined"

_subsequent :: Type Subsequent
_subsequent = Type $ error "_subsequent: undefined"

_beneath :: Type Beneath
_beneath = Type $ error "_subsequent: undefined"

_position :: Type Position
_position = Type $ error "_position: undefined"

_multiposition :: Type MultiPosition
_multiposition = Type $ error "_multiposition: undefined"

_facing :: Type Facing
_facing = Type $ error "_facing: undefined"

_the_universe :: Type (Reference TheUniverse)
_the_universe = Type $ error "_the_universe: undefined"

--
-- Getting References generically.
--
class GenericReference a where
    fromLocation :: (ReferenceType x) => Location (Reference x) b -> Maybe a
    generalizeReference :: a -> Reference ()

instance (GenericReference a,GenericReference b) => GenericReference (Either a b) where
    fromLocation x = case (fromLocation x,fromLocation x) of
            (Just a,_) -> Just $ Left a
            (_,Just b) -> Just $ Right b
            _ | otherwise -> Nothing
    generalizeReference = either generalizeReference generalizeReference

instance (ReferenceType a) => GenericReference (Reference a) where
    fromLocation = coerceReference . genericChild
    generalizeReference = unsafeReference

instance (LocationChild c,LocationParent p) => GenericReference (Location c p) where
    fromLocation = coerceLocationRecord
    generalizeReference = genericChild

--
-- Reference Equality
--
(=:=) :: (GenericReference a,GenericReference b) => a -> b -> Bool
a =:= b = generalizeReference a == generalizeReference b

(=/=) :: (GenericReference a,GenericReference b) => a -> b -> Bool
a =/= b = not $ a =:= b

--
-- References
--

the_universe :: Reference TheUniverse
the_universe = UniverseRef

coerceReferenceTyped :: (ReferenceType a) => Type (Reference a) -> Reference x -> Maybe (Reference a)
coerceReferenceTyped = const coerceReference

isReferenceTyped :: (ReferenceType a) => Type (Reference a) -> Reference x -> Bool
isReferenceTyped a = isJust . coerceReferenceTyped a

class ReferenceType a where
    coerceReference :: Reference x -> Maybe (Reference a)

instance ReferenceType () where
    coerceReference = Just . unsafeReference

instance ReferenceType Plane where
    coerceReference (PlaneRef ref) = Just $ PlaneRef ref
    coerceReference _ = Nothing

instance ReferenceType Tool where
    coerceReference (ToolRef ref) = Just $ ToolRef ref
    coerceReference _ = Nothing

instance ReferenceType Creature where
    coerceReference (CreatureRef ref) = Just $ CreatureRef ref
    coerceReference _ = Nothing

instance ReferenceType Building where
    coerceReference (BuildingRef ref) = Just $ BuildingRef ref
    coerceReference _ = Nothing

instance ReferenceType TheUniverse where
    coerceReference UniverseRef = Just UniverseRef
    coerceReference _ = Nothing

--
-- Locations
--
generalizeLocation :: Location e t -> Location (Reference ()) ()
generalizeLocation = unsafeLocation

generalizeParent :: Location e t -> Location e ()
generalizeParent = unsafeLocation

generalizeChild :: Location e t -> Location (Reference ()) t
generalizeChild = unsafeLocation

genericParent :: Location e t -> Reference ()
genericParent (IsStanding _ s) = unsafeReference $ standing_plane s
genericParent (IsDropped _ d) = unsafeReference $ dropped_plane d
genericParent (InInventory _ c) = unsafeReference $ inventory_creature c
genericParent (IsWielded _ c) = unsafeReference $ wielded_creature c
genericParent (IsConstructed _ c) = unsafeReference $ constructed_plane c
genericParent (InTheUniverse _) = unsafeReference UniverseRef
genericParent (IsSubsequent _ b) = unsafeReference $ subsequent_to b
genericParent (IsBeneath _ b) = unsafeReference $ beneath_of b

genericChild :: Location e t -> Reference ()
genericChild (IsStanding r _) = unsafeReference r
genericChild (IsDropped r _) = unsafeReference r
genericChild (InInventory r _) = unsafeReference r
genericChild (IsWielded r _) = unsafeReference r
genericChild (IsConstructed r _) = unsafeReference r
genericChild (InTheUniverse r) = unsafeReference r
genericChild (IsSubsequent r _) = unsafeReference r
genericChild (IsBeneath r _) = unsafeReference r

asLocationTyped :: (LocationChild e,LocationParent t) =>
                   Type e -> Type t -> Location e t -> Location e t
asLocationTyped _ _ = id

asType :: Type e -> e -> e
asType _ = id

coerceParentTyped :: (LocationParent p) =>
                       Type p -> Location c x -> Maybe (Location c p)
coerceParentTyped = const coerceParent

isParentTyped :: (LocationParent p) => Type p -> Location c x -> Bool
isParentTyped t = isJust . coerceParentTyped t

coerceChildTyped :: (LocationChild c) =>
                     Type c -> Location x p -> Maybe (Location c p)
coerceChildTyped = const coerceChild

isChildTyped :: (LocationChild c) => Type c -> Location x p -> Bool
isChildTyped t  = isJust . coerceChildTyped t

coerceParent :: forall c x p. (LocationParent p) =>
                Location c x -> Maybe (Location c p)
coerceParent l =
    do (_ :: p) <- extractParent l
       return $ unsafeLocation l

coerceChild :: forall x p c. (LocationChild c) =>
                Location x p -> Maybe (Location c p)
coerceChild l =
    do (_ :: c) <- extractChild l
       return $ unsafeLocation l

coerceLocationRecord :: forall x y c p. (LocationChild c,LocationParent p) =>
                        Location x y -> Maybe (Location c p)
coerceLocationRecord l =
    do (_ :: p) <- extractParent l
       (_ :: c) <- extractChild l
       return $ unsafeLocation l

parent :: (LocationParent p) => Location c p -> p
parent l = fromMaybe (error "parent: type error") $ extractParent l

child :: (LocationChild c) => Location c p -> c
child l = fromMaybe (error "child: type error") $ extractChild l

class (Eq a,Ord a) => LocationParent a where
    extractParent :: Location e t -> Maybe a

class (Eq a,Ord a) => LocationChild a where
    extractChild :: Location e t -> Maybe a

instance LocationParent Standing where
    extractParent (IsStanding _ s) = Just s
    extractParent _ = Nothing

instance LocationParent Dropped where
    extractParent (IsDropped _ d) = Just d
    extractParent _ = Nothing

instance LocationParent Inventory where
    extractParent (InInventory _ i) = Just i
    extractParent _ = Nothing

instance LocationParent Wielded where
    extractParent (IsWielded _ i) = Just i
    extractParent _ = Nothing

instance LocationParent Constructed where
    extractParent (IsConstructed _ i) = Just i
    extractParent _ = Nothing

instance LocationParent TheUniverse where
    extractParent (InTheUniverse {}) = Just TheUniverse
    extractParent _ = Nothing

instance LocationParent Subsequent where
    extractParent (IsSubsequent _ i) = Just i
    extractParent _ = Nothing

instance LocationParent Beneath where
    extractParent (IsBeneath _ i) = Just i
    extractParent _ = Nothing

instance LocationParent () where
    extractParent = const $ Just ()

instance LocationParent Position where
    extractParent (IsStanding _ s) = Just $ standing_position s
    extractParent (IsDropped _ d) = Just $ dropped_position d
    extractParent (InInventory {}) = Nothing
    extractParent (IsWielded {}) = Nothing
    extractParent (IsConstructed _ c) = Just $ constructed_position c
    extractParent (InTheUniverse {}) = Nothing
    extractParent (IsSubsequent {}) = Nothing
    extractParent (IsBeneath {}) = Nothing

instance LocationParent MultiPosition where
    extractParent (IsConstructed _ c) = Just $ multiPosition (constructed_position c) (buildingOccupies $ constructed_type c)
    extractParent x = fmap (toMultiPosition :: Position -> MultiPosition) $ extractParent x

instance LocationParent Facing where
    extractParent (IsStanding _ s) = Just $ standing_facing s
    extractParent (IsDropped {}) = Nothing
    extractParent (InInventory {}) = Nothing
    extractParent (IsWielded {}) = Nothing
    extractParent (IsConstructed {}) = Nothing
    extractParent (InTheUniverse {}) = Nothing
    extractParent (IsSubsequent {}) = Nothing
    extractParent (IsBeneath {}) = Nothing

instance ReferenceType a => LocationParent (Reference a) where
    extractParent = coerceReference . genericParent

instance ReferenceType a => LocationChild (Reference a) where
    extractChild = coerceReference . genericChild

instance (LocationParent a,LocationParent b) => LocationParent (a,b) where
    extractParent l = liftM2 (,) (extractParent l) (extractParent l)

instance (LocationChild a,LocationChild b) => LocationChild (a,b) where
    extractChild l = liftM2 (,) (extractChild l) (extractChild l)

--
-- Manipulating Locations
--
toStanding :: (LocationParent t) =>
              Standing ->
              Location CreatureRef t ->
              Location CreatureRef Standing
toStanding s l | isChildTyped _creature l = IsStanding (child l) s
toStanding _ _ = error "toStanding: type error"

toDropped :: (LocationParent t) => Dropped -> Location ToolRef t -> Location ToolRef Dropped
toDropped d l | isChildTyped _tool l = IsDropped (child l) d
toDropped _ _ = error "toDropped: type error"

toInventory :: (LocationParent t) => Inventory -> Location ToolRef t -> Location ToolRef Inventory
toInventory i l | isChildTyped _tool l = InInventory (child l) i
toInventory _ _ = error "toInventory: type error"

toWielded :: (LocationParent t) => Wielded -> Location ToolRef t -> Location ToolRef Wielded
toWielded i l | isChildTyped _tool l = IsWielded (child l) i
toWielded _ _ = error "toWielded: type error"

returnToInventory :: Location ToolRef Wielded -> Location ToolRef Inventory
returnToInventory l = InInventory (child l) (Inventory c)
    where Wielded c = parent l

shuntToTheUniverse :: Type p ->
                      Location PlaneRef p ->
                      Location PlaneRef TheUniverse
shuntToTheUniverse _ l = InTheUniverse (child l)