content: module Rattletrap.BitGet where
  
  import qualified Control.Exception as Exception
  import qualified Control.Monad as Monad
  import qualified Data.Bits as Bits
  import qualified Data.ByteString as ByteString
  import qualified Data.Functor.Identity as Identity
  import qualified Rattletrap.BitString as BitString
  import qualified Rattletrap.ByteGet as ByteGet
  import qualified Rattletrap.Exception.NotEnoughInput as NotEnoughInput
  import qualified Rattletrap.Get as Get
  
  type BitGet = Get.Get BitString.BitString Identity.Identity
  
  toByteGet :: BitGet a -> ByteGet.ByteGet a
  toByteGet g = do
    s1 <- Get.get
    case Identity.runIdentity . Get.run g $ BitString.fromByteString s1 of
      Left (ls, e) -> Get.labels ls $ ByteGet.throw e
      Right (s2, x) -> do
        Get.put $ BitString.byteString s2
        pure x
  
  fromByteGet :: ByteGet.ByteGet a -> Int -> BitGet a
  fromByteGet f n = do
    x <- byteString n
    Get.embed f x
  
  bits :: (Bits.Bits a) => Int -> BitGet a
  bits n = do
    let f :: (Bits.Bits a) => Bool -> a -> a
        f bit x = let y = Bits.shiftL x 1 in if bit then Bits.setBit y 0 else y
    xs <- Monad.replicateM n bool
    pure $ foldr f Bits.zeroBits xs
  
  bool :: BitGet Bool
  bool = do
    s1 <- Get.get
    case BitString.pop s1 of
      Nothing -> throw NotEnoughInput.NotEnoughInput
      Just (x, s2) -> do
        Get.put s2
        pure x
  
  byteString :: Int -> BitGet ByteString.ByteString
  byteString n = fmap ByteString.pack . Monad.replicateM n $ bits 8
  
  throw :: (Exception.Exception e) => e -> BitGet a
  throw = Get.throw
  
  label :: String -> BitGet a -> BitGet a
  label = Get.label
  