#include "GlRenderSystem/PrecompiledHeader.hpp"

#include "GlRenderSystem/GlFrameBuffer.hpp"
#include "GlRenderSystem/OpenGl.hpp"
#include "GlRenderSystem/GlBuffer.hpp"
#include "GlRenderSystem/GlTextureRenderer.hpp"

using namespace GlRender;
using namespace Castor3D;
using namespace Castor;

//*************************************************************************************************

GlRenderBuffer :: GlRenderBuffer( OpenGl & p_gl, eGL_RENDERBUFFER_STORAGE p_eInternal, RenderBuffer & p_renderBuffer )
	:	m_uiGlName		( eGL_INVALID_INDEX )
	,	m_eInternal		( p_eInternal		)
	,	m_size			( 0, 0				)
	,	m_renderBuffer	( p_renderBuffer	)
	,	m_gl			( p_gl				)
{
	CASTOR_ASSERT( m_eInternal != 0 );
}

GlRenderBuffer :: ~GlRenderBuffer()
{
}

bool GlRenderBuffer :: Create()
{
	if( m_gl.HasFbo() )
	{
		m_gl.GenRenderbuffers( 1, &m_uiGlName );
	}

	return m_uiGlName != eGL_INVALID_INDEX;
}

void GlRenderBuffer :: Destroy()
{
	if( m_uiGlName != eGL_INVALID_INDEX )
	{
		m_gl.DeleteRenderbuffers( 1, &m_uiGlName );
		m_uiGlName = uint32_t( eGL_INVALID_INDEX );
	}
}

bool GlRenderBuffer :: Initialise( Size const & p_size )
{
	bool l_bReturn = m_size == p_size;

	if( !l_bReturn && Bind() )
	{
		m_size = p_size;

		if( m_renderBuffer.GetSamplesCount() > 1 )
		{
			l_bReturn = m_gl.RenderbufferStorageMultisample( eGL_RENDERBUFFER_MODE_DEFAULT, m_renderBuffer.GetSamplesCount(), m_eInternal, p_size );
		}
		else
		{
			l_bReturn = m_gl.RenderbufferStorage( eGL_RENDERBUFFER_MODE_DEFAULT, m_eInternal, p_size );
		}

		Unbind();
	}

	return l_bReturn;
}

void GlRenderBuffer :: Cleanup()
{
}

bool GlRenderBuffer :: Bind()
{
	return m_uiGlName != eGL_INVALID_INDEX && m_gl.BindRenderbuffer( eGL_RENDERBUFFER_MODE_DEFAULT, m_uiGlName );
}

void GlRenderBuffer :: Unbind()
{
	m_uiGlName != eGL_INVALID_INDEX && m_gl.BindRenderbuffer( eGL_RENDERBUFFER_MODE_DEFAULT, 0 );
}

bool GlRenderBuffer :: Resize( Size const & p_size )
{
	bool l_bReturn = m_size == p_size;

	if( !l_bReturn && Bind() )
	{
		m_size = p_size;

		if( m_renderBuffer.GetSamplesCount() > 1 )
		{
			l_bReturn = m_gl.RenderbufferStorageMultisample( eGL_RENDERBUFFER_MODE_DEFAULT, m_renderBuffer.GetSamplesCount(), m_eInternal, p_size );
		}
		else
		{
			l_bReturn = m_gl.RenderbufferStorage( eGL_RENDERBUFFER_MODE_DEFAULT, m_eInternal, p_size );
		}

		Unbind();
	}

	return l_bReturn;
}

//*************************************************************************************************

GlColourRenderBuffer :: GlColourRenderBuffer( OpenGl & p_gl, ePIXEL_FORMAT p_eFormat )
	:	ColourRenderBuffer	( p_eFormat										)
	,	m_glRenderBuffer	( p_gl, p_gl.GetRboStorage( p_eFormat ), *this	)
{
}

GlColourRenderBuffer :: ~GlColourRenderBuffer()
{
}

bool GlColourRenderBuffer :: Create()
{
	return m_glRenderBuffer.Create();
}

void GlColourRenderBuffer :: Destroy()
{
	m_glRenderBuffer.Destroy();
}

bool GlColourRenderBuffer :: Initialise( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Initialise( p_size );
}

void GlColourRenderBuffer :: Cleanup()
{
	m_glRenderBuffer.Cleanup();
}

bool GlColourRenderBuffer :: Bind()
{
	return m_glRenderBuffer.Bind();
}

void GlColourRenderBuffer :: Unbind()
{
	m_glRenderBuffer.Unbind();
}

bool GlColourRenderBuffer :: Resize( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Resize( p_size );
}

//*************************************************************************************************

GlDepthRenderBuffer :: GlDepthRenderBuffer( OpenGl & p_gl, ePIXEL_FORMAT p_eFormat )
	:	DepthRenderBuffer	( p_eFormat										)
	,	m_glRenderBuffer	( p_gl, p_gl.GetRboStorage( p_eFormat ), *this	)
{
}

GlDepthRenderBuffer :: ~GlDepthRenderBuffer()
{
}

bool GlDepthRenderBuffer :: Create()
{
	return m_glRenderBuffer.Create();
}

void GlDepthRenderBuffer :: Destroy()
{
	m_glRenderBuffer.Destroy();
}

bool GlDepthRenderBuffer :: Initialise( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Initialise( p_size );
}

void GlDepthRenderBuffer :: Cleanup()
{
	m_glRenderBuffer.Cleanup();
}

bool GlDepthRenderBuffer :: Bind()
{
	return m_glRenderBuffer.Bind();
}

void GlDepthRenderBuffer :: Unbind()
{
	m_glRenderBuffer.Unbind();
}

bool GlDepthRenderBuffer :: Resize( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Resize( p_size );
}

//*************************************************************************************************

GlStencilRenderBuffer :: GlStencilRenderBuffer( OpenGl & p_gl, ePIXEL_FORMAT p_eFormat )
	:	StencilRenderBuffer	( p_eFormat											)
	,	m_glRenderBuffer	( p_gl, p_gl.GetRboStorage( p_eFormat ), *this	)
{
}

GlStencilRenderBuffer :: ~GlStencilRenderBuffer()
{
}

bool GlStencilRenderBuffer :: Create()
{
	return m_glRenderBuffer.Create();
}

void GlStencilRenderBuffer :: Destroy()
{
	m_glRenderBuffer.Destroy();
}

bool GlStencilRenderBuffer :: Initialise( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Initialise( p_size );
}

void GlStencilRenderBuffer :: Cleanup()
{
	m_glRenderBuffer.Cleanup();
}

bool GlStencilRenderBuffer :: Bind()
{
	return m_glRenderBuffer.Bind();
}

void GlStencilRenderBuffer :: Unbind()
{
	m_glRenderBuffer.Unbind();
}

bool GlStencilRenderBuffer :: Resize( Castor::Size const & p_size )
{
	return m_glRenderBuffer.Resize( p_size );
}

//*************************************************************************************************

GlRenderBufferAttachment :: GlRenderBufferAttachment( OpenGl & p_gl, RenderBufferSPtr p_pRenderBuffer )
	:	RenderBufferAttachment	( p_pRenderBuffer								)
	,	m_eGlAttachmentPoint	( eGL_RENDERBUFFER_ATTACHMENT_NONE				)
	,	m_eGlStatus				( eGL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT	)
	,	m_gl					( p_gl											)
{
}

GlRenderBufferAttachment :: ~GlRenderBufferAttachment()
{
}

bool GlRenderBufferAttachment :: DownloadBuffer( PxBufferBaseSPtr p_pBuffer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		FrameBufferSPtr l_pFrameBuffer = GetFrameBuffer();
		RenderBufferSPtr l_pRenderBuffer = GetRenderBuffer();
		l_bReturn = l_pFrameBuffer != nullptr && l_pRenderBuffer != nullptr;

		if( l_bReturn && l_pFrameBuffer->Bind( eFRAMEBUFFER_MODE_MANUAL, eFRAMEBUFFER_TARGET_READ ) )
		{
			OpenGl::PixelFmt l_pxFmt = m_gl.Get( p_pBuffer->format() );
			l_bReturn = m_gl.ReadPixels( 0, 0, l_pRenderBuffer->GetWidth(), l_pRenderBuffer->GetHeight(), l_pxFmt.Format, l_pxFmt.Type, p_pBuffer->ptr() );
			l_pFrameBuffer->Unbind();
		}
	}

	return l_bReturn;
}

bool GlRenderBufferAttachment :: Blit( FrameBufferSPtr p_pBuffer, Rect const & p_rectSrc, Rect const & p_rectDst, eINTERPOLATION_MODE p_eInterpolation )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		l_bReturn = m_eGlStatus == eGL_FRAMEBUFFER_COMPLETE;

		GlFrameBufferSPtr l_pBuffer = std::static_pointer_cast< GlFrameBuffer >( p_pBuffer );

		if( l_bReturn )
		{
			l_bReturn = m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_READ, m_pGlFrameBuffer.lock()->GetGlName() );
		}

		if( l_bReturn )
		{
			l_bReturn = m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_DRAW, l_pBuffer->GetGlName() );
		}

		if( l_bReturn )
		{
			l_bReturn = m_gl.ReadBuffer( m_gl.Get( m_eGlAttachmentPoint ) );
		}

		if( l_bReturn )
		{
			if( m_eGlAttachmentPoint == eGL_RENDERBUFFER_ATTACHMENT_DEPTH )
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_COMPONENT_DEPTH, eGL_INTERPOLATION_MODE_NEAREST );
			}
			else if( m_eGlAttachmentPoint == eGL_RENDERBUFFER_ATTACHMENT_STENCIL )
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_COMPONENT_STENCIL, eGL_INTERPOLATION_MODE_NEAREST );
			}
			else
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_COMPONENT_COLOR, m_gl.Get( p_eInterpolation ) );
			}
		}
	}

	return l_bReturn;
}

bool GlRenderBufferAttachment :: DoAttach( eATTACHMENT_POINT p_eAttachment, FrameBufferSPtr p_pFrameBuffer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		m_eGlAttachmentPoint = m_gl.GetRboAttachment( p_eAttachment );
		uint32_t l_uiGlName;

		switch( GetRenderBuffer()->GetComponent() )
		{
		case eBUFFER_COMPONENT_COLOUR:	l_uiGlName = std::static_pointer_cast< GlColourRenderBuffer		>( GetRenderBuffer() )->GetGlName();	break;
		case eBUFFER_COMPONENT_DEPTH:	l_uiGlName = std::static_pointer_cast< GlDepthRenderBuffer		>( GetRenderBuffer() )->GetGlName();	break;
		case eBUFFER_COMPONENT_STENCIL:	l_uiGlName = std::static_pointer_cast< GlStencilRenderBuffer	>( GetRenderBuffer() )->GetGlName();	break;
		default:						l_uiGlName = uint32_t( eGL_INVALID_INDEX );																break;
		}

		if( l_uiGlName != eGL_INVALID_INDEX )
		{
			m_pGlFrameBuffer = std::static_pointer_cast< GlFrameBuffer >( p_pFrameBuffer );
			l_bReturn = m_gl.FramebufferRenderbuffer( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, eGL_RENDERBUFFER_MODE_DEFAULT, l_uiGlName );
		}

		if( l_bReturn )
		{
			m_eGlStatus = eGL_FRAMEBUFFER_STATUS( m_gl.CheckFramebufferStatus( eGL_FRAMEBUFFER_MODE_DEFAULT ) );

			if( m_eGlStatus != eGL_FRAMEBUFFER_UNSUPPORTED )
			{
				m_eGlStatus = eGL_FRAMEBUFFER_COMPLETE;
			}
		}
		else
		{
			m_eGlStatus = eGL_FRAMEBUFFER_UNSUPPORTED;
		}
	}

	return l_bReturn;
}

void GlRenderBufferAttachment :: DoDetach()
{
	if( m_gl.HasFbo() )
	{
		if( m_eGlStatus != eGL_FRAMEBUFFER_UNSUPPORTED )
		{
			m_gl.FramebufferRenderbuffer( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, eGL_RENDERBUFFER_MODE_DEFAULT, 0 );
		}

		m_eGlAttachmentPoint = eGL_RENDERBUFFER_ATTACHMENT_NONE;
	}
}

//*************************************************************************************************

GlTextureAttachment :: GlTextureAttachment( OpenGl & p_gl, DynamicTextureSPtr p_pTexture )
	:	TextureAttachment		( p_pTexture									)
	,	m_eGlAttachmentPoint	( eGL_TEXTURE_ATTACHMENT_NONE					)
	,	m_eGlStatus				( eGL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT	)
	,	m_gl					( p_gl											)
{
}

GlTextureAttachment :: ~GlTextureAttachment()
{
}

bool GlTextureAttachment :: DownloadBuffer( PxBufferBaseSPtr p_pBuffer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		FrameBufferSPtr l_pFrameBuffer = GetFrameBuffer();
		DynamicTextureSPtr l_pTexture = GetTexture();
		l_bReturn = l_pFrameBuffer != nullptr && l_pTexture != nullptr;

		if( l_bReturn && l_pFrameBuffer->Bind( eFRAMEBUFFER_MODE_MANUAL, eFRAMEBUFFER_TARGET_READ ) )
		{
			m_gl.ReadBuffer( m_gl.Get( m_eGlAttachmentPoint ) );
			OpenGl::PixelFmt l_pxFmt = m_gl.Get( p_pBuffer->format() );
			l_bReturn = m_gl.ReadPixels( Position(), l_pTexture->GetDimensions(), l_pxFmt.Format, l_pxFmt.Type, p_pBuffer->ptr() );
			l_pFrameBuffer->Unbind();
		}
	}

	return l_bReturn;
}

bool GlTextureAttachment :: Blit( FrameBufferSPtr p_pBuffer, Rect const & p_rectSrc, Rect const & p_rectDst, eINTERPOLATION_MODE p_eInterpolation )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		l_bReturn = m_eGlStatus == eGL_FRAMEBUFFER_COMPLETE;

		GlFrameBufferSPtr l_pBuffer = std::static_pointer_cast< GlFrameBuffer >( p_pBuffer );

		if( l_bReturn )
		{
			l_bReturn = m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_READ, m_pGlFrameBuffer.lock()->GetGlName() );
		}

		if( l_bReturn )
		{
			l_bReturn = m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_DRAW, l_pBuffer->GetGlName() );
		}

		if( l_bReturn )
		{
			l_bReturn = m_gl.ReadBuffer( m_gl.Get( m_eGlAttachmentPoint ) );
		}

		if( l_bReturn )
		{
			if( m_eGlAttachmentPoint == eGL_TEXTURE_ATTACHMENT_DEPTH )
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_BUFFER_BIT_DEPTH, eGL_INTERPOLATION_MODE_NEAREST );
			}
			else if( m_eGlAttachmentPoint == eGL_TEXTURE_ATTACHMENT_STENCIL )
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_BUFFER_BIT_STENCIL, eGL_INTERPOLATION_MODE_NEAREST );
			}
			else
			{
				l_bReturn = m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, eGL_BUFFER_BIT_COLOR, m_gl.Get( p_eInterpolation ) );
			}
		}

		m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_READ, 0 );
		m_gl.BindFramebuffer( eGL_FRAMEBUFFER_MODE_DRAW, 0 );
	}

	return l_bReturn;
}

bool GlTextureAttachment :: DoAttach( eATTACHMENT_POINT p_eAttachment, FrameBufferSPtr p_pFrameBuffer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		m_eGlAttachmentPoint = m_gl.Get( p_eAttachment );
		GlDynamicTextureSPtr l_pTexture = std::static_pointer_cast< GlDynamicTexture >( GetTexture() );
		m_pGlFrameBuffer = std::static_pointer_cast< GlFrameBuffer >( p_pFrameBuffer );

		switch( GetAttachedTarget()  )
		{
		case eTEXTURE_TARGET_1D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_1D )
			{
				l_bReturn = m_gl.FramebufferTexture1D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0 );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_2D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_2D )
			{
				l_bReturn = m_gl.FramebufferTexture2D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0 );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_3D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_3D )
			{
				l_bReturn = m_gl.FramebufferTexture3D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0, GetAttachedLayer() );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_LAYER:
			l_bReturn = m_gl.FramebufferTextureLayer( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, l_pTexture->GetGlName(), 0, GetAttachedLayer() );
			break;
		}

		if( l_bReturn )
		{
			m_eGlStatus = eGL_FRAMEBUFFER_STATUS( m_gl.CheckFramebufferStatus( eGL_FRAMEBUFFER_MODE_DEFAULT ) );

			if( m_eGlStatus != eGL_FRAMEBUFFER_UNSUPPORTED )
			{
				m_eGlStatus = eGL_FRAMEBUFFER_COMPLETE;
			}
		}
		else
		{
			m_eGlStatus = eGL_FRAMEBUFFER_UNSUPPORTED;
		}
	}

	return l_bReturn;
}

void GlTextureAttachment :: DoDetach()
{
	if( m_gl.HasFbo() )
	{
		GlDynamicTextureSPtr l_pTexture = std::static_pointer_cast< GlDynamicTexture >( GetTexture() );

		if( m_eGlStatus != eGL_FRAMEBUFFER_UNSUPPORTED )
		{
			switch( GetAttachedTarget()  )
			{
			case eTEXTURE_TARGET_1D:
				m_gl.FramebufferTexture1D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), 0, 0 );
				break;

			case eTEXTURE_TARGET_2D:
				m_gl.FramebufferTexture2D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), 0, 0 );
				break;

			case eTEXTURE_TARGET_3D:
				m_gl.FramebufferTexture3D( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), 0, 0, GetAttachedLayer() );
				break;

			case eTEXTURE_TARGET_LAYER:
				m_gl.FramebufferTextureLayer( eGL_FRAMEBUFFER_MODE_DEFAULT, m_eGlAttachmentPoint, 0, 0, GetAttachedLayer() );
				break;
			}
		}

		m_eGlAttachmentPoint = eGL_TEXTURE_ATTACHMENT_NONE;
	}
}

//*************************************************************************************************

GlFrameBuffer :: GlFrameBuffer( OpenGl & p_gl, Engine * p_pEngine )
	:	FrameBuffer	( p_pEngine						)
	,	m_uiGlName	( uint32_t( eGL_INVALID_INDEX )	)
	,	m_gl		( p_gl							)
{
}

GlFrameBuffer :: ~GlFrameBuffer()
{
}

bool GlFrameBuffer :: Create( int CU_PARAM_UNUSED( p_iSamplesCount ) )
{
	if( m_gl.HasFbo() && m_uiGlName == eGL_INVALID_INDEX )
	{
		m_gl.GenFramebuffers( 1, &m_uiGlName );
	}

	return m_uiGlName != eGL_INVALID_INDEX;
}

void GlFrameBuffer :: Destroy()
{
	if( m_uiGlName != eGL_INVALID_INDEX )
	{
		m_gl.DeleteFramebuffers( 1, &m_uiGlName );
		m_uiGlName = uint32_t( eGL_INVALID_INDEX );
	}
}

bool GlFrameBuffer :: SetDrawBuffers( uint32_t p_uiSize, eATTACHMENT_POINT const * p_eAttaches )
{
	bool l_bReturn = false;

	if( m_arrayGlAttaches.size() )
	{
		l_bReturn = SetDrawBuffers();
	}
	else if( p_uiSize )
	{
		UIntArray l_arrayAttaches;
		l_arrayAttaches.reserve( p_uiSize );

		for( uint32_t i = 0; i < p_uiSize; i++ )
		{
			if( p_eAttaches[i] >= eATTACHMENT_POINT_COLOUR0 && p_eAttaches[i] < eATTACHMENT_POINT_DEPTH )
			{
				l_arrayAttaches.push_back( m_gl.Get( p_eAttaches[i] ) );
			}
		}

		l_bReturn = m_gl.DrawBuffers( int( l_arrayAttaches.size() ), &l_arrayAttaches[0] );
	}

	return l_bReturn;
}

bool GlFrameBuffer :: SetDrawBuffers()
{
	bool l_bReturn = false;

	if( m_arrayGlAttaches.size() )
	{
		l_bReturn = m_gl.DrawBuffers( int( m_arrayGlAttaches.size() ), &m_arrayGlAttaches[0] );
	}

	return l_bReturn;
}

bool GlFrameBuffer :: SetReadBuffer( eATTACHMENT_POINT p_eAttach )
{
	bool l_bReturn = false;
	eGL_BUFFER l_eAttach = m_gl.Get( m_gl.Get( p_eAttach ) );
	UIntArrayIt l_it = std::find( m_arrayGlAttaches.begin(), m_arrayGlAttaches.end(), l_eAttach );

	if( l_it != m_arrayGlAttaches.end() )
	{
		m_gl.ReadBuffer( l_eAttach );
	}

	return l_bReturn;
}

bool GlFrameBuffer :: IsComplete()
{
	return eGL_FRAMEBUFFER_STATUS( m_gl.CheckFramebufferStatus( eGL_FRAMEBUFFER_MODE_DEFAULT ) ) == eGL_FRAMEBUFFER_COMPLETE;
}

ColourRenderBufferSPtr GlFrameBuffer :: CreateColourRenderBuffer( ePIXEL_FORMAT p_ePixelFormat )
{
	return std::make_shared< GlColourRenderBuffer >( m_gl, p_ePixelFormat );
}

DepthRenderBufferSPtr GlFrameBuffer :: CreateDepthRenderBuffer( ePIXEL_FORMAT p_ePixelFormat )
{
	return std::make_shared< GlDepthRenderBuffer >( m_gl, p_ePixelFormat );
}

StencilRenderBufferSPtr GlFrameBuffer :: CreateStencilRenderBuffer( ePIXEL_FORMAT p_ePixelFormat )
{
	return std::make_shared< GlStencilRenderBuffer >( m_gl, p_ePixelFormat );
}

bool GlFrameBuffer :: DoBind( eFRAMEBUFFER_TARGET p_eTarget )
{
	bool l_bReturn = false;

	if( m_uiGlName != eGL_INVALID_INDEX )
	{
		m_eGlBindingMode = m_gl.Get( p_eTarget );
		l_bReturn = m_gl.BindFramebuffer( m_eGlBindingMode, m_uiGlName );
	}

	return l_bReturn;
}

void GlFrameBuffer :: DoUnbind()
{
	if( m_uiGlName != eGL_INVALID_INDEX )
	{
		m_gl.BindFramebuffer( m_eGlBindingMode, 0 );
	}
}

void GlFrameBuffer :: DoAttachFba( FrameBufferAttachmentRPtr p_pAttach )
{
	DoGlAttach( p_pAttach->GetAttachmentPoint() );
}

void GlFrameBuffer :: DoDetachFba( FrameBufferAttachmentRPtr p_pAttach )
{
	DoGlDetach( p_pAttach->GetAttachmentPoint() );
}

bool GlFrameBuffer :: DoAttach( eATTACHMENT_POINT p_eAttachment, DynamicTextureSPtr p_pTexture, eTEXTURE_TARGET p_eTarget, int p_iLayer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		eGL_TEXTURE_ATTACHMENT l_eGlAttachmentPoint = m_gl.Get( p_eAttachment );
		GlDynamicTextureSPtr l_pTexture = std::static_pointer_cast< GlDynamicTexture >( p_pTexture );

		switch( p_eTarget )
		{
		case eTEXTURE_TARGET_1D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_1D )
			{
				l_bReturn = m_gl.FramebufferTexture1D( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0 );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_2D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_2D )
			{
				l_bReturn = m_gl.FramebufferTexture2D( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0 );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_3D:
			if( l_pTexture->GetDimension() == eTEXTURE_DIMENSION_3D )
			{
				l_bReturn = m_gl.FramebufferTexture3D( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, m_gl.Get( l_pTexture->GetDimension() ), l_pTexture->GetGlName(), 0, p_iLayer );
			}
			else
			{
				l_bReturn = m_gl.FramebufferTexture( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, l_pTexture->GetGlName(), 0 );
			}
			break;

		case eTEXTURE_TARGET_LAYER:
			l_bReturn = m_gl.FramebufferTextureLayer( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, l_pTexture->GetGlName(), 0, p_iLayer );
			break;
		}
	}

	if( l_bReturn )
	{
		DoGlAttach( p_eAttachment );
	}

	return l_bReturn;
}

bool GlFrameBuffer :: DoAttach( eATTACHMENT_POINT p_eAttachment, RenderBufferSPtr p_pRenderBuffer )
{
	bool l_bReturn = false;

	if( m_gl.HasFbo() )
	{
		eGL_RENDERBUFFER_ATTACHMENT l_eGlAttachmentPoint = m_gl.GetRboAttachment( p_eAttachment );
		uint32_t l_uiGlName;

		switch( p_pRenderBuffer->GetComponent() )
		{
		case eBUFFER_COMPONENT_COLOUR:	l_uiGlName = std::static_pointer_cast< GlColourRenderBuffer		>( p_pRenderBuffer )->GetGlName();	break;
		case eBUFFER_COMPONENT_DEPTH:	l_uiGlName = std::static_pointer_cast< GlDepthRenderBuffer		>( p_pRenderBuffer )->GetGlName();	break;
		case eBUFFER_COMPONENT_STENCIL:	l_uiGlName = std::static_pointer_cast< GlStencilRenderBuffer	>( p_pRenderBuffer )->GetGlName();	break;
		default:						l_uiGlName = eGL_INVALID_INDEX;																		break;
		}

		if( l_uiGlName != eGL_INVALID_INDEX )
		{
			l_bReturn = m_gl.FramebufferRenderbuffer( eGL_FRAMEBUFFER_MODE_DEFAULT, l_eGlAttachmentPoint, eGL_RENDERBUFFER_MODE_DEFAULT, l_uiGlName );
		}
	}

	if( l_bReturn )
	{
		DoGlAttach( p_eAttachment );
	}

	return l_bReturn;
}

void GlFrameBuffer :: DoDetachAll()
{
	m_arrayGlAttaches.clear();
}

bool GlFrameBuffer :: DoStretchInto( FrameBufferSPtr p_pBuffer, Castor::Rect const & p_rectSrc, Castor::Rect const & p_rectDst, uint32_t p_uiComponents, eINTERPOLATION_MODE p_eInterpolationMode )
{
	return m_gl.BlitFramebuffer( p_rectSrc, p_rectDst, m_gl.GetComponents( p_uiComponents ), m_gl.Get( p_eInterpolationMode ) );
}

void GlFrameBuffer :: DoGlAttach( eATTACHMENT_POINT p_eAttachment )
{
	if( p_eAttachment != eATTACHMENT_POINT_DEPTH && p_eAttachment != eATTACHMENT_POINT_STENCIL )
	{
		DoGlDetach( p_eAttachment );
		uint32_t l_uiAttach = m_gl.Get( p_eAttachment );
		m_arrayGlAttaches.push_back( l_uiAttach );
	}
}

void GlFrameBuffer :: DoGlDetach( eATTACHMENT_POINT p_eAttachment )
{
	uint32_t l_uiAttach = m_gl.Get( p_eAttachment );
	UIntArrayIt l_it = std::find( m_arrayGlAttaches.begin(), m_arrayGlAttaches.end(), l_uiAttach );

	if( l_it != m_arrayGlAttaches.end() )
	{
		m_arrayGlAttaches.erase( l_it );
	}
}

//*************************************************************************************************
