#### Bzip2 Implementation
#### Copyright (C) 2023-2024 Remilia Scarlet
#### Copyright (c) 2022 drone1400
#### Copyright (C) 2015 Jaime Olivares
#### Copyright (c) 2011 Matthew Francis
#### MIT License
####
#### Ported from the Java implementation by Matthew Francis:
#### https://github.com/MateuszBartosiewicz/bzip2.
####
#### Modified by drone1400
####
#### Ported by Remilia Scarlet from the C# implementation by Jamie Olivares:
#### http://github.com/jaime-olivares/bzip2

module RemiLib::Compression::BZip2
  private class BitWriter
    protected getter io : IO

    # A buffer of bits waiting to be written to the output stream.
    @bitBuffer : UInt32 = 0

    # The number of bits currently buffered in bitBuffer.
    @bitCount : Int32 = 0

    # Creates a new `BitWriter` instance that will write to the given `IO`.
    def initialize(@io : IO)
    end

    @[AlwaysInline]
    def writeBoolean(value : Bool) : Nil
      @bitCount += 1
      @bitBuffer |= (value ? 1u32 : 0u32) << (32 - @bitCount)

      if @bitCount == 8
        @io.write_byte((@bitBuffer >> 24).to_u8!)
        @bitBuffer = 0
        @bitCount = 0
      end
    end

    @[AlwaysInline]
    def writeUnary(value : Int) : Nil
      while value > 0
        writeBoolean(true)
        value -= 1
      end
      writeBoolean(false)
    end

    @[AlwaysInline]
    def writeBits(count : Int, value : Int32|UInt32)
      @bitBuffer |= (value.to_u32! << (32 - count)) >> @bitCount
      @bitCount += count

      while @bitCount >= 8
        @io.write_byte((@bitBuffer >> 24).to_u8!)
        @bitBuffer <<= 8
        @bitCount -= 8
      end
    end

    @[AlwaysInline]
    def writeInt32(value : Int32|UInt32) : Nil
      writeBits(16, (value.to_u32! >> 16) & 0xFFFF)
      writeBits(16, value.to_u32! & 0xFFFF)
    end

    @[AlwaysInline]
    def flush : Nil
      if @bitCount > 0
        writeBits(8 - @bitCount, 0)
      end
    end
  end
end
