{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
{-|
 -}
module LivingFlame.Monad (
  LivingFlameEnv (..), LivingFlame, 
  mkDefaultEnv, 
  runLivingFlame, 
  --
  ask, asks, 
  --
  putLog, rotateLog, 
  --
  getConfig, getConfigDefault) where

import qualified Blaze.ByteString.Builder as BZ
import qualified Blaze.ByteString.Builder.Char.Utf8 as BZ
import Control.Applicative ((<$>))
import Control.Monad.Trans (liftIO)
import Control.Monad.Trans.Reader (ReaderT, asks, ask, runReaderT)
import Control.Monad.Trans.Resource (ResourceT, runResourceT)
import Data.Monoid (mappend)
import Control.Concurrent.MVar (MVar, withMVar, newMVar, modifyMVar_)
import Control.Concurrent.Chan.Lifted (Chan, newChan)
import Control.Concurrent (ThreadId)
import Data.Text (Text)
import Data.Time (UTCTime,formatTime,getCurrentTime)
import qualified Data.Configurator as CF
import qualified Data.Configurator.Types as CF
import System.IO (Handle, openFile, IOMode (..), hClose)
import System.Locale (defaultTimeLocale)
import System.FilePath ((</>))
import System.Directory (setCurrentDirectory)

import System.Log.FastLogger



data LivingFlameEnv
  = LivingFlameEnv {
      systemBaseDir :: FilePath,
      systemStartupTimestamp :: UTCTime,
      systemLogSpec :: FileLogSpec,
      systemLogHandle :: MVar Handle,
      systemRunningMonitors :: MVar [ThreadId],
      systemMonitorChan :: Chan Text,
      systemConfig :: (CF.Config, ThreadId)
  }
type LivingFlame = ReaderT LivingFlameEnv (ResourceT IO)


mkDefaultEnv :: String -> IO LivingFlameEnv
mkDefaultEnv baseDir = do
  setCurrentDirectory baseDir
  tm <- getCurrentTime
  let logpath = baseDir </> "logs" </> "LivingFlame.log"

  -- 環境を構築して、LivingFlameモナドを実行
  logh <- openFile logpath AppendMode >>= newMVar
  mons <- newMVar []
  monchan <- newChan
  cfg <- CF.autoReload CF.autoConfig [CF.Required "config"]
  return $ LivingFlameEnv {
               systemBaseDir = baseDir,
               systemStartupTimestamp = tm,
               systemLogSpec = FileLogSpec {
                   log_file = logpath,
                   log_file_size = 128 * 1024 * 1024,
                   log_backup_number = 9
               },
               systemLogHandle = logh,
	       systemRunningMonitors = mons,
	       systemMonitorChan = monchan,
               systemConfig = cfg
             }
 

runLivingFlame :: LivingFlame a -> LivingFlameEnv -> IO a
runLivingFlame m env = runResourceT $ runReaderT m env


{-| 設定を取得します -}
getConfig :: CF.Configured a => Text -> LivingFlame (Maybe a)
getConfig x = do
  (cfg, _) <- asks systemConfig
  liftIO $ CF.lookup cfg x

  
getConfigDefault :: CF.Configured a => a -> Text -> LivingFlame a
getConfigDefault dft x = do
  (cfg, _) <- asks systemConfig
  liftIO $ CF.lookupDefault dft cfg x



{- ロギング関係
-}

{-| 運転ログを出力します -}
putLog :: BZ.Builder -> LivingFlame ()
putLog x = do
  stamp <- liftIO $ BZ.fromString <$> (formatTime defaultTimeLocale "%F %T " <$> getCurrentTime)
  mv <- asks systemLogHandle
  liftIO $ withMVar mv (\h -> hPutBuilder h $ stamp `mappend` x `mappend` BZ.fromString "\n")

{-| 運転ログをローテーションします -}
rotateLog :: LivingFlame ()
rotateLog = do
  mv <- asks systemLogHandle
  ls <- asks systemLogSpec
  liftIO $ modifyMVar_ mv (\h -> hClose h >> rotate ls >> openFile (log_file ls) AppendMode)



