{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell   #-}

-- | This whole module is basically a huge hack that
-- compiles a haskell module and returns the expression
-- bound to the top-level, non-recursive `expr` binding.
module Analyses.Syntax.MkCoreFromFile where

import           Control.Monad.IO.Class
import           Data.Maybe                         (fromMaybe)
import           Data.Monoid                        (First (..))
import qualified Data.Text                          as Text
import           Distribution.Simple.LocalBuildInfo
import           Distribution.Simple.Toolkit        (getGHCPackageDBFlags,
                                                     localBuildInfoQ)
import           Prelude                            hiding (FilePath)
import qualified Prelude
import           System.Directory                   (canonicalizePath)
import           System.FilePath                    (splitSearchPath)
import           Turtle

import           CoreSyn
import           CoreTidy                           (tidyExpr)
import           DynFlags
import           GHC
import qualified GHC.Paths
import           Id
import           Name
import           Packages
import           VarEnv                             (emptyTidyEnv)

findTopLevelDecl :: String -> CoreProgram -> Maybe CoreExpr
findTopLevelDecl occ = getFirst . foldMap (First . findName)
  where
    findName (NonRec id_ rhs)
      | occNameString (occName (idName id_)) == occ
      = Just rhs
    findName _ = Nothing


compileCoreExpr :: Prelude.FilePath -> IO CoreExpr
compileCoreExpr modulePath = runGhc (Just GHC.Paths.libdir) $ do
  -- Don't generate any artifacts
  _ <- getSessionDynFlags >>= setSessionDynFlags . (\df -> df { hscTarget = HscNothing })
  -- Set up the package database

  addPkgDbs $(localBuildInfoQ)
  m <- liftIO (canonicalizePath modulePath) >>= compileToCoreModule
  pure $
    tidyExpr emptyTidyEnv
    -- . pprTraceIt "expr"
    . fromMaybe (error "Could not find top-level non-recursive binding `expr`")
    . findTopLevelDecl "expr"
    . cm_binds
    $ m


stackPkgDbs :: MonadIO m => m (Maybe [Prelude.FilePath])
stackPkgDbs = do
  (ec, paths) <- procStrict "stack" ["path", "--ghc-package-path"] mempty
  if ec == ExitSuccess
    then pure (Just (splitSearchPath (Text.unpack paths)))
    else pure Nothing


-- | Add a list of package databases to the Ghc monad.
addPkgDbs :: (MonadIO m, GhcMonad m) => LocalBuildInfo -> m ()
addPkgDbs lbi = do
  dfs <- getSessionDynFlags
  let pkgs = getGHCPackageDBFlags lbi
#if MIN_VERSION_Cabal(2,0,0)
  let dfs' = dfs { packageDBFlags = pkgs }
#else
  let dfs' = dfs { extraPkgConfs = (pkgs ++) . extraPkgConfs dfs }
#endif
  _ <- setSessionDynFlags dfs'
  _ <- liftIO $ initPackages dfs'
  return ()
