{-# LANGUAGE UnicodeSyntax, DeriveDataTypeable, FlexibleContexts #-}

module Data.Dates.Internal where

import Data.Char

import Text.Parsec

-- | Parser version of Prelude.read
tryRead :: (Read a, Stream s m Char) => String -> ParsecT s st m a
tryRead str =
  case reads str of
    [(res, "")] -> return res
    _ -> fail $ "Cannot read: " ++ str

tryReadInt  (Stream s m Char, Num a)  String  ParsecT s st m a
tryReadInt str =
  if all isDigit str
    then return $ fromIntegral $ foldl (\a b  10*a+b) 0 $ map digitToInt str
    else fail $ "Cannot read: " ++ str

-- | Apply parser N times
times  (Stream s m Char)
      Int
      ParsecT s st m t
      ParsecT s st m [t]
times 0 _ = return []
times n p = do
  ts  times (n-1) p
  t  optionMaybe p
  case t of
    Just t'  return (ts ++ [t'])
    Nothing  return ts
                               
-- | Parse natural number of N digits
-- which is not greater than M
number  Stream s m Char
        Int   -- ^ Number of digits
        Int   -- ^ Maximum value
        ParsecT s st m Int
number n m = do
  t  tryReadInt =<< (n `times` digit)
  if t > m
    then fail "number too large"
    else return t

pYear  Stream s m Char => ParsecT s st m Int
pYear = do
  y  number 4 10000
  if y < 2000
    then return (y+2000)
    else return y

pMonth  Stream s m Char => ParsecT s st m Int
pMonth = number 2 12

pDay  Stream s m Char => ParsecT s st m Int
pDay = number 2 31