-- |Binary processing extensions to Attoparsec.
module Data.Attoparsec.Binary(
  anyWord16be,
  anyWord16le,
  anyWord32be,
  anyWord32le,
  anyWord64be,
  anyWord64le,
  word16be,
  word16le,
  word32be,
  word32le,
  word64be,
  word64le
  ) where

import Data.Attoparsec
import Data.Bits
import qualified Data.ByteString as B
import Data.Word

byteSize :: (Bits a) => a -> Int
byteSize = (`div` 8) . bitSize

pack :: (Bits a) => B.ByteString -> a
pack = B.foldl' (\n h -> (n `shiftL` 8) .|. fromIntegral h) 0

anyWordN :: (Bits a) => (B.ByteString -> a) -> Parser a
anyWordN = anyWordN' undefined
  where anyWordN' :: (Bits a) => a -> (B.ByteString -> a) -> Parser a
        anyWordN' d = flip fmap $ Data.Attoparsec.take $ byteSize d

-- |Match any 16-bit big-endian word.
anyWord16be :: Parser Word16
anyWord16be = anyWordN pack

-- |Match any 16-bit little-endian word.
anyWord16le :: Parser Word16
anyWord16le = anyWordN $ pack . B.reverse

-- |Match any 32-bit big-endian word.
anyWord32be :: Parser Word32
anyWord32be = anyWordN pack

-- |Match any 32-bit little-endian word.
anyWord32le :: Parser Word32
anyWord32le = anyWordN $ pack . B.reverse

-- |Match any 64-bit big-endian word.
anyWord64be :: Parser Word64
anyWord64be = anyWordN pack

-- |Match any 64-bit little-endian word.
anyWord64le :: Parser Word64
anyWord64le = anyWordN $ pack . B.reverse

unpack :: (Bits a, Integral a) => a -> B.ByteString
unpack x = B.pack $ map (fromIntegral . shiftR x . (8 *)) $ reverse [0..byteSize x - 1]

wordN :: (Bits a) => (a -> B.ByteString) -> a -> Parser a
wordN u w = string (u w) >> return w

-- |Match a specific 16-bit big-endian word.
word16be :: Word16 -> Parser Word16
word16be = wordN unpack

-- |Match a specific 16-bit little-endian word.
word16le :: Word16 -> Parser Word16
word16le = wordN $ B.reverse . unpack

-- |Match a specific 32-bit big-endian word.
word32be :: Word32 -> Parser Word32
word32be = wordN unpack

-- |Match a specific 32-bit little-endian word.
word32le :: Word32 -> Parser Word32
word32le = wordN $ B.reverse . unpack

-- |Match a specific 64-bit big-endian word.
word64be :: Word64 -> Parser Word64
word64be = wordN unpack

-- |Match a specific 64-bit little-endian word.
word64le :: Word64 -> Parser Word64
word64le = wordN $ B.reverse . unpack