#pragma once

#ifndef __gx_buffer_h__
	#define __gx_buffer_h__

#include "foundation.h"
#include "graphicsresource.h"
#include "gxopengl.h"

namespace Gx
{
	class GX_API  Buffer : public GraphicsResource
	{
		friend class GraphicsDevice;

	public:
		virtual ~Buffer() {
			glDeleteBuffers(1, &m_id);
		}

		template<typename T>
		T* lock(GLenum access) {
			glBindBuffer(m_target, m_id);
			T* buffer = reinterpret_cast<T*>(glMapBuffer(m_target, access));
			glBindBuffer(m_target, 0);

			return buffer;
		}

		void unlock() {
			glBindBuffer(m_target, m_id);
			glUnmapBuffer(m_target);
			glBindBuffer(m_target, 0);
		}

	protected:
		Buffer(GLenum target, GLenum usage)
			: m_target(target), m_usage(usage), m_id(0) {
				glGenBuffers(1, &m_id);
		}

		void copyData(GLsizeiptr size, const GLvoid* data) {
			GX_ASSERT(data != NULL, "Try copy null data.");
			if (data != NULL)
			{
				glBindBuffer(m_target, m_id);
				glBufferData(m_target, size, data, m_usage);
				glBindBuffer(m_target, 0);
			}
		}

		void copySubData(GLsizeiptr size, const GLvoid* data, GLsizeiptr offset) {
			GX_ASSERT(data != NULL, "Try copy null data.");

			if (data != NULL)
			{
				glBindBuffer(m_target, m_id);
				glBufferSubData(m_target, offset, size, data);
				glBindBuffer(m_target, 0);
			}
		}

		void reserve(const GLsizeiptr size) {
			glBindBuffer(m_target, m_id);
			glBufferData(m_target, size, NULL, m_usage);
			glBindBuffer(m_target, 0);
		}

		GLuint m_id;
		GLenum m_target; // BufferTargetBinding?
		GLenum m_usage; // BufferUsage::Enum?

	private:
		Buffer(const Buffer&);
		Buffer& operator=(const Buffer&);
	};
}

#endif /* __gx_buffer_h__ */