#include "stdafx.h"

bool OpenGL_3::Initialize( HWND hWnd, HGLRC& glRc, int color /*= 32*/, int depth /*= 24*/, bool forwardCompatible /*= true*/ )
{
#define _RET_WITH_ERROR(msg)	\
	{	if ( hDC )	ReleaseDC( hWnd, hDC );	\
		if ( fakeGLRC ) {	\
			wglMakeCurrent( NULL, NULL );	\
			wglDeleteContext( fakeGLRC );	\
		}	\
		Log_Error( msg );	\
		return false;	\
	}

	Log_Normal( "Creating fake OpenGL context..." );

	HGLRC  fakeGLRC = NULL;
	HDC    hDC = NULL;
	uint32 pixelFormat = 0;

	PIXELFORMATDESCRIPTOR pfd =
	{
		sizeof( PIXELFORMATDESCRIPTOR ), 1,
		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA, color, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, depth, 0, 0,
		PFD_MAIN_PLANE, 0, 0, 0, 0
	};

	if ( !( hDC = GetDC( hWnd ) ) )
		_RET_WITH_ERROR( "Can't create a dummy gl device context" )
	if ( !( pixelFormat = ::ChoosePixelFormat( hDC, &pfd ) ) )
		_RET_WITH_ERROR( "Can't find a suitable dummy pixelformat" )
	if ( !::SetPixelFormat( hDC, pixelFormat, &pfd ) )
		_RET_WITH_ERROR( "Can't set the pixelformat" )
	if ( !( fakeGLRC = wglCreateContext( hDC ) ) )
		_RET_WITH_ERROR( "Can't create a dummy gl rendering context" )
	if ( !wglMakeCurrent( hDC, fakeGLRC ) )
		_RET_WITH_ERROR( "Can't activate dummy gl rendering context" )
	if ( !HaveOpenGL3Support() )
		_RET_WITH_ERROR( "Your videocard not support OpenGL 3.0" )

	PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)ProcedureAddress( "wglCreateContextAttribsARB" );
	if ( !wglCreateContextAttribsARB )
		_RET_WITH_ERROR( "Your videocard support OpenGL 3.0,\nbut wglCreateContextAttribsARB == NULL!" )

	Log_Normal( "OpenGL 3.0 support detected.\n\tCreating context" );

	int attribs[] =
	{	WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
		WGL_CONTEXT_MINOR_VERSION_ARB, 0,
		WGL_CONTEXT_FLAGS_ARB, forwardCompatible ? WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB : 0,
		0, 0
	};
	if ( NULL == ( glRc = wglCreateContextAttribsARB( hDC, NULL, attribs ) ) )
	{
		ReleaseDC( hWnd, hDC );
		_RET_WITH_ERROR( "Creating OpenGL 3.0 context failed" )
	}

	Log_Normal( "Destroying fake OpenGL context..." );

	wglMakeCurrent( NULL, NULL );
	wglDeleteContext( fakeGLRC );

	Log_Normal( "Setup OpenGL 3.0 context..." );
	if ( !wglMakeCurrent( hDC, glRc ) )
	{
		Log_Error( "Couldn't make OprnGL 3.0 context current" );
		ReleaseDC( hWnd, hDC );
		wglDeleteContext( glRc );
		return false;
	}	

	//ReleaseDC( hWnd, hDC );

	return true;
}

void OpenGL_3::Shutdown( void )
{
	HGLRC glRc = wglGetCurrentContext();
	if ( glRc )
	{
		wglMakeCurrent( NULL, NULL );
		wglDeleteContext( glRc );
	}
}

void* OpenGL_3::ProcedureAddress( const std::string& procName )
{
#ifdef _WIN32
	return wglGetProcAddress( procName.c_str() );
#else
	return glXGetProcAddressARB( reinterpret_cast<const GLubyte*>(procName.c_str()) );
#endif
}

std::string OpenGL_3::GetVersion( void )
{
	const int8* version_str = reinterpret_cast<const int8 *>(glGetString(GL_VERSION));
	return ( NULL != version_str ) ? version_str : "";
}

std::string OpenGL_3::GetVendor( void )
{
	const int8* vendor_str = reinterpret_cast<const int8 *>(glGetString(GL_VENDOR));
	return ( NULL != vendor_str ) ? vendor_str : "";
}

std::string OpenGL_3::GetRenderer( void )
{
	const int8* renderer_str = reinterpret_cast<const int8 *>(glGetString(GL_RENDERER));
	return ( NULL != renderer_str ) ? renderer_str : "";
}

std::string OpenGL_3::GetExtensions( void )
{
	//const int8* extensions_str = reinterpret_cast<const int8 *>(glGetString(GL_EXTENSIONS));
	//return ( NULL != extensions_str ) ? extensions_str : "";
	std::string ret = "";
	PFNGLGETSTRINGIPROC glGetStringi = (PFNGLGETSTRINGIPROC)ProcedureAddress( "glGetStringi" );

	if ( NULL != glGetStringi )
	{
		int numExt = 0;
		glGetIntegerv( GL_NUM_EXTENSIONS, &numExt );
		for ( int i = 0; i < numExt; ++i )
		{
			ret += reinterpret_cast<const int8 *>(glGetStringi(GL_EXTENSIONS, i));
			if ( i + 1 < numExt )
				ret += " ";
		}
	}

	return ret;
}

std::string OpenGL_3::GetWGLExtensions( void )
{
	PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)ProcedureAddress( "wglGetExtensionsStringEXT" );
	PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)ProcedureAddress( "wglGetExtensionsStringARB" );
	if ( !wglGetExtensionsStringEXT || !wglGetExtensionsStringARB )
		return "";

	const int8* wgl_extensions_str = ( NULL != wglGetExtensionsStringARB )
									? (const int8 *)wglGetExtensionsStringARB( wglGetCurrentDC() )
									: (const int8 *)wglGetExtensionsStringEXT();

	return ( NULL != wgl_extensions_str ) ? wgl_extensions_str : "";
}

std::string OpenGL_3::GetGLSLVersion( void )
{
	const int8* glsl_version_str = reinterpret_cast<const int8 *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
	return ( NULL != glsl_version_str ) ? glsl_version_str : "";
}

void OpenGL_3::AssertGLError( void )
{
	int err = glGetError();
	if ( GL_NO_ERROR != err )
		__asm int 3;
}

GLhandleARB OpenGL_3::CreateGLSLProgram( const std::string& vs, const std::string& fs )
{
	GLhandleARB program = glext::glCreateProgramObjectARB();
	if ( !program )
		return NULL;

	GLhandleARB vertexProgram = glext::glCreateShaderObjectARB( GL_VERTEX_SHADER );
	GLhandleARB fragmentProgram = glext::glCreateShaderObjectARB( GL_FRAGMENT_SHADER );

	const char* data = vs.c_str();
	int len = (int)vs.length(), compiled;
	glext::glShaderSourceARB( vertexProgram, 1, &data, &len );
	glext::glCompileShaderARB( vertexProgram );
	glext::glGetObjectParameterivARB ( vertexProgram, GL_OBJECT_COMPILE_STATUS_ARB, &compiled );
	if ( !CheckForGLSLErrors( vertexProgram ) || !compiled )
	{
		glext::glDeleteObjectARB( vertexProgram );
		glext::glDeleteObjectARB( fragmentProgram );
		glext::glDeleteObjectARB( program );
		return NULL;
	}
	glext::glAttachObjectARB( program, vertexProgram );
	if ( !CheckForGLSLErrors( program ) )
	{
		glext::glDeleteObjectARB( vertexProgram );
		glext::glDeleteObjectARB( fragmentProgram );
		glext::glDeleteObjectARB( program );
		return NULL;
	}

	data = fs.c_str();
	len = (int)fs.length();
	glext::glShaderSourceARB( fragmentProgram, 1, &data, &len );
	glext::glCompileShaderARB( fragmentProgram );
	glext::glGetObjectParameterivARB ( fragmentProgram, GL_OBJECT_COMPILE_STATUS_ARB, &compiled );
	if ( !CheckForGLSLErrors( fragmentProgram ) || !compiled )
	{
		glext::glDeleteObjectARB( vertexProgram );
		glext::glDeleteObjectARB( fragmentProgram );
		glext::glDeleteObjectARB( program );
		return NULL;
	}
	glext::glAttachObjectARB( program, fragmentProgram );

	glext::glLinkProgramARB( program );
	glext::glGetObjectParameterivARB( program, GL_OBJECT_LINK_STATUS_ARB, &compiled );
	if ( !compiled || !CheckForGLSLErrors( program ) )
	{
		glext::glDeleteObjectARB( vertexProgram );
		glext::glDeleteObjectARB( fragmentProgram );
		glext::glDeleteObjectARB( program );
		return NULL;
	}

	glext::glDeleteObjectARB( vertexProgram );
	glext::glDeleteObjectARB( fragmentProgram );

	CheckForGLSLErrors( program );

	return program;
}




bool OpenGL_3::HaveOpenGL3Support( void )
{
	std::string extensions = (const int8 *)glGetString( GL_EXTENSIONS );
	PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)ProcedureAddress( "wglGetExtensionsStringEXT" );
	PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)ProcedureAddress( "wglGetExtensionsStringARB" );

	if ( !wglGetExtensionsStringEXT || !wglGetExtensionsStringARB )
	{
		Log_Error( "Failed to get wgl extensions string" );
		return false;
	}

	std::string wglExtensions = GetWGLExtensions();

	if ( std::string::npos == wglExtensions.find_first_of( "WGL_ARB_create_context" ) )
	{
		Log_Error( "You have no \"WGL_ARB_create_context\" extension :(" );
		return false;
	}

	return true;
}

bool OpenGL_3::CheckForGLSLErrors( GLhandleARB object )
{
	static char buffer[2048];

	int len( 0 ), chars( 0 );
	glext::glGetObjectParameterivARB( object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &len );

	if ( len < 1 )
		return true;
	
	glext::glGetInfoLogARB( object, len, &chars, buffer );
	if ( 0 >= chars )
		return true;

	Log_Error( buffer );
	return false;
}

