{- hlint ignore "Avoid restricted extensions" -}
{-# LANGUAGE BangPatterns #-}

module Rattletrap.BitBuilder where

import qualified Data.Bits as Bits
import qualified Data.ByteString.Builder as Builder
import qualified Data.Word as Word

data BitBuilder = BitBuilder
  { BitBuilder -> Word8
buffer :: Word.Word8,
    BitBuilder -> Builder
builder :: Builder.Builder,
    BitBuilder -> Int
offset :: Int
  }

empty :: BitBuilder
empty :: BitBuilder
empty = BitBuilder {buffer :: Word8
buffer = Word8
0x00, builder :: Builder
builder = forall a. Monoid a => a
mempty, offset :: Int
offset = Int
0}

push :: Bool -> BitBuilder -> BitBuilder
push :: Bool -> BitBuilder -> BitBuilder
push Bool
b BitBuilder
x =
  let !newBuffer :: Word8
newBuffer = if Bool
b then forall a. Bits a => a -> Int -> a
Bits.setBit (BitBuilder -> Word8
buffer BitBuilder
x) (BitBuilder -> Int
offset BitBuilder
x) else BitBuilder -> Word8
buffer BitBuilder
x
   in if BitBuilder -> Int
offset BitBuilder
x forall a. Eq a => a -> a -> Bool
== Int
7
        then
          BitBuilder
            { buffer :: Word8
buffer = Word8
0x00,
              builder :: Builder
builder = BitBuilder -> Builder
builder BitBuilder
x forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
Builder.word8 Word8
newBuffer,
              offset :: Int
offset = Int
0
            }
        else BitBuilder
x {buffer :: Word8
buffer = Word8
newBuffer, offset :: Int
offset = BitBuilder -> Int
offset BitBuilder
x forall a. Num a => a -> a -> a
+ Int
1}

toBuilder :: BitBuilder -> Builder.Builder
toBuilder :: BitBuilder -> Builder
toBuilder BitBuilder
x =
  if BitBuilder -> Int
offset BitBuilder
x forall a. Eq a => a -> a -> Bool
== Int
0 then BitBuilder -> Builder
builder BitBuilder
x else BitBuilder -> Builder
builder BitBuilder
x forall a. Semigroup a => a -> a -> a
<> Word8 -> Builder
Builder.word8 (BitBuilder -> Word8
buffer BitBuilder
x)