/*
								+-----------------------------------+
								|                                   |
								|     ***  VBO / PBO class  ***     |
								|                                   |
								|   Copyright  © -tHE SWINe- 2009   |
								|                                   |
								|         BufferObjects.cpp         |
								|                                   |
								+-----------------------------------+
*/

/**
 *	@file gl3/BufferObjects.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief wrapper for GL_ARB_vertex_buffer_object / GL_ARB_pixel_buffer_object extensions
 */

#include "../NewFix.h"
#include "../CallStack.h"
#include "BufferObjects.h"

/*
 *								=== CGLBufferObject ===
 */

CGLBufferObject::CGLBufferObject(GLenum n_target/* = GL_ARRAY_BUFFER*/)
	:m_n_buffer_id(0), m_n_size(0), m_n_usage(0/*GL_STATIC_DRAW*/),
	m_n_default_target(n_target), m_n_bind_target(n_target), m_b_mapped(false)
{}

CGLBufferObject::~CGLBufferObject()
{
	if(m_n_buffer_id) {
		if(m_b_mapped) {
			GLuint n_former_buffer;
			glGetIntegerv(m_n_bind_target, (int*)&n_former_buffer);
			if(n_former_buffer != m_n_buffer_id)
				glBindBuffer(m_n_bind_target, m_n_buffer_id);
			glUnmapBuffer(m_n_bind_target);
			if(n_former_buffer != m_n_buffer_id)
				glBindBuffer(m_n_bind_target, n_former_buffer);
			// determine id of bound buffer, bind self, unmap, bind buffer bound before
			// therefore not damaging OpenGL state
		}
		glDeleteBuffers(1, &m_n_buffer_id);
		//CGLState::NotifyDeleteBufferObject(m_n_buffer_id);
	}
}

bool CGLBufferObject::Alloc(unsigned int n_size,
	const void *p_data/*= 0*/, GLenum n_usage)
{
	if(!m_n_buffer_id) {
		glGenBuffers(1, &m_n_buffer_id);
		if(!m_n_buffer_id)
			return false;
	}
	// called first time and buffer is not yet allocated ... alloc a new buffer as primary target

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	glBufferData(m_n_default_target, n_size, p_data, n_usage);

	m_n_size = n_size;
	m_n_usage = n_usage;

	return true;
}

bool CGLBufferObject::Bind()
{
	if(!m_n_buffer_id) {
		glGenBuffers(1, &m_n_buffer_id);
		if(!m_n_buffer_id)
			return false;
	}

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);

	return true;
}

bool CGLBufferObject::BindAs(GLenum n_target/*= GL_PIXEL_PACK_BUFFER_ARB*/)
{
	if(!m_n_buffer_id) {
		glGenBuffers(1, &m_n_buffer_id);
		if(!m_n_buffer_id)
			return false;
	}

	glBindBuffer(m_n_bind_target = n_target, m_n_buffer_id);

	return true;
}

void CGLBufferObject::BufferSubData(unsigned int n_offset,
	unsigned int n_size, const void *p_data)
{
	_ASSERTE(n_offset + n_size <= m_n_size);
	_ASSERTE(p_data);
	_ASSERTE(m_n_buffer_id);

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	glBufferSubData(m_n_default_target, n_offset, n_size, p_data);
}

void CGLBufferObject::GetBufferSubData(unsigned int n_offset,
		unsigned int n_size, void *p_data)
{
	_ASSERTE(n_offset + n_size <= m_n_size);
	_ASSERTE(p_data);
	_ASSERTE(m_n_buffer_id);

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	glGetBufferSubData(m_n_default_target, n_offset, n_size, p_data);
}

void *CGLBufferObject::p_Map(GLenum n_access)
{
	_ASSERTE(m_n_buffer_id);

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	void *p_result = glMapBuffer(m_n_default_target, n_access);
	if(p_result) {
		m_n_access = n_access;
		m_b_mapped = true;
	}
	return p_result;
}

void *CGLBufferObject::p_BufferPointer()
{
	if(!m_b_mapped)
		return 0;

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	void *p_ptr;
	glGetBufferPointerv(m_n_default_target, GL_BUFFER_MAP_POINTER, &p_ptr);
	return p_ptr;
}

bool CGLBufferObject::UnMap()
{
	if(!m_b_mapped)
		return true;

	glBindBuffer(m_n_bind_target = m_n_default_target, m_n_buffer_id);
	m_b_mapped = false;
	return glUnmapBuffer(m_n_default_target) == GL_TRUE;
}

/*
 *								=== ~CGLBufferObject ===
 */
