{-# LANGUAGE MultiParamTypeClasses
           , TypeSynonymInstances
           , FlexibleInstances
  #-}


module Data.ByteString.Nums.Careless.Hex where


import Prelude hiding (head, tail, drop)
import Data.Word
import Data.Int
import Data.ByteString hiding (head, pack)
import Data.ByteString.Internal
import qualified Data.ByteString.Lazy.Char8 as Lazy
import qualified Data.ByteString.Lazy.Internal as Lazy




{-| Types that can be read from hexadecimal strings. Characters that are not
    hexadecimal digits are skipped over. One pleasant consequence of this is
    that a leading @0x@ is simply ignored.
 -}
class (Num n) => Hexable b n where
  hex                       ::  b -> n

instance Hexable ByteString Word8 where
  hex                        =  strict_hex
instance Hexable ByteString Word16 where
  hex                        =  strict_hex
instance Hexable ByteString Word32 where
  hex                        =  strict_hex
instance Hexable ByteString Word64 where
  hex                        =  strict_hex
instance Hexable ByteString Word where
  hex                        =  strict_hex
instance Hexable ByteString Int8 where
  hex                        =  strict_hex
instance Hexable ByteString Int16 where
  hex                        =  strict_hex
instance Hexable ByteString Int32 where
  hex                        =  strict_hex
instance Hexable ByteString Int64 where
  hex                        =  strict_hex
instance Hexable ByteString Int where
  hex                        =  strict_hex
instance Hexable ByteString Float where
  hex                        =  strict_hex
instance Hexable ByteString Double where
  hex                        =  strict_hex
instance Hexable ByteString Rational where
  hex                        =  strict_hex
instance Hexable ByteString Integer where
  hex                        =  strict_hex

instance Hexable Lazy.ByteString Word8 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Word16 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Word32 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Word64 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Word where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Int8 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Int16 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Int32 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Int64 where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Int where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Float where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Double where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Rational where
  hex                        =  lazy_hex
instance Hexable Lazy.ByteString Integer where
  hex                        =  lazy_hex




hexalize                    ::  (Num n) => n -> Word8 -> n
hexalize acc byte
  | between 'a' 'f'          =  place_up (byte + 0x0a - c2w 'a')
  | between 'A' 'F'          =  place_up (byte + 0x0a - c2w 'A')
  | between '0' '9'          =  place_up (byte - c2w '0')
  | otherwise                =  acc
 where
  between a z                =  byte >= c2w a && byte <= c2w z
  place_up b                 =  (0x10 * acc) + fromIntegral b

strict_hex                  ::  (Num n) => ByteString -> n
strict_hex bytes             =  foldl' hexalize 0 bytes

lazy_hex                    ::  (Num n) => Lazy.ByteString -> n
lazy_hex bytes               =  Lazy.foldlChunks (foldl' hexalize) 0 bytes