{-# LANGUAGE CPP #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RecordWildCards #-}

#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif

import Banner
import System.Environment
import Control.Exception
import Control.Monad
import Control.Concurrent
import Data.IORef
import Control.Concurrent.STM
import Halive.SubHalive
import Halive.Recompiler
import Halive.Args
import System.FilePath

main :: IO ()
main = do
    args <- parseArgs <$> getArgs
    case args of
        Nothing -> putStrLn usage
        Just Args {..} -> do
            let mainFilePath = dropFileName mainFileName
            setEnv "Halive Active" "Yes"
            putStrLn banner
            withArgs targetArgs $
                startRecompiler (fileTypes ++ defaultFileTypes) mainFileName
                    (mainFilePath:includeDirs)
                    shouldCompile

defaultFileTypes :: [FileType]
defaultFileTypes = ["hs", "pd", "frag", "vert"]

printBanner :: String -> IO ()
printBanner title = putStrLn $ ribbon ++ " " ++ title ++ " " ++ ribbon
    where ribbon = replicate 25 '*'

startRecompiler :: [FileType] -> FilePath -> [FilePath] -> Bool -> IO b
startRecompiler fileTypes mainFileName includeDirs shouldCompile = do
    ghc <- startGHC
        (defaultGHCSessionConfig
            { gscImportPaths = includeDirs
            , gscCompilationMode = if shouldCompile then Compiled else Interpreted
            , gscUseColor = True
            })

    recompiler <- recompilerWithConfig ghc RecompilerConfig
        { rccWatchAll = Just (".", fileTypes)
        , rccExpressions = ["main"]
        , rccFilePath = mainFileName
        }

    mainThreadId <- myThreadId

    newCodeTChan <- newTChanIO
    isMainRunning <- newIORef False
    _ <- forkIO $ forever $ do
        result <- atomically $ readTChan (recResultTChan recompiler)
        case result of
            Left errors -> do
                printBanner "Compilation Errors, Waiting...     "
                putStrLn errors
            Right values -> do
                printBanner "Compilation Success, Relaunching..."
                case values of
                    [newCode] -> do
                        atomically $ writeTChan newCodeTChan newCode
                        mainIsRunning <- readIORef isMainRunning
                        when mainIsRunning $ killThread mainThreadId
                    _ ->
                        error "Unexpected number of values received on recResultTChan"

    forever $ do
        newCode <- atomically $ readTChan newCodeTChan
        case getCompiledValue newCode of
            Just (mainFunc :: IO ()) -> do
                writeIORef isMainRunning True
                mainFunc `catch` (\x ->
                    putStrLn ("App killed: " ++ show (x :: SomeException)))
                writeIORef isMainRunning False
            Nothing -> do
                putStrLn "main was not of type IO ()"