{-|
Module      : Angle.Interpreter
Description : File runner for the Angle programming language.
Copyright   : Copyright (C) 2015 Ben Moon
License     : GNU GPL, version 3
Maintainer  : GuiltyDolphin@gmail.com
Stability   : alpha

Defines the file runner for use with the Angle programming language.
-}
module Angle.Interpreter (runInterpreter) where

import Control.Monad
import Control.Monad.Reader
import System.Directory (canonicalizePath)
import System.IO.Error
import System.Exit

import Angle.Options
import Angle.Exec.Builtins
import Angle.Exec.Error
import Angle.Exec.Exec
import Angle.Exec.Types
import Angle.Parse.Parser
import Angle.Scanner
import Angle.Types.Lang (Stmt)


runInterpreter :: Options -> IO ()
runInterpreter opts = do
    let Options { optFiles = files
                , optCode = code
                } = opts

    runWithOptions opts $ runFiles files
    runWithOptions opts $ runCodes code



runFiles :: [FilePath] -> OptionEnv ()
runFiles = mapM_ runFile


runFile :: FilePath -> OptionEnv ()
runFile fp = do
    source' <- liftIO $ tryIOError $ readFile fp
    case source' of
        Left e -> handleNoFile fp e
        Right source -> runLex (Just fp) source
  where
    -- runLex source =
    --     case evalParse source program of
    --         Left err -> handleSyntax (Just fp) err abort
    --         Right toExec -> executeProg toExec
    -- executeProg toExec = do
    --     res <- runExecIOEnv initialEnvMain (execStmt toExec)
    --     case res of
    --         Left err -> handleRuntime (Just fp) err abort
    --         Right _ -> return ()


runLex :: Maybe FilePath -> String -> OptionEnv ()
runLex fp source =
    case evalParse source program of
        Left err -> handleSyntax fp err
        Right toExec -> executeProg fp source toExec


executeProg :: Maybe FilePath -> String -> Stmt -> OptionEnv ()
executeProg fp source toExec = do
    paths <- liftM optSearchPath ask
    currPath <- case fp of
                 Nothing -> return Nothing
                 Just pth -> liftM Just $ liftIO $ canonicalizePath pth
    let execEnv = initialEnvMain { sourceText = source
                                 , angleLibPath = paths
                                 , currentFile = currPath
                                 }
    res <- liftIO $ runExecIOEnv execEnv (execStmt toExec)
    case res of
        Left err -> handleRuntime fp err
        Right _ -> return ()


runCodes :: [String] -> OptionEnv ()
runCodes = mapM_ runCode


runCode :: String -> OptionEnv ()
runCode = runLex Nothing


handleSyntax :: Maybe FilePath -> ParseError -> OptionEnv ()
handleSyntax = handleErr "Syntax error" 100


handleRuntime :: Maybe FilePath -> AngleError -> OptionEnv ()
handleRuntime = handleErr "Runtime error" 101



fileError :: Maybe FilePath -> String
fileError fp = case fp of
                (Just f) -> " in file " ++ f
                Nothing -> ""


handleErr :: (Show e) => String -> Int -> Maybe FilePath -> e -> OptionEnv ()
handleErr errMsgStart errCode fp err = do
    abort <- liftM optAbort ask
    liftIO $ putStrLn (errMsgStart ++ fileError fp) >> print err
    liftIO $ when abort $ exitWith $ ExitFailure errCode


handleNoFile :: FilePath -> IOError -> OptionEnv ()
handleNoFile fp e | isDoesNotExistError e = handleNoFile'
                  | otherwise = liftIO $ ioError e
  where
    handleNoFile' = do
        abort <- liftM optAbort ask
        liftIO $ putStrLn $ "No such file: " ++ fp
        liftIO $ when abort $ exitWith $ ExitFailure 102
