//
// C++ wrapper for framebuffer object
//
// Author: Alex V. Boreskoff <alexboreskoff@mtu-net.ru>, <steps3d@narod.ru>
//

#include	"libExt.h"
#include	"FrameBuffer.h"

FrameBuffer :: FrameBuffer  ( int theWidth, int theHeight, int theFlags )
{
	width         = theWidth;
	height        = theHeight;
	flags         = theFlags;
	frameBuffer   = 0;
	depthBuffer   = 0;
	stencilBuffer = 0;
}

FrameBuffer :: ~FrameBuffer ()
{
	if ( depthBuffer != 0 )
		glDeleteRenderbuffersEXT ( 1, &depthBuffer );

	if ( stencilBuffer != 0 )
		glDeleteRenderbuffersEXT ( 1, &stencilBuffer );

	if ( frameBuffer != 0 )
		glDeleteFramebuffersEXT ( 1, &frameBuffer );
}

bool	FrameBuffer :: create ()
{
	if ( width <= 0 || height <= 0 )
		return false;

	glGenFramebuffersEXT ( 1, &frameBuffer );
	glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );

	int	depthFormat   = 0;
	int stencilFormat = 0;

	if ( flags & depth16 )
		depthFormat = GL_DEPTH_COMPONENT16_ARB;
	else
	if ( flags & depth24 )
		depthFormat = GL_DEPTH_COMPONENT24_ARB;
	else
	if ( flags & depth32 )
		depthFormat = GL_DEPTH_COMPONENT32_ARB;

	if ( flags & stencil1 )
		stencilFormat = GL_STENCIL_INDEX1_EXT;
	else
	if ( flags & stencil4 )
		stencilFormat = GL_STENCIL_INDEX4_EXT;
	else
	if ( flags & stencil8 )
		stencilFormat = GL_STENCIL_INDEX8_EXT;
	else
	if ( flags & stencil16 )
		stencilFormat = GL_STENCIL_INDEX16_EXT;


	if ( depthFormat != 0 )
	{
		glGenRenderbuffersEXT        ( 1, &depthBuffer );
		glBindRenderbufferEXT        ( GL_RENDERBUFFER_EXT, depthBuffer );
		glRenderbufferStorageEXT     ( GL_RENDERBUFFER_EXT, depthFormat, width, height );
		glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT,  GL_DEPTH_ATTACHMENT_EXT,
		                               GL_RENDERBUFFER_EXT, depthBuffer );
	}

	if ( stencilFormat != 0 )
	{
		glGenRenderbuffersEXT        ( 1, &stencilBuffer );
		glBindRenderbufferEXT        ( GL_RENDERBUFFER_EXT, stencilBuffer );
		glRenderbufferStorageEXT     ( GL_RENDERBUFFER_EXT, stencilFormat, width, height );
		glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT,  GL_STENCIL_ATTACHMENT_EXT,
		                               GL_RENDERBUFFER_EXT, stencilBuffer );
	}

	GLenum status = glCheckFramebufferStatusEXT ( GL_FRAMEBUFFER_EXT );
	
	glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, 0 );

	return status == GL_FRAMEBUFFER_COMPLETE_EXT;
}

bool	FrameBuffer :: isOk () const
{
	unsigned	currentFb;

	glGetIntegerv ( GL_FRAMEBUFFER_BINDING_EXT, (int *)&currentFb );

	if ( currentFb != frameBuffer )
	{
		glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );
		glReadBuffer         ( GL_COLOR_ATTACHMENT0_EXT );
	}

	int		status   = glCheckFramebufferStatusEXT ( GL_FRAMEBUFFER_EXT );

	if ( currentFb != frameBuffer )
		glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, currentFb );

	return 	status == GL_FRAMEBUFFER_COMPLETE_EXT;
}

bool	FrameBuffer :: bind ()
{
	if ( frameBuffer == 0 )
		return false;

	glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, frameBuffer );
	glReadBuffer         ( GL_COLOR_ATTACHMENT0_EXT );

	return true;
}

bool	FrameBuffer :: unbind ()
{
	if ( frameBuffer == 0 )
		return false;

	glBindFramebufferEXT ( GL_FRAMEBUFFER_EXT, 0 );

	return true;
}

bool	FrameBuffer :: attachColorTexture ( GLenum target, unsigned texId, int no )
{
	if ( frameBuffer == 0 )
		return false;

	if ( target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB) )
		return false;

	glBindTexture ( target, texId );
	glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + no, target, texId, 0 );

	return true;
}

bool	FrameBuffer :: attachDepthTexture ( GLenum target, unsigned texId )
{
	if ( frameBuffer == 0 )
		return false;

	glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, target, texId, 0 );

	return true;
}

unsigned	FrameBuffer :: createColorTexture ( GLenum format, GLenum internalFormat )
{
	unsigned	tex;

	glGenTextures   ( 1, &tex );
    glBindTexture   ( GL_TEXTURE_2D, tex );
    glTexImage2D    ( GL_TEXTURE_2D, 0, internalFormat, getWidth (), getHeight (), 0,
                      format, GL_UNSIGNED_BYTE, NULL );

	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST   );
	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST   );

    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

    return tex;
}

bool	FrameBuffer :: isSupported ()
{
	return isExtensionSupported ( "EXT_framebuffer_object" );
}

int		FrameBuffer :: maxColorAttachemnts ()
{
    int n;

    glGetIntegerv ( GL_MAX_COLOR_ATTACHMENTS_EXT, &n );

	return n;
}

int		FrameBuffer :: maxSize ()
{
    int sz;

    glGetIntegerv ( GL_MAX_RENDERBUFFER_SIZE_EXT, &sz );

	return sz;
}
