#include "stdafx.h"
#include "Buffer.h"

#ifdef GUI_WIN32

namespace gui {

	D3DBuffer::D3DBuffer(Engine &e, ID3D10Device *device, UINT bindFlags, BufferUsage usage,
						const void *data, size_t size) : bindFlags(bindFlags) {
		create(e, device, usage, data, size);
	}

	void D3DBuffer::create(Engine &e, ID3D10Device *device, BufferUsage usage, const void *data, size_t size) {
		D3D10_BUFFER_DESC desc;
		zeroMem(desc);
		desc.ByteWidth = UINT(size);
		desc.BindFlags = bindFlags;

		switch (usage) {
		case bufferReplace:
			desc.Usage = D3D10_USAGE_DYNAMIC;
			desc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
			break;
		default:
			desc.Usage = D3D10_USAGE_DEFAULT;
			desc.CPUAccessFlags = 0;
			break;
		}

		D3D10_SUBRESOURCE_DATA initData;
		zeroMem(initData);
		initData.pSysMem = data;

		buffer.clear();
		HRESULT r = device->CreateBuffer(&desc, &initData, &buffer.v);
		verify(e, r, S("Failed to create a device buffer: "));

		this->size = size;
		this->usage = usage;
	}

	static bool isSlow(BufferUsage a) {
		return a == bufferStatic || a == bufferDynamic;
	}

	static bool bothSlow(BufferUsage a, BufferUsage b) {
		return isSlow(a) && isSlow(b);
	}

	void D3DBuffer::update(Engine &e, BufferUsage usage, const void *data, size_t size,
						size_t dirtyFrom, size_t dirtyTo) {
		ComPtr<ID3D10Device> device;
		buffer->GetDevice(&device.v);

		if (this->usage == bufferReplace && this->usage == usage && this->size == size) {
			// Note: Just MAP_WRITE does not work. Most likely because we would need to wait quite
			// long for the GPU for that to work.
			void *bufData = null;
			HRESULT r = buffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &bufData);
			verify(e, r, S("Failed to map the buffer: "));
			memcpy(bufData, data, size);
			buffer->Unmap();

		} else if (bothSlow(this->usage, usage) && this->size == size) {
			// We write to it using UpdateSubresource.
			D3D10_BOX offsets;
			zeroMem(offsets);
			offsets.left = UINT(dirtyFrom);
			offsets.right = UINT(dirtyTo);
			offsets.top = 0;
			offsets.bottom = 1;
			offsets.front = 0;
			offsets.back = 1;
			device->UpdateSubresource(buffer.v, 0, &offsets, (const char *)data + dirtyFrom, 0, 0);
		} else {
			// Need to re-create the buffer.
			create(e, device.v, usage, data, size);
		}
	}

}

#endif
