#pragma once

#include "AbstractChecksumCalculator.h"

#include <vector>
#include <string>
#include <optional>

class BitStream
{
public:
    virtual ~BitStream();

    unsigned char getCurrentByte();

    int getCurrentByteOffset() const;

    unsigned getCurrentBitOffset() const;

    virtual bool isFinished() const = 0;

    std::string logNextNBytes(unsigned n) const;

    std::string logLocation();

    virtual std::vector<unsigned char> peekNextNBytes(unsigned n) const = 0;

    virtual bool readNextNBits(unsigned n, unsigned char& buffer);

    virtual std::optional<unsigned char> readNextByte() = 0;

    virtual void writeNBits(uint32_t data, unsigned length);

    virtual void writeByte(unsigned char data, bool checkOverflow = true) = 0;

    void write(uint32_t data);

    void writeWord(uint16_t data);

    virtual void writeBytes(const std::vector<unsigned char> data) = 0;

    void resetOffsets()
    {
        mEndByteOffset = mByteOffset;
        mEndBitOffset = mBitOffset;
        mEndByte = mCurrentByte;

        mCurrentByte = 0;
        mByteOffset = -1;
        mBitOffset = 0;
    }

    virtual void reset()
    {
        resetOffsets();
        mCurrentByte = 0;
    }

    void flushRemainingBits()
    {
        if (mBitOffset > 0)
        {
            writeByte(mCurrentByte, false);
            mBitOffset = 0;
        }
    }

    std::pair<unsigned char, unsigned> getRemainingBits() const
    {
        return {mEndByte, mEndBitOffset};
    }

    void setChecksumCalculator(AbstractChecksumCalculator* calc)
    {
        mChecksumCalculator = calc;
    }

    void clearChecksumCalculator()
    {
        mChecksumCalculator = nullptr;
    }

protected:
    int mByteOffset{-1};
    unsigned mBitOffset{0};
    unsigned char mCurrentByte{0};

    int mEndByteOffset{-1};
    unsigned mEndBitOffset{0};
    unsigned char mEndByte{0};
    AbstractChecksumCalculator* mChecksumCalculator{nullptr};
};
