{-# LANGUAGE CPP #-}
import GHC.Paths
import GHC
import DynFlags
import Linker
import Control.Monad.IO.Class
import Data.Time.Clock.POSIX
import Data.Time
import StringBuffer
import Data.Dynamic
import System.Directory
import System.FilePath

logIO :: MonadIO m => String -> m ()
logIO = liftIO . putStrLn

withGHC :: Ghc a -> IO a
withGHC action = defaultErrorHandler defaultFatalMessager defaultFlushOut $ runGhc (Just libdir) $ do

    packageIDs <-
            getSessionDynFlags
        >>= (\d -> pure d
            { ghcLink     = LinkInMemory
            , ghcMode     = CompManager
            , hscTarget   = HscAsm
            , optLevel    = 2
            , verbosity   = 0
            })
        -- turn off the GHCi sandbox
        -- since it breaks OpenGL/GUI usage
        >>= (pure . (`gopt_unset` Opt_GhciSandbox))
        >>= (pure . (if dynamicGhc then addWay' WayDyn else id))
        -- We must call setSessionDynFlags before calling initPackages or any other GHC API
        >>= setSessionDynFlags

    getSession >>= \hscEnv ->
        liftIO $ linkPackages hscEnv packageIDs
    liftIO . initDynLinker =<< getSession

    action

fileContentsStringToBuffer :: (MonadIO m) => String -> m (StringBuffer, UTCTime)
fileContentsStringToBuffer fileContents = do
    now <- liftIO getCurrentTime
    return (stringToStringBuffer fileContents, now)

ourFile :: String
ourFile = unlines
    [ "main = print $ 123456789"
    ]

main :: IO ()
main = withGHC $ do
    logIO ""
    logIO "Starting..."

    let expression = "main"
    fileContents <- fileContentsStringToBuffer ourFile

    -- Set the target

    -- Create a dummy temporary file to sate GHC's desires for one,
    -- even though we're passing it the text as a buffer.
    tempDir <- liftIO $ getTemporaryDirectory
    now <- show . diffTimeToPicoseconds . realToFrac <$> liftIO getPOSIXTime
    let tempFile = tempDir </> "halive_" ++ now <.> "hs"
    liftIO $ writeFile tempFile ""

    target <- guessTarget tempFile Nothing

    logIO "Setting targets..."
    setTargets [target { targetContents = Just fileContents }]

    -- logIO "Dep analysis..."
    -- graph <- depanal [mkModuleName "Main"] False

    -- Reload the main target
    logIO "Loading..."
    -- setContext $ [ IIModule . mkModuleName $ "Main" ]
    loadSuccess <- load LoadAllTargets

    if succeeded loadSuccess
        then do

            logIO "Analyzing deps..."
            -- Get the dependencies of the main target (and update the session with them)
            graph <- depanal [] False
            -- -- We must parse and typecheck modules before they'll be available for usage
            -- forM_ graph (typecheckModule <=< parseModule)

            #if __GLASGOW_HASKELL__ >= 804
            let modSummaries = mgModSummaries graph
            #else
            let modSummaries = graph
            #endif

            -- Load the dependencies of the main target
            setContext
                (IIDecl . simpleImportDecl . ms_mod_name <$> modSummaries)

            -- Compile the expression and return the result
            result <- dynCompileExpr expression

            case fromDynamic result of
                Just a -> liftIO (a :: IO ())
                Nothing -> return ()
            -- liftIO (print result)
        else do
            return ()