#include "stdafx.h"
#include "VertexBuffer.h"

namespace Assets
{
	VertexBuffer::VertexBuffer()
		: _Buffer(nullptr), _BufferSize(0), _StoreSize(0), _MaxInfluence(1), _UVLayers(1)
	{
	}

	VertexBuffer::VertexBuffer(uint8_t MaxInfluence, uint8_t UVLayers)
		: _Buffer(nullptr), _BufferSize(0), _StoreSize(0), _MaxInfluence(MaxInfluence), _UVLayers(UVLayers)
	{
	}

	VertexBuffer::VertexBuffer(uint32_t InitialSize, uint8_t MaxInfluence, uint8_t UVLayers)
		: _Buffer(nullptr), _BufferSize(0), _StoreSize(0), _MaxInfluence(MaxInfluence), _UVLayers(UVLayers)
	{
		this->EnsureCapacity(InitialSize);
		this->_StoreSize = 0;	// Must reset this so that we start from 0
	}

	VertexBuffer& VertexBuffer::operator=(const VertexBuffer& Rhs)
	{
		if (Rhs._Buffer)
		{
			auto vSize = Rhs.VertexSize();
			this->_Buffer = new uint8_t[vSize * Rhs._BufferSize];
			this->_StoreSize = Rhs._StoreSize;
			this->_BufferSize = Rhs._BufferSize;

			std::memcpy(this->_Buffer, Rhs._Buffer, vSize * Rhs._BufferSize);
		}

		this->_MaxInfluence = Rhs._MaxInfluence;
		this->_UVLayers = Rhs._UVLayers;

		return *this;
	}

	VertexBuffer::~VertexBuffer()
	{
		if (this->_Buffer)
			delete[] this->_Buffer;

		this->_Buffer = nullptr;
		this->_BufferSize = 0;
		this->_StoreSize = 0;
	}

	void VertexBuffer::EmplaceBack(Vector3 Position, Vector3 Normal, VertexColor Color)
	{
		uint64_t nPos = this->_StoreSize;
		this->EnsureCapacity((uint32_t)nPos + 1);

		// Copy the values at expected vertex offset
		nPos *= this->VertexSize();

		// Copy them
		std::memcpy(this->_Buffer + nPos, &Position, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + sizeof(Vector3), &Normal, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2), &Color, sizeof(VertexColor));
	}

	Vertex VertexBuffer::Emplace(Vector3 Position, Vector3 Normal, VertexColor Color)
	{
		uint64_t nPos = this->_StoreSize;
		this->EnsureCapacity((uint32_t)nPos + 1);

		// Copy the values at expected vertex offset
		nPos *= this->VertexSize();

		// Copy them
		std::memcpy(this->_Buffer + nPos, &Position, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + sizeof(Vector3), &Normal, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2), &Color, sizeof(VertexColor));

		// Return vertex controller
		return Vertex(this->_Buffer + nPos, this->_MaxInfluence, this->_UVLayers);
	}

	void VertexBuffer::EmplaceBack(Vector3 Position, Vector3 Normal, VertexColor Color, Vector2 UVLayer)
	{
		uint64_t nPos = this->_StoreSize;
		this->EnsureCapacity((uint32_t)nPos + 1);

		// Copy the values at expected vertex offset
		nPos *= this->VertexSize();

		// Copy them
		std::memcpy(this->_Buffer + nPos, &Position, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + sizeof(Vector3), &Normal, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2), &Color, sizeof(VertexColor));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2) + sizeof(VertexColor), &UVLayer, sizeof(Vector2));
	}

	Vertex VertexBuffer::Emplace(Vector3 Position, Vector3 Normal, VertexColor Color, Vector2 UVLayer)
	{
		uint64_t nPos = this->_StoreSize;
		this->EnsureCapacity((uint32_t)nPos + 1);

		// Copy the values at expected vertex offset
		nPos *= this->VertexSize();

		// Copy them
		std::memcpy(this->_Buffer + nPos, &Position, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + sizeof(Vector3), &Normal, sizeof(Vector3));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2), &Color, sizeof(VertexColor));
		std::memcpy(this->_Buffer + nPos + (sizeof(Vector3) * 2) + sizeof(VertexColor), &UVLayer, sizeof(Vector2));

		// Return vertex controller
		return Vertex(this->_Buffer + nPos, this->_MaxInfluence, this->_UVLayers);
	}

	void VertexBuffer::Clear()
	{
		if (this->_Buffer)
			delete[] this->_Buffer;

		this->_Buffer = nullptr;
		this->_BufferSize = 0;
		this->_StoreSize = 0;
	}

	void VertexBuffer::RemoveAt(uint32_t Index)
	{
		if (Index >= this->_StoreSize)
			return;

		auto vSize = this->VertexSize();

		this->_StoreSize--;
		if (Index < this->_StoreSize)
			std::memcpy(this->_Buffer + Index, this->_Buffer + (Index + 1), (this->_StoreSize - Index) * vSize);

		std::memset(&this->_Buffer[this->_StoreSize], 0, vSize);
	}

	const uint8_t VertexBuffer::UVLayerCount() const
	{
		return this->_UVLayers;
	}

	const uint8_t VertexBuffer::WeightCount() const
	{
		return this->_MaxInfluence;
	}

	uint32_t VertexBuffer::Count() const
	{
		return this->_StoreSize;
	}

	bool VertexBuffer::Empty() const
	{
		return (this->_StoreSize == 0);
	}

	Vertex VertexBuffer::begin() const noexcept
	{
		return Vertex(this->_Buffer, this->_MaxInfluence, this->_UVLayers);
	}

	Vertex VertexBuffer::end() const noexcept
	{
		return Vertex(this->_Buffer + (this->_StoreSize * this->VertexSize()), this->_MaxInfluence, this->_UVLayers);
	}

	Vertex VertexBuffer::operator[](uint32_t Index)
	{
		return Vertex(this->_Buffer + (Index * this->VertexSize()), this->_MaxInfluence, this->_UVLayers);
	}

	const Vertex VertexBuffer::operator[](uint32_t Index) const
	{
		return Vertex(this->_Buffer + (Index * this->VertexSize()), this->_MaxInfluence, this->_UVLayers);
	}

	void VertexBuffer::EnsureCapacity(uint32_t Capacity)
	{
		// Ensure that we have a proper buffer size for the string here, this is in units, NOT bytes...
		// Check to ensure we aren't wasting our time first...
		if (Capacity <= this->_BufferSize)
		{
			this->_StoreSize = Capacity;
			return;
		}

		// Pre-calculate the size of a single vertex (Position Normal) (UVLayers) (Weights) (Colors)
		auto vSize = this->VertexSize();

		auto nCapacity = Capacity;
		if (nCapacity < 16)
			nCapacity = 16;

		if (nCapacity < (this->_BufferSize + (this->_BufferSize / 2)))
			nCapacity = (this->_BufferSize + (this->_BufferSize / 2));

		auto tBuffer = this->_Buffer;
		this->_Buffer = new uint8_t[nCapacity * vSize]();

		if (tBuffer)
		{
			std::memcpy(this->_Buffer, tBuffer, this->_BufferSize * vSize);
			delete[] tBuffer;
		}

		this->_BufferSize = nCapacity;
		this->_StoreSize = Capacity;
	}

	uint32_t VertexBuffer::VertexSize() const
	{
		return (sizeof(Vector3) * 2) + (sizeof(Vector2) * this->_UVLayers) + (sizeof(VertexWeight) * this->_MaxInfluence) + sizeof(VertexColor) + sizeof(uint8_t);
	}
}