/*
								+----------------------------------+
								|                                  |
								|     ***  VBO wrap class  ***     |
								|                                  |
								|   Copyright © -tHE SWINe- 2005   |
								|                                  |
								|      VertexBufferObject.cpp      |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file gl2/VertexBufferObject.cpp
 *	@date 2005
 *	@author -tHE SWINe-
 *	@brief VBO wrap class
 *
 *	@date 2006-08-13
 *
 *	passed code revision
 *
 *	integers, contanining just true or false were changed to bool
 *	added CPixelBufferObject class
 *	changed behavior of CVertexBufferObject::UnMap() so it returns true when buffer was not mapped
 *	(thereby return walue false always mean buffer data were lost)
 *	renamed CVBOUnBinder to CBufferObjectUnBinder
 *
 *	@date 2007-03-14
 *
 *	passed code revision
 *
 *	fixed flaw in CGLVertexBufferObject::BindAs() where explicit bind target was not used
 *	renamed CVertexBufferObject to CGLVertexBufferObject, renamed CPixelBufferObject to
 *	CGLPixelBufferObject and CBufferObjectUnBinder to CGLBufferObjectUnBinder
 *
 *	@date 2007-05-10
 *
 *	removed debug buffer properties consistency checks as they fails in case another
 *	buffer (or no buffer) is bound, properties are now returned by inline functions
 *	added CGLVertexBufferObject::n_Primary_Target() and CGLVertexBufferObject::n_Bind_Target()
 *	functions to reduce confusion about what target should be unbound
 *
 *	@date 2007-11-10
 *
 *	improved linux compatibility
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64
 *
 *	@date 2009-05-04
 *
 *	fixed mixed windows / linux line endings
 *
 */

#include "../NewFix.h"

#include "../CallStack.h"
#include <vector>
#include <stdio.h>
#include <math.h>
#include <time.h>
#include "OpenGL20.h"
#include "OpenGLState.h"

#include "VertexBufferObject.h"

/*
 *								=== CGLVertexBufferObject ===
 */

/*
 *	CGLVertexBufferObject::CGLVertexBufferObject(GLenum n_target = GL_ARRAY_BUFFER_ARB)
 *		- default constructor
 *		- valid parameters are GL_ARRAY_BUFFER_ARB (vertex data) and
 *		  GL_ELEMENT_ARRAY_BUFFER_ARB (vertex indices)
 */
CGLVertexBufferObject::CGLVertexBufferObject(GLenum n_target/* = GL_ARRAY_BUFFER_ARB*/)
	:m_n_buffer_id(0), m_n_size(0), m_n_usage(0/*GL_STATIC_DRAW_ARB*/),
	m_n_primary_target(n_target), m_n_bind_target(n_target), m_b_mapped(false)
{
	__FuncGuard("CGLVertexBufferObject::CGLVertexBufferObject");

	CGLExtensionHandler::n_GetVertexBufferObjectARBFuncPointers();
}

/*
 *	CGLVertexBufferObject::~CGLVertexBufferObject()
 *		- default destructor
 */
CGLVertexBufferObject::~CGLVertexBufferObject()
{
	__FuncGuard("CGLVertexBufferObject::~CGLVertexBufferObject");

	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)
				glBindBufferARB(m_n_bind_target, m_n_buffer_id);
			glUnmapBufferARB(m_n_bind_target);
			if(n_former_buffer != m_n_buffer_id)
				glBindBufferARB(m_n_bind_target, n_former_buffer);
			// determine id of bound buffer, bind self, unmap, bind buffer bound before
			// therefore not damaging OpenGL state
		}
		glDeleteBuffersARB(1, &m_n_buffer_id);
		CGLState::NotifyDeleteBufferObject(m_n_buffer_id);
		m_n_buffer_id = 0;
		m_n_size = 0;
		m_n_usage = 0; //GL_STATIC_DRAW_ARB?
	}
}

/*
 *	static bool CGLVertexBufferObject::b_Supported()
 *		- returns true when GL_ARB_VERTEX_BUFFER_OBJECT extension is supported
 *		  and all entry points are located
 */
bool CGLVertexBufferObject::b_Supported()
{
	__FuncGuard("CGLVertexBufferObject::b_Supported");

	return CGLExtensionHandler::b_SupportedExtension("GL_ARB_vertex_buffer_object") &&
		!CGLExtensionHandler::n_GetVertexBufferObjectARBFuncPointers();
}

/*
 *	bool CGLVertexBufferObject::Alloc(CGLState *p_state, unsigned int n_size,
 *		const void *p_data = 0, GLenum n_usage = GL_STATIC_DRAW_ARB)
 *		- alloc buffer data of size n_size bytes, can be initialized to p_data in case
 *		  non-NULL pointer is supplied
 *		- n_usage is optimalization hint only, it is one of:
 *		  GL_STREAM_DRAW_ARB, GL_STREAM_READ_ARB, GL_STREAM_COPY_ARB
 *		  GL_STATIC_DRAW_ARB, GL_STATIC_READ_ARB, GL_STATIC_COPY_ARB
 *		  GL_DYNAMIC_DRAW_ARB, GL_DYNAMIC_READ_ARB, GL_DYNAMIC_COPY_ARB
 *		- in case buffer was allocated already, it's data are freed as soon as there are no
 *		  OpenGL commands, sourcing data from it (it shouldn't block program execution,
 *		  depends on graphics card vendor and driver version) and new buffer is allocated
 *		- it always returns true, but it may generate GL_OUT_OF_MEMORY error (call glGetError())
 */
bool CGLVertexBufferObject::Alloc(CGLState *p_state, unsigned int n_size,
	const void *p_data/*= 0*/, GLenum n_usage)
{
	__FuncGuard("CGLVertexBufferObject::Alloc");

	if(!m_n_buffer_id) {
		glGenBuffersARB(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

	p_state->BindBuffer(m_n_bind_target = m_n_primary_target, m_n_buffer_id);
	glBufferDataARB(m_n_primary_target, n_size, p_data, n_usage);

	m_n_size = n_size;
	m_n_usage = n_usage;

	return true; // return value is for "software" (non-opengl accelerated)
	// implementations only that may actually alloc something
}

/*
 *	bool CGLVertexBufferObject::Bind(CGLState *p_state)
 *		- binds the buffer with target set to target, passed to constructor
 *		  (when called first time, it creates OpenGL object as well)
 *		- returns true on success, false on failure
 */
bool CGLVertexBufferObject::Bind(CGLState *p_state)
{
	__FuncGuard("CGLVertexBufferObject::Bind");

	if(!m_n_buffer_id) {
		glGenBuffersARB(1, &m_n_buffer_id);
		if(!m_n_buffer_id)
			return false;
	}

	p_state->BindBuffer(m_n_bind_target = m_n_primary_target, m_n_buffer_id);

	return true;
}

/*
 *	bool CGLVertexBufferObject::BindAs(CGLState *p_state,
 *		GLenum n_target = GL_PIXEL_PACK_BUFFER_ARB)
 *		- binds the buffer with target n_target
 *		  (when called first time, it creates OpenGL object as well)
 *		- returns true on success, false on failure
 *		- valid parameters are GL_ARRAY_BUFFER_ARB (vertex data) and
 *		  GL_ELEMENT_ARRAY_BUFFER_ARB (vertex indices)
 *		- note GL_PIXEL_PACK_BUFFER_ARB and GL_PIXEL_UNPACK_BUFFER_ARB are possible, but
 *		  should only be used with CGLPixelBufferObject (it's essentialy the same class, but it
 *		  makes you sure you check hardware support of this extension)
 *		- you can copy data using glReadPixels or glGetTexImage with <data> parameter,
 *		  returned by call to p_OffsetPointer(0)
 */
bool CGLVertexBufferObject::BindAs(CGLState *p_state,
	GLenum n_target/*= GL_PIXEL_PACK_BUFFER_ARB*/)
{
	__FuncGuard("CGLVertexBufferObject::BindAs");

	if(!m_n_buffer_id) {
		glGenBuffersARB(1, &m_n_buffer_id);
		if(!m_n_buffer_id)
			return false;
	}

	p_state->BindBuffer(m_n_bind_target = n_target, m_n_buffer_id);

	return true;
}

/*
 *	void *CGLVertexBufferObject::p_OffsetPointer(unsigned int n_offset)
 *		- returns offset pointer for use with glDrawElements, etc ... (always, can never fail)
 *		- do not use this pointer to access data, it doesn't point to client address space
 */
/*void *CGLVertexBufferObject::p_OffsetPointer(unsigned int n_offset)
{
	__FuncGuard("CGLVertexBufferObject::p_OffsetPointer");

	return (void*)((char*)NULL + n_offset);
}*/

/*
 *	void CGLVertexBufferObject::BufferSubData(CGLState *p_state, unsigned int n_offset,
 *		unsigned int n_size, const void *p_data)
 *		- re-specify part (or all) of buffer data from p_data
 *		- n_offset must be greater or equal to 0, n_offset + n_size must
 *		  be less or equal to buffer size, otherwise OpenGL error is generated (glGetError())
 */
void CGLVertexBufferObject::BufferSubData(CGLState *p_state, unsigned int n_offset,
	unsigned int n_size, const void *p_data)
{
	__FuncGuard("CGLVertexBufferObject::BufferSubData");

	p_state->BindBuffer(m_n_bind_target, m_n_buffer_id);
	// must be bound

	glBufferSubDataARB(m_n_primary_target, n_offset, n_size, p_data);
}

/*
 *	void CGLVertexBufferObject::GetBufferSubData(CGLState *p_state, unsigned int n_offset,
 *		unsigned int n_size, void *p_data)
 *		- read part (or all) of buffer data into p_data
 *		- n_offset must be greater or equal to 0, n_offset + n_size must
 *		  be less or equal to buffer size, otherwise OpenGL error is generated (glGetError())
 */
void CGLVertexBufferObject::GetBufferSubData(CGLState *p_state, unsigned int n_offset,
		unsigned int n_size, void *p_data)
{
	__FuncGuard("CGLVertexBufferObject::GetBufferSubData");

	p_state->BindBuffer(m_n_bind_target, m_n_buffer_id);
	// must be bound

	glGetBufferSubDataARB(m_n_primary_target, n_offset, n_size, p_data);
}

/*
 *	void *CGLVertexBufferObject::p_Map(CGLState *p_state, GLenum n_access)
 *		- map buffer into client address space (one buffer only can be mapped at a time)
 *		- n_access is buffer access, it can be one of:
 *		  GL_READ_ONLY_ARB, GL_WRITE_ONLY_ARB, GL_READ_WRITE_ARB
 *		- return client address space pointer in case of success or NULL on failure
 */
void *CGLVertexBufferObject::p_Map(CGLState *p_state, GLenum n_access)
{
	__FuncGuard("CGLVertexBufferObject::p_Map");

	p_state->BindBuffer(m_n_bind_target, m_n_buffer_id);
	// must be bound

	void *p_result = glMapBufferARB(m_n_primary_target, n_access);

	if(p_result != NULL) {
		m_n_access = n_access;
		m_b_mapped = true;
	}

	return p_result;
}

/*
 *	void *CGLVertexBufferObject::p_BufferPointer(CGLState *p_state)
 *		- in case buffer object has been successfuly mapped, return client-space pointer
 *		  otherwise return NULL
 */
void *CGLVertexBufferObject::p_BufferPointer(CGLState *p_state)
{
	__FuncGuard("CGLVertexBufferObject::p_BufferPointer");

	if(!m_b_mapped)
		return 0;

	p_state->BindBuffer(m_n_bind_target, m_n_buffer_id);
	// must be bound

	void *p_ptr;
	glGetBufferPointervARB(m_n_primary_target, GL_BUFFER_MAP_POINTER_ARB, &p_ptr);

	return p_ptr;
} // return pointer only in case it's been mapped

/*
 *	bool CGLVertexBufferObject::UnMap(CGLState *p_state)
 *		- unmaps buffer, returns true on success or in case buffer was not mapped
 *		- returns false on failure (buffer contents were lost, may be caused by
 *		  screen-mode switch while buffer was bound)
 */
bool CGLVertexBufferObject::UnMap(CGLState *p_state)
{
	__FuncGuard("CGLVertexBufferObject::UnMap");

	if(!m_b_mapped)
		return true;

	p_state->BindBuffer(m_n_bind_target, m_n_buffer_id);
	// must be bound

	m_b_mapped = false;

	return glUnmapBufferARB(m_n_primary_target) == GL_TRUE;
}

/*
 *	int CGLVertexBufferObject::n_Size()
 *		- returns size of buffer in bytes
 */
/*int CGLVertexBufferObject::n_Size()
{
	__FuncGuard("CGLVertexBufferObject::n_Size");

#ifdef _DEBUG
	int n_size = m_n_size; // must be bound
	glGetBufferParameterivARB(m_n_primary_target, GL_BUFFER_SIZE_ARB, &n_size);
	_ASSERTE(n_size == m_n_size);
#endif

	return m_n_size;
}*/

/*
 *	int CGLVertexBufferObject::n_Usage()
 *		- returns buffer ussage; one of:
 *		  GL_STREAM_DRAW_ARB, GL_STREAM_READ_ARB, GL_STREAM_COPY_ARB
 *		  GL_STATIC_DRAW_ARB, GL_STATIC_READ_ARB, GL_STATIC_COPY_ARB
 *		  GL_DYNAMIC_DRAW_ARB, GL_DYNAMIC_READ_ARB, GL_DYNAMIC_COPY_ARB
 */
/*int CGLVertexBufferObject::n_Usage()
{
	__FuncGuard("CGLVertexBufferObject::n_Usage");

	if(!m_b_mapped) {
		_ASSERTE(!b_Mapped()); // check the ogl state against m_b_mapped
		return 0;
	}

#ifdef _DEBUG
	int n_usage = m_n_usage; // must be bound
	glGetBufferParameterivARB(m_n_primary_target, GL_BUFFER_USAGE_ARB, &n_usage);
	_ASSERTE(n_usage == m_n_usage);
#endif

	return m_n_usage;
}*/

/*
 *	bool CGLVertexBufferObject::b_Mapped()
 *		- returns true in case buffer contents were mapped into client-space memory
 *		- it's possible to obtain pointer to buffer data by call to p_BufferPointer()
 */
/*bool CGLVertexBufferObject::b_Mapped()
{
	__FuncGuard("CGLVertexBufferObject::b_Mapped");

#ifdef _DEBUG
	int b_mapped = m_b_mapped; // must be bound
	glGetBufferParameterivARB(m_n_primary_target, GL_BUFFER_MAPPED_ARB, &b_mapped);
	_ASSERTE(m_b_mapped == (b_mapped == GL_TRUE));
#endif

	return m_b_mapped;
}*/

/*
 *	int CGLVertexBufferObject::n_AccessFlag()
 *		- returns access flag to buffer in case it has been mapped (one of GL_READ_ONLY_ARB,
 *		  GL_WRITE_ONLY_ARB, GL_READ_WRITE_ARB), in case buffer is not mapped, return 0
 */
/*int CGLVertexBufferObject::n_AccessFlag()
{
	__FuncGuard("CGLVertexBufferObject::n_AccessFlag");

	if(!m_b_mapped) {
		_ASSERTE(!b_Mapped()); // check the ogl state against m_b_mapped
		return 0;
	}

#ifdef _DEBUG
	int n_access = m_n_access; // must be bound
	glGetBufferParameterivARB(m_n_primary_target, GL_BUFFER_ACCESS_ARB, &n_access);
	_ASSERTE(m_n_access == n_access);
#endif

	return m_n_access;
}*/

/*
 *								=== ~CGLVertexBufferObject ===
 */

/*
 *								=== CGLPixelBufferObject ===
 */

/*
 *	CGLPixelBufferObject::CGLPixelBufferObject(GLenum n_target = GL_PIXEL_PACK_BUFFER_ARB)
 *		- default constructor, valid parameters are GL_ARRAY_BUFFER_ARB (vertex data),
 *		  GL_ELEMENT_ARRAY_BUFFER_ARB (vertex indices), GL_PIXEL_PACK_BUFFER_ARB
 *		  (pixel-buffer in mode for pixel storage) and GL_PIXEL_UNPACK_BUFFER_ARB
 *		  (pixel-buffer in mode for for reading pixels)
 */
CGLPixelBufferObject::CGLPixelBufferObject(GLenum n_target/* = GL_PIXEL_PACK_BUFFER_ARB*/)
	:CGLVertexBufferObject(n_target)
{}

/*
 *	static bool CGLPixelBufferObject::b_Supported()
 *		- returns true when GL_ARB_VERTEX_BUFFER_OBJECT and GL_ARB_PIXEL_BUFFER_OBJECT
 *		  extensions are supported and all entry points were found
 */
bool CGLPixelBufferObject::b_Supported()
{
	return CGLVertexBufferObject::b_Supported() &&
		CGLExtensionHandler::b_SupportedExtension("GL_ARB_pixel_buffer_object");
}

/*
 *								=== ~CGLPixelBufferObject ===
 */

/*
 *		-end-of-file-
 */
