{-|
Module      : Devel.Compile
Description : For building and running your WAI application.
Copyright   : (c)
License     : GPL-3
Maintainer  : njagi@urbanslug.com
Stability   : experimental
Portability : POSIX
-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}

module Devel.Build 
( build
) where


import IdeSession
import qualified Data.ByteString.Char8 as S8
import Data.Text (unpack)

import GHC.Conc (newTVarIO)
import Control.Concurrent (forkIO, killThread, ThreadId)

-- Rebuild
import Control.Monad (unless)
# if __GLASGOW_HASKELL__ < 710
import Data.Monoid (mempty)
#endif

import Devel.Compile
import Devel.ReverseProxy (startReverseProxy)
import Devel.Watch


-- | Compiles and calls run on your WAI application.
build :: FilePath -> String ->  Bool -> SessionConfig -> (Int, Int) -> Maybe IdeSession -> Bool -> IO ()
build buildFile runFunction isReverseProxy sessionConfig (fromProxyPort, toProxyPort) mSession isRebuild = do

  (initialSession, extensionList, includeTargets) <- initCompile sessionConfig mSession

  -- Do this if isRebuild is False.
  unless isRebuild $
    if isReverseProxy then
      do _ <- forkIO $ startReverseProxy (fromProxyPort, toProxyPort)
         putStrLn $ "Starting devel application at http://localhost:" ++
                    show fromProxyPort
    else
      putStrLn $ "Starting app without reverse proxying at http://localhost:" ++
                 show fromProxyPort


  (updatedSession, update) <- 
    if isRebuild
       then return (initialSession, mempty)
       else compile initialSession buildFile extensionList includeTargets

  eitherSession <- finishCompile (updatedSession, update)

  case eitherSession of
    Left _ -> do
      -- Listen for changes in the current working directory.
      isDirty <- newTVarIO False

      _ <- forkIO $ watch isDirty includeTargets

      -- Block until relevant change is made then carry on with program execution.
      _ <- checkForChange isDirty

      -- Stop the current app.
      putStrLn "\n\nRebuilding...\n\n"
      
      _ <- shutdownSession updatedSession

      -- Coming to fix.
      build buildFile runFunction False sessionConfig (fromProxyPort, toProxyPort) Nothing False

    Right session -> do
      -- run the session
      (runActionsRunResult, threadId) <- run session buildFile runFunction

      -- Start watching for file changes.
      isDirty <- newTVarIO False

      -- Watch for changes in the current working directory.
      watchId <- forkIO $ watch isDirty includeTargets

      -- Block until relevant change is made then carry on with program execution.
      _ <- checkForChange isDirty

      killThread watchId
      
      -- Stop the current app.
      _ <- stopApp runActionsRunResult threadId
      putStrLn "\n\nRebuilding...\n\n"
      build buildFile runFunction isReverseProxy sessionConfig (fromProxyPort, toProxyPort) (Just session) True


run :: IdeSession -> FilePath -> String -> IO (RunActions RunResult, ThreadId)
run session buildFile runFunction = do
  -- Get the module name from the file path
  mapFunction <- getFileMap session
  buildModule <- case mapFunction buildFile of
                   Nothing -> fail $ "The file's module name for: " ++ show buildFile ++" couldn't be found"
                   Just moduleId -> return $ unpack $ moduleName moduleId

  -- Run the given ide-backend session.
  runActionsRunResult <- runStmt session buildModule runFunction
  threadId <- forkIO $ loop runActionsRunResult

  return (runActionsRunResult, threadId)
  

-- | Stop the currently running WAI application.
stopApp :: RunActions RunResult -> ThreadId -> IO ()
stopApp runResult threadId = do
  interrupt runResult
  killThread threadId


-- | Run for as long as we need to.
loop :: RunActions RunResult -> IO ()
loop res = do
  runAction <- runWait res
  case runAction of
    Left bs -> S8.putStr bs >> loop res
    Right result -> putStrLn $ "Run result: " ++ show result