#include <blitz/protocol/ByteBuffer.h>

#include <blitz/protocol/PacketData.h>

namespace blitz {
namespace protocol {

#define Operators(Type, GodotType)                                                                                                    \
	ByteBuffer& ByteBuffer::operator>>(Type& a_Data) {                                                                                \
		if (sizeof(a_Data) + m_ReadOffset > m_Buffer.size()) {                                                                        \
			throw ReadError("Buffer is too small ! Can't read " #Type);                                                               \
		}                                                                                                                             \
		a_Data = m_Buffer.decode_##GodotType(m_ReadOffset);                                                                           \
		m_ReadOffset += sizeof(a_Data);                                                                                               \
		return *this;                                                                                                                 \
	}                                                                                                                                 \
                                                                                                                                      \
	ByteBuffer& ByteBuffer::operator<<(Type a_Data) {                                                                                 \
		m_Buffer.resize(m_Buffer.size() + sizeof(a_Data));                                                                            \
		m_Buffer.encode_##GodotType(m_Buffer.size() - sizeof(a_Data), a_Data);                                                        \
		return *this;                                                                                                                 \
	}

// Integers
Operators(int8_t, s8);
Operators(uint8_t, u8);
Operators(int16_t, s16);
Operators(uint16_t, u16);
Operators(int32_t, s32);
Operators(uint32_t, u32);
Operators(int64_t, s64);
Operators(uint64_t, u64);

// Reals
Operators(float, float);
Operators(double, double);

ByteBuffer& ByteBuffer::operator>>(PlayerInfo& a_Data) {
	*this >> a_Data.m_PlayerId >> a_Data.m_PlayerName;
	return *this;
}

ByteBuffer& ByteBuffer::operator<<(const PlayerInfo& a_Data) {
	*this << a_Data.m_PlayerId << a_Data.m_PlayerName;
	return *this;
}

ByteBuffer& ByteBuffer::operator<<(const godot::Vector3& a_Data) {
	*this << a_Data.x << a_Data.y << a_Data.z;
	return *this;
}

ByteBuffer& ByteBuffer::operator>>(godot::Vector3& a_Data) {
	*this >> a_Data.x >> a_Data.y >> a_Data.z;
	return *this;
}

ByteBuffer& ByteBuffer::operator>>(godot::String& a_Data) {
	int nullPos = m_Buffer.find(0, m_ReadOffset);
	if (nullPos < 0)
		throw ReadError("String does not have an and in buffer !");


	godot::PackedByteArray stringBuffer = m_Buffer.slice(m_ReadOffset, nullPos);
	a_Data = stringBuffer.get_string_from_utf8();
	m_ReadOffset = nullPos + 1;
	return *this;
}

ByteBuffer& ByteBuffer::operator<<(const godot::String& a_Data) {
	godot::PackedByteArray stringBuffer = a_Data.to_utf8_buffer();
	m_Buffer.append_array(stringBuffer);
	// ends the string
	*this << static_cast<std::uint8_t>(0);
	return *this;
}

}  // namespace protocol
}  // namespace blitz
