module Main where
  
import System (getArgs)
import System.Console.GetOpt
import System.FilePath (FilePath, dropExtension, addExtension, takeBaseName)
import System.Cmd (rawSystem)
import Control.Monad.Trans (liftIO)
import Data.Maybe (fromMaybe, isJust)

import Parser (parseStr)
import LPTypechecker
import qualified Compiler as C
import Preprocessor
import LLVMUtils (writeBitcodeToFile)

import Debug.Trace (trace)

data Options = Options { libDir :: Maybe FilePath, outputFile :: Maybe FilePath,
                         prelude :: Maybe FilePath, fastArithmetic :: Bool }
defaultOptions = Options { libDir = Nothing, outputFile = Nothing, prelude = Nothing,
                           fastArithmetic = False }

options = [ Option ['L'] ["libdir"]
            (ReqArg (\d opts -> opts { libDir = Just d }) "DIR")
              "Library directory, containing external primitive functions",
            Option ['o'] ["output"]
            (ReqArg (\f opts -> opts {outputFile = Just f}) "OUTPUT")
              "Path to output file",
            Option [] ["fast-math"] (NoArg (\opts -> opts { fastArithmetic = True }))
              "Use built-in operators for arithmetic functions",
            Option ['p'] ["prelude"]
            (ReqArg (\p opts -> opts {prelude = Just p}) "PRELUDE")
              "Include a particular prelude file (default is none)"
          ]

main = do
  args <- getArgs
  case getOpt Permute options args of
      (_, f:f':[], []) -> error "multiple source files specified"
      (opts, [file], []) -> processFile (foldl (flip id) defaultOptions opts)
                                        file
      (_, _, errors) -> error $ unlines errors

processFile opts f = do
  preludeStr <- case prelude opts of
                  Nothing -> return ""
                  Just p  -> readFile p
  fileStr <- readFile f
  let str = preludeStr ++ fileStr
  case buildAST f str of
    Left err -> error err
    Right prog -> compile opts f prog
  where buildAST f str = do
          parseTree  <- parseStr f str
          annotated  <- typecheck parseTree
          return $ createClosures $ preprocess (fastArithmetic opts) annotated

-- Take a .lambda file and produce a native executable of the same name. Also
-- creates an intermediate .bc file, and links against main.bc for the entry
-- point.
compile opts fileName prog = do
    (code, entryPoint) <- C.compile (dropExtension fileName) prog
    let bitcodeFile = addExtension (dropExtension fileName) ".bc"
    writeBitcodeToFile bitcodeFile code
    let executable = fromMaybe (takeBaseName fileName) (outputFile opts)
    let lib = fromMaybe "./lib" (libDir opts)
    rawSystem "llvm-ld" [bitcodeFile, lib++"/main.bc", lib++"/fold.o",
                         "-disable-opt", "-native", "-o", executable]
