module Language.Syntactic.Sharing.StableName where

import Control.Monad.IO.Class
import Data.IntMap as Map
import Data.IORef
import Data.Typeable
import System.Mem.StableName
import Unsafe.Coerce

import Language.Syntactic
import Language.Syntactic.Sharing.Graph

-- | 'StableName' of a (@c (`Full` a)@) with hidden result type
data StName c
    StName :: Typeable a => StableName (c (Full a)) -> StName c

stCast :: forall a b c . (Typeable a, Typeable b) =>
    StableName (c (Full a)) -> Maybe (StableName (c (Full b)))
stCast a
    | ta==tb    = Just (unsafeCoerce a)
    | otherwise = Nothing
    ta = typeOf (undefined :: a)
    tb = typeOf (undefined :: b)

instance Eq (StName c)
    StName st1 == StName st2 = case stCast st1 of
        Just st1' -> st1'==st2
        _         -> False

hash :: StName c -> Int
hash (StName st) = hashStableName st

-- 'History' implements a hash table from 'StName' to 'NodeId' (with 'hash' as
-- the hashing function). I.e. it is assumed that the 'StName's at each entry
-- all have the same 'hash', and that this number is equal to the entry's key.
type History c = IntMap [(StName c, NodeId)]

lookHistory :: History c -> StName c -> Maybe NodeId
lookHistory hist st = case Map.lookup (hash st) hist of
    Nothing   -> Nothing
    Just list -> Prelude.lookup st list

remember :: StName c -> NodeId -> History c -> History c
remember st n hist = insertWith (++) (hash st) [(st,n)] hist

-- | Return a fresh identifier from the given supply
fresh :: (Enum a, MonadIO m) => IORef a -> m a
fresh aRef = do
    a <- liftIO $ readIORef aRef
    liftIO $ writeIORef aRef (succ a)
    return a