#include "bytebuf.hpp"

#include <cassert>
#include <fstream>
#include <iostream>

void ByteBuffer::WriteUInt8(uint8_t v)
{
	m_buffer.push_back(v);
}

void ByteBuffer::WriteUInt16(uint16_t v)
{
	m_buffer.push_back(v & 0xff);
	m_buffer.push_back((v >> 8) & 0xff);
}

void ByteBuffer::WriteUInt32(uint32_t v)
{
	m_buffer.push_back(v & 0xff);
	m_buffer.push_back((v >> 8) & 0xff);
	m_buffer.push_back((v >> 16) & 0xff);
	m_buffer.push_back((v >> 24) & 0xff);
}

void ByteBuffer::WriteInt8(int8_t v) { WriteUInt8(*reinterpret_cast<uint8_t *>(&v)); }
void ByteBuffer::WriteInt16(int16_t v) { WriteUInt16(*reinterpret_cast<uint16_t *>(&v)); }
void ByteBuffer::WriteInt32(int32_t v) { WriteUInt32(*reinterpret_cast<uint32_t *>(&v)); }
void ByteBuffer::WriteFloat(float v) { WriteUInt32(*reinterpret_cast<uint32_t *>(&v)); }

void ByteBuffer::OverwriteUInt32(size_t offs, uint32_t v)
{
	assert(offs < m_buffer.size() - sizeof(uint32_t));
	m_buffer[offs + 0] = v & 0xff;
	m_buffer[offs + 1] = (v >> 8) & 0xff;
	m_buffer[offs + 2] = (v >> 16) & 0xff;
	m_buffer[offs + 3] = (v >> 24) & 0xff;
}

void ByteBuffer::WriteBytes(void *data, size_t len)
{
	for (int i = 0; i < len; i++)
		m_buffer.push_back(((char*)data)[i]);
}

void ByteBuffer::FillZero(size_t num)
{
	for (int i = 0; i < num; i++)
		m_buffer.push_back(0);
}

void ByteBuffer::CopyFrom(const ByteBuffer &other)
{
	for (int i = 0; i < other.size(); i++)
		m_buffer.push_back(other.m_buffer[i]);
}

void ByteBuffer::WriteFixedString(const std::string &str, int maxlen)
{
	for (int i = 0; i < maxlen; i++)
	{
		if (i >= str.length())
			WriteUInt8(0);
		else
			WriteUInt8(str[i]);
	}
}

void ByteBuffer::WriteLengthString(const std::string &str)
{
	if (str.length() >= 256)
	{
		std::cerr << "String '" << str << "' exceeds 255 character limit" << std::endl;
		return;
	}
	WriteUInt8(str.length());
	for (int i = 0; i < str.length(); i++)
	{
		WriteUInt8(str[i]);
	}
}

void ByteBuffer::SaveToFile(const std::filesystem::path &path) const
{
	std::ofstream outfile(path, std::ios::binary);
	if (!outfile.is_open())
	{
		std::cerr << "Unable to open bytecode output file" << std::endl;
		return;
	}
	outfile.write(reinterpret_cast<const char *>(m_buffer.data()), m_buffer.size());
	outfile.close();
}
