{-# LANGUAGE ScopedTypeVariables #-}
-- | Module implementing loading generic plugins using Hint.
module Data.Aeson.AutoType.Plugin.Loader (
    importPlugins
  , main
  ) where

import           System.IO     ( hPutStrLn, stderr )
import           System.Exit   ( exitFailure )
import           Data.Typeable
import           Control.Arrow ((&&&))
import           Control.Monad
import qualified Language.Haskell.Interpreter as Hint

-- | Test script
main :: IO ()
main  = do result :: [[Int]] <- importPlugins "export" moduleNames
           forM_ (zip moduleNames result) $ \(moduleName, exportVal) ->
             putStrLn $ concat [moduleName, ": ", show exportVal, "\n"]
  where
    moduleNames = ["Plugin.hs"
                  ,"Plugin2.hs"]

-- | Fatal exit from the program
fatal :: String -> IO a
fatal msg = do
  hPutStrLn stderr msg
  exitFailure

-- | Imports a set of plugin modules with the same "interface" value,
-- and returns values exported as their interfaces.
--
-- Arguments:
--  * argument name exported from each module as an interface,
--  * list of plugin modules (given as either module paths or file paths).
--
-- Result:
--  * Result is a list of exported objects in the order of module names.
importPlugins ::   Typeable a =>
                    String    ->
                   [FilePath] ->
                    IO [a]
importPlugins interfaceName pluginModules = do
    result <- Hint.runInterpreter $ do
      Hint.loadModules pluginModules
      moduleNames <- Hint.getLoadedModules
      Hint.setImportsQ $ ("Prelude", Nothing):map (id &&& Just) moduleNames
      mapM getInterfaceVar moduleNames
    case result of
      Left  err   -> fatal $ "Cannot load plugins:\n" ++ show err
      Right value -> return value
  where
    getInterfaceVar moduleName = Hint.interpret (moduleName ++ "." ++ interfaceName) Hint.as