{-# LANGUAGE CPP, DeriveDataTypeable, TypeSynonymInstances #-}
module Shim.SHM where

import Data.Typeable

#if __GLASGOW_HASKELL__ >= 610
import GHC hiding ( load )
#else
import GHC hiding ( load, newSession )
#endif

import qualified GHC
import HscTypes
import Outputable
import ErrUtils

import qualified Control.OldException as CE
import Control.Monad.State
import qualified Data.ByteString.Char8 as BC
import qualified Data.Map as M

import qualified Data.Digest.Pure.MD5 as MD5

import Shim.SessionMonad
import Shim.Utils
import Shim.GhcCompat
--------------------------------------------------------------
-- SHM Monad
--------------------------------------------------------------


data CompilationResult
  = FileCompiled
  | ImportsOnly
  | PreludeOnly
  | NothingCompiled String
    deriving Show

replaceWith :: CompilationResult -> CompilationResult -> Bool
replaceWith _ FileCompiled = True
replaceWith FileCompiled _ = False
replaceWith _ ImportsOnly = True
replaceWith _ _ = False

type IdData = [(String, String)]

type Hash = MD5.MD5Digest


type CachedMod = (Hash,TypecheckedModule)
type CompBuffer = M.Map FilePath (CompilationResult, IdData, Maybe CachedMod)

type SessionMap = M.Map FilePath Session

data ShimState = ShimState
  { tempSession :: Session,
    sessionMap :: SessionMap,
    compBuffer :: CompBuffer,
    compLogAction :: Severity -> SrcSpan -> PprStyle -> Message -> IO () }
  deriving Typeable


type SHM = StateT ShimState IO

instance SessionMonad SHM where
  getSession = gets tempSession

lookupSession :: FilePath -> SHM (Maybe Session)
lookupSession cabalfile = do m <- gets sessionMap
                             return $ M.lookup cabalfile m

addSession :: FilePath -> Session -> SHM ()
addSession cabalfile ses =
  modify (\s ->
            s{sessionMap=(M.insert cabalfile ses (sessionMap s))})

shmHandle :: (CE.Exception -> SHM a) -> SHM a -> SHM a
shmHandle h m = StateT $ \s ->
                CE.catch (runStateT m s)
                         (\e -> runStateT (h e) s)

shmCatch :: SHM a -> (CE.Exception -> SHM a) -> SHM a
shmCatch = flip shmHandle

getCompBuffer :: SHM CompBuffer
getCompBuffer = gets compBuffer

addCompBuffer :: FilePath -> IdData -> CompilationResult -> Maybe (Hash,TypecheckedModule) -> SHM ()
addCompBuffer sourcefile id_data compilation_result checked_mod =
  modify (\s ->
            s{compBuffer=(M.insert sourcefile (compilation_result,id_data,checked_mod)
                                   (compBuffer s))})

io :: IO a -> SHM a
io = lift

runSHM :: Session -> FilePath -> (Severity -> SrcSpan -> PprStyle -> Message -> IO ()) -> SHM a -> IO a
runSHM ses ghc compLogAction m = fmap fst $ runStateT m
                             $ ShimState {tempSession=ses,
                                          sessionMap=M.empty,
                                          compBuffer=M.empty,
                                          compLogAction=compLogAction}

logInfo :: String -> SHM ()
logInfo = io . logS

getCompLogAction :: SHM (Severity -> SrcSpan -> PprStyle -> Message -> IO ())
getCompLogAction = gets compLogAction
