content: module IdPropCaching (
      MonadIdPropCaching(..)
    , modifyIdPropCache
    , extendIdPropCache
    , recordIdPropType
    ) where
  
  import Control.Applicative ((<|>))
  import System.IO.Unsafe (unsafePerformIO)
  
  import IdeSession.Types.Private (IdPropPtr(..), IdProp(..))
  import IdeSession.Types.Public (Type)
  import IdeSession.Strict.IORef
  import IdeSession.Strict.Container
  import qualified IdeSession.Strict.IntMap as IntMap
  import qualified IdeSession.Strict.Maybe  as Maybe
  
  import GHC (Ghc)
  import MonadUtils
  
  class Monad m => MonadIdPropCaching m where
    getIdPropCache :: m (Strict IntMap IdProp)
    putIdPropCache :: Strict IntMap IdProp -> m ()
  
  idPropCacheRef :: StrictIORef (Strict IntMap IdProp)
  {-# NOINLINE idPropCacheRef #-}
  idPropCacheRef = unsafePerformIO $ newIORef IntMap.empty
  
  instance MonadIdPropCaching IO where
    getIdPropCache = readIORef  idPropCacheRef
    putIdPropCache = writeIORef idPropCacheRef
  
  instance MonadIdPropCaching Ghc where
    getIdPropCache = liftIO $ getIdPropCache
    putIdPropCache = liftIO . putIdPropCache
  
  modifyIdPropCache :: MonadIdPropCaching m => IdPropPtr -> (IdProp -> IdProp) -> m ()
  modifyIdPropCache ptr f = do
    cache <- getIdPropCache
    putIdPropCache $ IntMap.adjust f (idPropPtr ptr) cache
  
  extendIdPropCache :: MonadIdPropCaching m => IdPropPtr -> IdProp -> m ()
  extendIdPropCache ptr prop = do
      cache <- getIdPropCache
      putIdPropCache $ IntMap.insertWith update (idPropPtr ptr) prop cache
    where
      -- We need to make sure not to lose information that we learned earlier
      update :: IdProp -> IdProp -> IdProp
      update new old
        = new { idType       = idType       new <|> idType       old
              , idHomeModule = idHomeModule new <|> idHomeModule old
              }
  
  recordIdPropType :: MonadIdPropCaching m => IdPropPtr -> Type -> m ()
  recordIdPropType ptr tp =
    modifyIdPropCache ptr $ \idInfo -> idInfo {
        idType = Maybe.just tp
      }
  