{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}

module Data.ByteString.Read.Integral
    ( integral'
    , integral
    , int
    ) where

import Data.Proxy.Compat
import GHC.TypeLits.Compat

import Data.ByteString.Read.Class as C

-- $setup
-- >>> :set -XDataKinds -XOverloadedStrings
-- >>> import qualified Data.ByteString as S
-- >>> import qualified Data.ByteString.Lazy as L

integral_ :: (Radix b, Num n, Source s) => proxy b -> s -> (n, Int, s)
integral_ pn = loop 0 0
  where
    loop !i !d !s
        | C.null s                    = (i, d, s)
        | not (isDigit pn (C.head s)) = (i, d, s)
        | otherwise                   = loop
            (i * fromIntegral (natVal pn) + (fromIntegral $ unsafeToDigit pn (C.head s)))
            (d+1) (C.tail s)
{-# INLINABLE integral_ #-}

-- | convert bytestring into unsigned integral using radix
--
-- >>> integral' (Proxy :: Proxy 10) "12345" :: Maybe (Int, S.ByteString)
-- Just (12345,"")
-- >>> integral' (Proxy :: Proxy 2) "10112" :: Maybe (Int, L.ByteString)
-- Just (11,"2")
-- >>> integral' (Proxy :: Proxy 36) "Z" :: Maybe (Double, S.ByteString)
-- Just (35.0,"")
integral' :: (Radix b, Num n, Source s) => proxy b -> s -> Maybe (n, s)
integral' pn s0 = case integral_ pn s0 of
    (_, 0, _) -> Nothing
    (n, _, s) -> Just (n, s)
{-# INLINABLE integral' #-}
-- | @
-- integral = 'integral'' (Proxy :: Proxy 10)
-- @
integral :: (Num n, Source s) => s -> Maybe (n, s)
integral = integral' (Proxy :: Proxy 10)
{-# INLINABLE integral #-}

-- | @
-- int = integral
-- @
int :: Source s => s -> Maybe (Int, s)
int = integral
{-# INLINABLE int #-}