-- | Runtime metadata functions, part of the 
-- RPC mechanism
module Remote.Reg (
         registerCalls,
         Lookup,
         Identifier,
         putReg,
         getEntryByIdent,
         empty,
         RemoteCallMetaData
           ) where

import Data.Dynamic (Dynamic,toDyn,fromDynamic)
import Data.Typeable (Typeable)
import qualified Data.Map as Map (insert,lookup,Map,empty)

----------------------------------------------
-- * Runtime metadata
------------------------------

-- | Data of this type is generated at compile-time
-- by 'remotable' and can be used with 'registerCalls'
-- and 'remoteInit' to create a metadata lookup table, 'Lookup'.
-- The name '__remoteCallMetaData' will be present
-- in any module that uses 'remotable'.
type RemoteCallMetaData = Lookup -> Lookup


type Identifier = String

data Entry = Entry {
               entryName :: Identifier,
               entryFunRef :: Dynamic
             }

-- | Creates a metadata lookup table based on compile-time metadata.
-- You probably don't want to call this function yourself, but instead
-- use 'Remote.Init.remoteInit'.
registerCalls :: [RemoteCallMetaData] -> Lookup
registerCalls [] = empty
registerCalls (h:rest) = let registered = registerCalls rest
                          in h registered

makeEntry :: (Typeable a) => Identifier -> a -> Entry
makeEntry ident funref = Entry {entryName=ident, entryFunRef=toDyn funref}

type IdentMap = Map.Map Identifier Entry
data Lookup = Lookup { identMap :: IdentMap }

putReg :: (Typeable a) => a -> Identifier -> Lookup -> Lookup
putReg a i l = putEntry l a i

putEntry :: (Typeable a) => Lookup -> a -> Identifier -> Lookup
putEntry amap value name = 
                             Lookup {
                                identMap = Map.insert name entry (identMap amap)
                             }
  where
       entry = makeEntry name value


getEntryByIdent :: (Typeable a) => Lookup -> Identifier -> Maybe a
getEntryByIdent amap ident = (Map.lookup ident (identMap amap)) >>= (\x -> fromDynamic (entryFunRef x))

empty :: Lookup
empty = Lookup {identMap = Map.empty}