{-# LANGUAGE BangPatterns #-}

module Network.Wai.Logger.Date (
    ZonedDate
  , dateInit
  , getDate
  , DateRef
  ) where

import Control.Concurrent
import Control.DeepSeq
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Data.IORef
import Data.Time
import System.Locale

-- | A type for zoned date.
type ZonedDate = ByteString

-- | Reference to the cache of 'ZoneDate'.
newtype DateRef = DateRef (IORef ByteString)

-- | Getting 'ZonedDate' from the cache.
getDate :: DateRef -> IO ZonedDate
getDate (DateRef ref) = readIORef ref

-- | Start caching 'ZonedDate' every second.
dateInit :: IO DateRef
dateInit = do
    ref <- formatDate >>= newIORef
    let dateref = DateRef ref
    forkIO $ date dateref
    return dateref

date :: DateRef -> IO ()
date dateref@(DateRef !ref) = do
    !tmstr <- formatDate
    x <- atomicModifyIORef ref (\_ -> (tmstr, ()))
    -- atomicModifyIORef is prone to leak spaces.
    x `seq` return ()
    threadDelay 1000000
    date dateref

formatDate :: IO ByteString
formatDate = do
    tm <- getZonedTime
    let !str = formatTime defaultTimeLocale "%d/%b/%Y:%T %z" tm
        !tmstr = str `deepseq` BS.pack str
    return tmstr