#include "VarInt.h"

#include "DataBuffer.h"
#include <stdexcept>

static constexpr int SEGMENT_BITS = 0x7F;
static constexpr int CONTINUE_BIT = 0x80;

std::size_t VarInt::GetSerializedLength() const {
	DataBuffer buffer;
	buffer << *this;
	return buffer.GetSize();
}

DataBuffer& operator<<(DataBuffer& out, const VarInt& var) {
	std::uint64_t value = var.m_Value;
	while (true) {
		if ((value & ~static_cast<std::uint64_t>(SEGMENT_BITS)) == 0) {
			out << static_cast<std::uint8_t>(value);
			return out;
		}

		out << static_cast<std::uint8_t>((value & SEGMENT_BITS) | CONTINUE_BIT);

		value >>= 7;
	}
}

DataBuffer& operator>>(DataBuffer& in, VarInt& var) {
	var.m_Value = 0;
	unsigned int position = 0;
	std::uint8_t currentByte;

	while (true) {
		in.ReadSome(&currentByte, 1);
		var.m_Value |= static_cast<std::uint64_t>(currentByte & SEGMENT_BITS) << position;

		if ((currentByte & CONTINUE_BIT) == 0)
			break;

		position += 7;

		if (position >= 8 * sizeof(var.m_Value))
			throw std::runtime_error("VarInt is too big");
	}

	return in;
}
