%  Copyright (C) 2004 David Roundy
%
%  This program is free software; you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation; either version 2, or (at your option)
%  any later version.
%
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
%  You should have received a copy of the GNU General Public License
%  along with this program; see the file COPYING.  If not, write to
%  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
%  Boston, MA 02110-1301, USA.


\begin{code}
module DateMatcher ( parseDateMatcher ) where

import Control.Exception ( catchJust, userErrors )
import System.Time
import IsoDate ( parseDate, englishDateTime, englishInterval, iso8601_interval,
                 subtractFromCal, getLocalTz )
import Control.Monad ( liftM )
import Text.ParserCombinators.Parsec ( eof, parse )

sameDate :: CalendarTime -> CalendarTime -> Bool
sameDate a b = ctDay a == ctDay b &&
               ctMonth a == ctMonth b &&
               ctYear a == ctYear b

dateRange :: CalendarTime -> CalendarTime -> CalendarTime -> Bool
dateRange a b c = a <= c && b >= c

parseDateMatcher :: String -> IO (CalendarTime -> Bool)
parseDateMatcher d = 
 do rightNow <- now
    tzNow <- getLocalTz
    let parseToEof p = parse $ do { x <- p; eof; return x }
        --
        tryEnglishDateOr next =
          case parseToEof (englishDateTime rightNow) "" d of
               Right ed -> dateRange ed rightNow
               _ -> next
        --
        tryEnglishIntervalOr next =
          case parseToEof (englishInterval rightNow) "" d of
               Right (a,b) -> dateRange a b 
               _ -> next
        --
        tryISOIntervalOr next =
          case parseToEof (iso8601_interval 0) "" d of
               Right (Left dur)    -> dateRange (dur `subtractFromCal` rightNow) rightNow
               Right (Right (a,b)) -> dateRange a b
               _ -> next
        --
        tryDateOr next =
          case parseDate tzNow d of
               Right ct -> sameDate ct
               _ -> next
    let matcher = tryEnglishDateOr $ tryEnglishIntervalOr
                $ tryISOIntervalOr $ tryDateOr
                $ error "Can't support fancy dates."
    -- Hack: test the matcher against the current date and discard the results.
    -- We just want to make sure it won't throw any exceptions when we use it for real.
    matcher `liftM` now >>= (`seq` return matcher)
 `catchUserError`
    -- If the user enters a date > maxint seconds ago, the toClockTime
    -- function cannot work.
    \e -> if e == "Time.toClockTime: invalid input"
          then error "Can't handle dates that far back!"
          else error e
 where catchUserError = catchJust userErrors


now :: IO CalendarTime
now = toUTCTime `liftM` getClockTime
\end{code}

