import Data.List

import Control.Monad
import Language.Haskell.Interpreter
import System.Directory

main :: IO ()
main = do setCurrentDirectory "examples"
          r <- runInterpreter testHint
          case r of
            Left err -> putStrLn $ errorString err
            Right () -> return ()

errorString :: InterpreterError -> String
errorString (WontCompile es) = intercalate "\n" (header : map unbox es)
  where
    header = "ERROR: Won't compile:"
    unbox (GhcError e) = e
errorString e = show e

say :: String -> Interpreter ()
say = liftIO . putStrLn

emptyLine :: Interpreter ()
emptyLine = say ""

-- observe that Interpreter () is an alias for InterpreterT IO ()
testHint :: Interpreter ()
testHint =
    do
      say "Load SomeModule.hs"
      loadModules ["SomeModule.hs"]
      emptyLine
      say "Put the Prelude, Data.Map and *SomeModule in scope"
      say "Data.Map is qualified as M!"
      setTopLevelModules ["SomeModule"]
      setImportsQ [("Prelude", Nothing), ("Data.Map", Just "M")]
      emptyLine
      say "Now we can query the type of an expression"
      let expr1 = "M.singleton (f, g, h, 42)"
      say $ "e.g. typeOf " ++ expr1
      say =<< typeOf expr1
      emptyLine
      say $ "Observe that f, g and h are defined in SomeModule.hs, " ++
            "but f is not exported. Let's check it..."
      exports <- getModuleExports "SomeModule"
      say $ show exports
      emptyLine
      say "We can also evaluate an expression; the result will be a string"
      let expr2 = "length $ concat [[f,g],[h]]"
      say $ "e.g. eval " ++ show expr2
      a <- eval expr2
      say $ show a
      emptyLine
      say "Or we can interpret it as a proper, say, int value!"
      a_int <- interpret expr2 (as :: Int)
      say $ show a_int
      emptyLine
      say "This works for any monomorphic type, even for function types"
      let expr3 = "\\(Just x) -> succ x"
      say $ "e.g. we interpret " ++ expr3 ++
            " with type Maybe Int -> Int and apply it on Just 7"
      fun <- interpret expr3 (as :: Maybe Int -> Int)
      say $ show $ fun (Just 7)
      emptyLine
      say "And sometimes we can even use the type system to infer the expected type (eg Maybe Bool -> Bool)!"
      bool_val <- interpret expr3 infer `ap` return (Just False)
      say $ show $ not bool_val
      emptyLine
      say "Here we evaluate an expression of type string, that when evaluated (again) leads to a string"
      res <- do
        s <- interpret "head $ map show [\"Worked!\", \"Didn't work\"]" infer
        interpret s infer
      say res
      emptyLine
      say "We can also execute statements in the IO monad and bind new names, e.g."
      let stmts = ["x <- return 42", "print x"]
      forM_ stmts $ \s -> do
          say $ "    " ++ s
          runStmt s
      emptyLine