module Main where

import Acid                (withAcid)
import Control.Concurrent  (forkIO, killThread)
import Control.Monad       (msum)
import Extra.IOThread
import Happstack.Server (Browsing(..), Conf(validator), nullConf, seeOther, simpleHTTP, toResponse, dir, serveDirectory, notFound)
import System.Console.GetOpt (ArgDescr(NoArg, ReqArg), ArgOrder(Permute), OptDescr(Option), getOpt)
import System.Directory (createDirectoryIfMissing)
import System.Environment (getArgs, getProgName)
import System.Exit (exitFailure)
import System.FilePath((</>))
import System.IO (stdout)
import System.Log.Logger (Priority(DEBUG, INFO, ERROR), logM, rootLoggerName, setHandlers, setLevel, updateGlobalLogger)
import System.Log.Handler.Simple (fileHandler, streamHandler)
import State
import Types
import Server (Config(..))
import Web (webImpl)

main :: IO ()
main = 
    do -- progname effects where state is stored and what the logfile is named
       progName <- getProgName
       args <- getArgs

       -- parse the command-line options
       appConf <- case parseConfig args of
               (Left e) -> do logM progName ERROR (unlines e)
                              exitFailure
               (Right f) -> return (f $ defaultConf)

       -- create image store and cache directories if missing
       createDirectoryIfMissing True (imageStore appConf)
       createDirectoryIfMissing True (imageCache appConf)

       -- setup the log files
       setupLogger progName (logs appConf) (logMode appConf)

       -- start the image thumbnailing loop
       (filterId, filterChan) <- startIOThread (applyTransforms (imageStore appConf) (imageCache appConf))

       -- start the system state
       accts <- genAccts
       withAcid  (initialBoardState accts) (Just $ store appConf </> "state") $ \acid ->
        do tid <- forkIO $ simpleHTTP (httpConf appConf) $ msum 
                  [ dir "favicon.ico" $ notFound (toResponse ())
                  , dir "jquery"    $ serveDirectory DisableBrowsing [] "/usr/share/javascript/jquery/"
                  , dir "jquery-ui" $ serveDirectory DisableBrowsing [] "/usr/share/javascript/jquery-ui/"
                  , dir "theme"     $ serveDirectory DisableBrowsing [] (static appConf)
                  , webImpl (Config { storageDir = (imageStore appConf)
                                    , cacheDir   = (imageCache appConf)
                                    , ioThread   = filterChan
                                    , appSecret  = "1234"
                                    , role       = User
                                    , acid       = acid
                                    })
                  , seeOther "/web" (toResponse "/web")
                  ]
           _ <- getLine
           --       waitForTermination
           killThread tid
           killThread filterId

data AppConf
    = AppConf { httpConf   :: Conf
              , store      :: FilePath
              , static     :: FilePath 
              , imageStore :: FilePath
              , imageCache :: FilePath
              , logs       :: FilePath
              , logMode    :: LogMode
              }

defaultConf :: AppConf
defaultConf
    = AppConf { httpConf = nullConf -- nullConf { port = maybe 80 (read . drop 1 . uriPort) $ uriAuthority (connectURL facebookConfig)  }
              , store      = "_local"
              , static     = "theme"
              , imageStore = "_local/imageStore"
              , imageCache = "_local/imageCache"
              , logs       = "logs"
              , logMode    = Development
              }

opts :: [OptDescr (AppConf -> AppConf)]
opts = [ -- Option [] ["http-port"]   (ReqArg (\h c -> c { httpConf = (httpConf c) {port = read h} }) "port") "port to bind http server"
         Option [] ["no-validate"] (NoArg (\ c -> c { httpConf = (httpConf c) { validator = Nothing } })) "Turn off HTML validation"
       , Option [] ["store"]       (ReqArg (\h c -> c {store = h}) "PATH") "The directory used for database storage."
       , Option [] ["static"]      (ReqArg (\h c -> c {static = h}) "PATH") "The directory searched for static files" 
       , Option [] ["image-store"]       (ReqArg (\h c -> c {imageStore = h}) "PATH") "The directory used for upload images."
       , Option [] ["image-cache"]       (ReqArg (\h c -> c {imageCache = h}) "PATH") "The directory used for thumbnails and other cached image transformations."
       , Option [] ["logs"]        (ReqArg (\h c -> c {logs = h}) "PATH") "The directory to store log files in"
       , Option [] ["log-mode"]    (ReqArg (\h c -> c {logMode = read h}) (show ([minBound .. maxBound] :: [LogMode]))) "The logging mode to use"
       ]

parseConfig :: [String] -> Either [String] (AppConf -> AppConf)
parseConfig args
    = case getOpt Permute opts args of
        (flags,_,[]) -> Right $ \appConf -> foldr ($) appConf flags
        (_,_,errs)   -> Left errs

data LogMode
    = Production
    | Development
      deriving (Read, Show, Eq, Ord, Enum, Bounded)

setupLogger :: String -> FilePath -> LogMode -> IO ()
setupLogger progName logDir logMode = do
    createDirectoryIfMissing True logDir
    appLog    <- fileHandler (logDir </> (progName ++ "_root.log"))   DEBUG
    accessLog <- fileHandler (logDir </> (progName ++ "_access.log")) INFO
    stdoutLog <- streamHandler stdout DEBUG

    case logMode of
      Development -> do
          -- Root Log
          updateGlobalLogger rootLoggerName 
            (setLevel DEBUG . setHandlers [appLog, stdoutLog])
          -- Access Log
          updateGlobalLogger "Happstack.Server.AccessLog.Combined" 
            (setLevel INFO . setHandlers [accessLog])

      Production -> do
          -- Root Log
          updateGlobalLogger rootLoggerName 
            (setLevel INFO . setHandlers [appLog])
          -- Access Log
          updateGlobalLogger "Happstack.Server.AccessLog.Combined" 
            (setLevel INFO . setHandlers [accessLog])
