    {-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}

    module Propellor.Property.Scheduled
    	( period
    	, periodParse
    	, Recurrance(..)
    	, WeekDay
    	, MonthDay
    	, YearDay
    	) where

    import Propellor.Base
    import Propellor.Types.Core
    import Utility.Scheduled

    import Data.Time.Clock
    import Data.Time.LocalTime
    import qualified Data.Map as M

    -- | Makes a Property only be checked every so often.
    --
    -- This uses the description of the Property to keep track of when it was
    -- last run.
    period :: (IsProp (Property i)) => Property i -> Recurrance -> Property i
    period prop recurrance = flip describe desc $ adjustPropertySatisfy prop $ \satisfy -> do
    	lasttime <- liftIO $ getLastChecked (getDesc prop)
    	nexttime <- liftIO $ fmap startTime <$> nextTime schedule lasttime
    	t <- liftIO localNow
    	if Just t >= nexttime
    		then do
    			r <- satisfy
    			liftIO $ setLastChecked t (getDesc prop)
    			return r
    		else noChange
      where
    	schedule = Schedule recurrance AnyTime
    	desc = getDesc prop ++ " (period " ++ fromRecurrance recurrance ++ ")"

    -- | Like period, but parse a human-friendly string.
    periodParse :: (IsProp (Property i)) => Property i -> String -> Property i
    periodParse prop s = case toRecurrance s of
    	Just recurrance -> period prop recurrance
    	Nothing -> adjustPropertySatisfy prop $ \_ -> do
    		liftIO $ warningMessage $ "failed periodParse: " ++ s
    		noChange

    lastCheckedFile :: FilePath
    lastCheckedFile = localdir </> ".lastchecked"

    getLastChecked :: Desc -> IO (Maybe LocalTime)
    getLastChecked desc = M.lookup desc <$> readLastChecked

    localNow :: IO LocalTime
    localNow = do
    	now <- getCurrentTime
    	tz <- getTimeZone now
    	return $ utcToLocalTime tz now

    setLastChecked :: LocalTime -> Desc -> IO ()
    setLastChecked time desc = do
    	m <- readLastChecked
    	writeLastChecked (M.insert desc time m)

    readLastChecked :: IO (M.Map Desc LocalTime)
    readLastChecked = fromMaybe M.empty <$> catchDefaultIO Nothing go
      where
    	go = readish <$> readFileStrict lastCheckedFile

    writeLastChecked :: M.Map Desc LocalTime -> IO ()
    writeLastChecked = writeFile lastCheckedFile . show