module Text.Read.Bounded (
    BoundedRead(..),
    ReadBounded(..)
) where


import Data.Int
import Data.Word
import Text.Read (readMaybe)


data BoundedRead a
    = NoRead
    | ExactRead a
    | ClampedRead a
    deriving (Show, Read, Eq, Ord)


fromMaybe :: Maybe a -> BoundedRead a
fromMaybe = maybe NoRead ExactRead


data Clamped a
    = Exact a
    | Clamped a


clamp :: (Bounded a, Integral a) => Integer -> Clamped a
clamp x = if x < minInteger
    then Clamped minNum
    else if x > maxInteger
        then Clamped maxNum
        else Exact $ fromInteger x
    where
        minNum = minBound
        maxNum = maxBound
        minInteger = toInteger minNum
        maxInteger = toInteger maxNum


readClamped :: (Bounded a, Read a, Integral a) => String -> BoundedRead a
readClamped str = case readMaybe str of
    Nothing -> NoRead
    Just x -> case clamp x of
        Exact y -> ExactRead y
        Clamped y -> ClampedRead y


class ReadBounded a where
    readBounded :: String -> BoundedRead a


instance ReadBounded Integer where
    readBounded = fromMaybe . readMaybe


instance ReadBounded Int where
    readBounded = readClamped


instance ReadBounded Int8 where
    readBounded = readClamped


instance ReadBounded Int16 where
    readBounded = readClamped


instance ReadBounded Int32 where
    readBounded = readClamped


instance ReadBounded Int64 where
    readBounded = readClamped


instance ReadBounded Word where
    readBounded = readClamped


instance ReadBounded Word8 where
    readBounded = readClamped


instance ReadBounded Word16 where
    readBounded = readClamped


instance ReadBounded Word32 where
    readBounded = readClamped


instance ReadBounded Word64 where
    readBounded = readClamped