module Fixer.Supply(
    Supply(),
    newSupply,
    supplyReadValues,
    sValue,
    readSValue,
    supplyValue
    ) where

import Control.Monad.Trans
import Data.IORef
import Data.Typeable
import Fixer.Fixer
import qualified Data.Map as Map

-- maps b's to values of a's, creating them as needed.

data Supply b a = Supply Fixer {-# UNPACK #-} !(IORef (Map.Map b (Value a)))
    deriving(Typeable)

newSupply :: MonadIO m => Fixer -> m (Supply b a)
newSupply fixer = liftIO $ do
    ref <- newIORef Map.empty
    return $ Supply fixer ref

supplyValue :: (MonadIO m, Ord b, Fixable a) => Supply b a -> b -> m (Value a)
supplyValue (Supply fixer ref) b = liftIO $ do
    mp <- readIORef ref
    case Map.lookup b mp of
        Just v -> return v
        Nothing -> do
            v <- newValue fixer bottom
            modifyIORef ref (Map.insert b v)
            return v

sValue :: (Ord b, Fixable a) => Supply b a -> b -> (Value a)
sValue s b = ioValue (supplyValue s b)

supplyReadValues :: (Fixable a,MonadIO m) => Supply b a -> m [(b,a)]
supplyReadValues (Supply _fixer ref) = liftIO $ do
    mp <- readIORef ref
    flip mapM (Map.toList mp) $ \ (b,va) -> do
        a <- readValue va
        return (b,a)

readSValue :: (MonadIO m, Ord b, Fixable a) => Supply b a -> b -> m a
readSValue s b = do
    v <- supplyValue s b
    readValue v