{- 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)