module Yesod.Filter.Read where

import           Data.Char                (isSpace, toLower, toUpper)
import           Data.Time                (Day, TimeOfDay, UTCTime)
import           Data.Time.Format.ISO8601 (iso8601ParseM)
import           Text.Read                (readMaybe)


-- | Parse a string to Int.
--
-- >>> readMaybeInt "1"
-- Just 1
--
-- >>> readMaybeInt "one"
-- Nothing
--
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe'

-- | Parse a string to Double.
--
-- >>> readMaybeDouble "1.0"
-- Just 1.0
--
-- >>> readMaybeDouble "1"
-- Just 1.0
--
readMaybeDouble :: String -> Maybe Double
readMaybeDouble = readMaybe'

-- | Parse a string to Bool.
--
-- >>> readMaybeBool "true"
-- Just True
--
-- >>> readMaybeBool "FALSE"
-- Just False
--
readMaybeBool :: String -> Maybe Bool
readMaybeBool = readMaybe' . capitalize

-- | Parse a string to Day.
--
-- >>> readMaybeDay "2020-01-01"
-- Just 2020-01-01
--
-- >>> readMaybeDay "2020/01/01"
-- Nothing
--
readMaybeDay :: String -> Maybe Day
readMaybeDay = readMaybe'

-- | Parse a string to TimeOfDay.
--
-- >>> readMaybeTimeOfDay "12:34:56"
-- Just 12:34:56
--
-- >>> readMaybeTimeOfDay "12:34:56.789"
-- Just 12:34:56.789
--
readMaybeTimeOfDay :: String -> Maybe TimeOfDay
readMaybeTimeOfDay = readMaybe'

-- | Parse a string to UTCTime.
--
-- >>> readMaybeUTCTime "2020-01-01T12:34:56Z"
-- Just 2020-01-01 12:34:56 UTC
--
-- >>> readMaybeUTCTime "2020-01-01T12:34:56+09:00"
-- Nothing
--
readMaybeUTCTime :: String -> Maybe UTCTime
readMaybeUTCTime = iso8601ParseM

-- | Wrapper function of `readMaybe` that returns Nothing if a string has leading/trailing whitespaces.
--
-- >>> readMaybe' "1" :: Maybe Int
-- Just 1
--
-- >>> readMaybe' " 1" :: Maybe Int
-- Nothing
--
-- >>> readMaybe' "1 " :: Maybe Int
-- Nothing
--
readMaybe' :: Read a => String -> Maybe a
readMaybe' "" = readMaybe ""
readMaybe' s
    | isSpace (head s) || isSpace (last s) = Nothing
    | otherwise                            = readMaybe s

-- | Capitalize a string.
--
-- >>> capitalize "foo"
-- "Foo"
--
-- >>> capitalize "FOO"
-- "Foo"
--
capitalize :: String -> String
capitalize []       = []
capitalize (x : xs) = toUpper x : map toLower xs