module Data.Icao.Time
    (
    
      Hhmm(hour, minute)
    , DayTime(dayOfMonth, time)
    , Date(year, month, day)
    
    , hhmmParser
    , dateParser
    , dayTimeParser
    , parseHhmm
    , parseDate
    , parseDayTime
    
    , mkHhmm
    , mkDate
    , mkDayTime
    ) where
import Control.Monad.Fail
import Prelude hiding (fail)
import Data.Aeromess.Parser
import Data.Either ()
data Hhmm = Hhmm
    { hour :: Int
    , minute :: Int
    } deriving (Eq, Show)
data Date = Date
    { year :: Int
    , month :: Int
    , day :: Int
    } deriving (Eq, Show)
data DayTime = DayTime
    { dayOfMonth :: Int 
    , time :: Hhmm 
    } deriving (Eq, Show)
hhmmParser :: Parser Hhmm
hhmmParser = do
    hh <- natural 2
    mm <- natural 2
    mkHhmm hh mm
dateParser :: Parser Date
dateParser = do
    yy <- natural 2
    mm <- natural 2
    dd <- natural 2
    mkDate yy mm dd
dayTimeParser :: Parser DayTime
dayTimeParser = do
    dd <- natural 2
    hhmm <- hhmmParser
    mkDayTime dd (hour hhmm) (minute hhmm)
parseHhmm :: String -> Either Error Hhmm
parseHhmm = runParser hhmmParser
parseDate :: String -> Either Error Date
parseDate = runParser dateParser
parseDayTime :: String -> Either Error DayTime
parseDayTime = runParser dayTimeParser
mkHhmm
    :: (MonadFail m)
    => Int -> Int -> m Hhmm
mkHhmm hh mm
    | hh < 0 || hh > 23 = fail ("invalid hour=" ++ show hh)
    | mm < 0 || mm > 59 = fail ("invalid minute=" ++ show mm)
    | otherwise = return (Hhmm hh mm)
mkDate
    :: (MonadFail m)
    => Int -> Int -> Int -> m Date
mkDate yy mm dd
    | yy < 0 || yy > 99 = fail ("invalid year=" ++ show yy)
    | mm < 1 || mm > 12 = fail ("invalid month=" ++ show mm)
    | dd < 1 || dd > 31 = fail ("invalid day=" ++ show dd)
    | otherwise = return (Date yy mm dd)
mkDayTime
    :: (MonadFail m)
    => Int -> Int -> Int -> m DayTime
mkDayTime dd hh mm
    | dd < 1 || dd > 31 = fail ("invalid day=" ++ show dd)
    | otherwise = fmap (DayTime dd) (mkHhmm hh mm)