#define STRINGIZE2(s) #s
#define STRINGIZE(s) STRINGIZE2(s)

#include <stdlib.h>
#include <string.h>
#include <GL/glut.h>
#include <proto/exec.h>
#include "sysinc.h"
#include "mglut.library_rev.h"
#include "requesters.h"

#include <interfaces/intuition.h>
#include <proto/intuition.h>

#include <mgl/minigl.h>
#include <GL/GLUT.h>

#include "glut_internal.h"

struct Library *__glut_base = 0;
struct GlutIFace *__glut_current_context = 0;
static BOOL intuitionOpened = FALSE;
struct Library *IntuitionBase = NULL;
struct IntuitionIFace *IIntuition = NULL;


void __glut_init(void) __attribute__((constructor));
void __glut_term(void) __attribute__((destructor));

void __glut_init(void)
{
    if(!IntuitionBase)
	{
		IntuitionBase = IExec->OpenLibrary("intuition.library", 0);
		IIntuition = (struct IntuitionIFace*)IExec->GetInterface(IntuitionBase, "main", 1, NULL);
		intuitionOpened = TRUE;
	}

	__glut_base = IExec->OpenLibrary("mglut.library", VERSION);
	if (!__glut_base)
	{
		displayWarningReq(NULL, NULL, "Fatal Error",
									  "Could not open mglut.library version " STRINGIZE(VERSION)
									  " or higher and/or "
									  "minigl.library version " STRINGIZE(MIN_MINIGLVERSION)
									  " or higher.",
									  REQIMAGE_ERROR);
		exit(0);
	}
	
	__glut_current_context = (struct GlutIFace *)
				IExec->GetInterface(__glut_base, "main", 1, NULL);
	if (!__glut_current_context)
	{
		displayWarningReq(NULL, NULL, "Fatal Error",
									  "Could not open mglut.library version " STRINGIZE(VERSION),
									  REQIMAGE_ERROR);
		exit(0);
	}
}

void __glut_term(void)
{
	if (__glut_current_context)
	{
		__glut_current_context->GLUTExit();
		IExec->DropInterface((struct Interface*)__glut_current_context);
		__glut_current_context = 0;
	}
	
	if (__glut_base)
	{
		IExec->CloseLibrary(__glut_base);
		__glut_base = 0;
	}

	if(intuitionOpened)
	{
		if(IIntuition)
		{
			IExec->DropInterface((struct Interface*)IIntuition);
			IIntuition = NULL;
		}
		if(IntuitionBase)
		{
			IExec->CloseLibrary(IntuitionBase);
			IntuitionBase = NULL;
		}
		intuitionOpened = FALSE;
	}
}

/** The following code is based on FreeGLUT
 */

GLUTproc getProcAddress( const char* procName )
{
    /* optimization: quick initial check */
    if( strncmp( procName, "glut", 4 ) != 0 )
        return NULL;

#define CHECK_NAME(x) if( strcmp( procName, #x ) == 0) return (GLUTproc)x;
	CHECK_NAME(glutInit);
    CHECK_NAME(glutInitDisplayMode);
//	  CHECK_NAME(glutInitDisplayString);
    CHECK_NAME(glutInitWindowPosition);
    CHECK_NAME(glutInitWindowSize);
    CHECK_NAME(glutMainLoop);
    CHECK_NAME(glutCreateWindow);
//	  CHECK_NAME(glutCreateSubWindow);
    CHECK_NAME(glutDestroyWindow);
    CHECK_NAME(glutPostRedisplay);
//	  CHECK_NAME(glutPostWindowRedisplay);
    CHECK_NAME(glutSwapBuffers);
//	  CHECK_NAME(glutGetWindow);
//	  CHECK_NAME(glutSetWindow);
    CHECK_NAME(glutSetWindowTitle);
    CHECK_NAME(glutSetIconTitle);
    CHECK_NAME(glutPositionWindow);
    CHECK_NAME(glutReshapeWindow);
    CHECK_NAME(glutPopWindow);
    CHECK_NAME(glutPushWindow);
    CHECK_NAME(glutIconifyWindow);
    CHECK_NAME(glutShowWindow);
    CHECK_NAME(glutHideWindow);
    CHECK_NAME(glutFullScreen);
//	  CHECK_NAME(glutSetCursor);
	CHECK_NAME(glutWarpPointer);
    CHECK_NAME(glutEstablishOverlay);
    CHECK_NAME(glutRemoveOverlay);
    CHECK_NAME(glutUseLayer);
    CHECK_NAME(glutPostOverlayRedisplay);
    CHECK_NAME(glutPostWindowOverlayRedisplay);
    CHECK_NAME(glutShowOverlay);
    CHECK_NAME(glutHideOverlay);
//	  CHECK_NAME(glutCreateMenu);
//	  CHECK_NAME(glutDestroyMenu);
//	  CHECK_NAME(glutGetMenu);
//	  CHECK_NAME(glutSetMenu);
//	  CHECK_NAME(glutAddMenuEntry);
//	  CHECK_NAME(glutAddSubMenu);
//	  CHECK_NAME(glutChangeToMenuEntry);
//	  CHECK_NAME(glutChangeToSubMenu);
//	  CHECK_NAME(glutRemoveMenuItem);
//	  CHECK_NAME(glutAttachMenu);
//	  CHECK_NAME(glutDetachMenu);
	CHECK_NAME(glutDisplayFunc);
    CHECK_NAME(glutReshapeFunc);
	CHECK_NAME(glutKeyboardFunc);
    CHECK_NAME(glutMouseFunc);
    CHECK_NAME(glutMotionFunc);
    CHECK_NAME(glutPassiveMotionFunc);
    CHECK_NAME(glutEntryFunc);
    CHECK_NAME(glutVisibilityFunc);
    CHECK_NAME(glutIdleFunc);
	CHECK_NAME(glutTimerFunc);
//	  CHECK_NAME(glutMenuStateFunc);
    CHECK_NAME(glutSpecialFunc);
	CHECK_NAME(glutSpaceballMotionFunc);
	CHECK_NAME(glutSpaceballRotateFunc);
	CHECK_NAME(glutSpaceballButtonFunc);
	CHECK_NAME(glutButtonBoxFunc);
	CHECK_NAME(glutDialsFunc);
	CHECK_NAME(glutTabletMotionFunc);
	CHECK_NAME(glutTabletButtonFunc);
//	  CHECK_NAME(glutMenuStatusFunc);
	CHECK_NAME(glutOverlayDisplayFunc);
//	  CHECK_NAME(glutWindowStatusFunc);
    CHECK_NAME(glutKeyboardUpFunc);
    CHECK_NAME(glutSpecialUpFunc);
	CHECK_NAME(glutJoystickFunc);
	CHECK_NAME(glutSetColor);
	CHECK_NAME(glutGetColor);
	CHECK_NAME(glutCopyColormap);
    CHECK_NAME(glutGet);
    CHECK_NAME(glutDeviceGet);
	CHECK_NAME(glutExtensionSupported);
    CHECK_NAME(glutGetModifiers);
    CHECK_NAME(glutLayerGet);
    CHECK_NAME(glutBitmapCharacter);
    CHECK_NAME(glutBitmapWidth);
    CHECK_NAME(glutStrokeCharacter);
    CHECK_NAME(glutStrokeWidth);
    CHECK_NAME(glutBitmapLength);
    CHECK_NAME(glutStrokeLength);
    CHECK_NAME(glutWireSphere);
    CHECK_NAME(glutSolidSphere);
    CHECK_NAME(glutWireCone);
    CHECK_NAME(glutSolidCone);
    CHECK_NAME(glutWireCube);
    CHECK_NAME(glutSolidCube);
    CHECK_NAME(glutWireTorus);
    CHECK_NAME(glutSolidTorus);
    CHECK_NAME(glutWireDodecahedron);
    CHECK_NAME(glutSolidDodecahedron);
    CHECK_NAME(glutWireTeapot);
    CHECK_NAME(glutSolidTeapot);
    CHECK_NAME(glutWireOctahedron);
    CHECK_NAME(glutSolidOctahedron);
    CHECK_NAME(glutWireTetrahedron);
    CHECK_NAME(glutSolidTetrahedron);
    CHECK_NAME(glutWireIcosahedron);
    CHECK_NAME(glutSolidIcosahedron);
//	  CHECK_NAME(glutVideoResizeGet);
//	  CHECK_NAME(glutSetupVideoResizing);
//	  CHECK_NAME(glutStopVideoResizing);
//	  CHECK_NAME(glutVideoResize);
//	  CHECK_NAME(glutVideoPan);
	CHECK_NAME(glutReportErrors);
    CHECK_NAME(glutIgnoreKeyRepeat);
	CHECK_NAME(glutSetKeyRepeat);
	CHECK_NAME(glutForceJoystickFunc);
    CHECK_NAME(glutGameModeString);
    CHECK_NAME(glutEnterGameMode);
    CHECK_NAME(glutLeaveGameMode);
    CHECK_NAME(glutGameModeGet);

    /* freeglut extensions */
//	  CHECK_NAME(glutMainLoopEvent);
//	  CHECK_NAME(glutLeaveMainLoop);
	CHECK_NAME(glutCloseFunc);
	CHECK_NAME(glutWMCloseFunc);
//	  CHECK_NAME(glutMenuDestroyFunc);
	CHECK_NAME(glutSetOption);
//	  CHECK_NAME(glutSetWindowData);
//	  CHECK_NAME(glutGetWindowData);
//	  CHECK_NAME(glutSetMenuData);
//	  CHECK_NAME(glutGetMenuData);
    CHECK_NAME(glutBitmapHeight);
    CHECK_NAME(glutStrokeHeight);
    CHECK_NAME(glutBitmapString);
    CHECK_NAME(glutStrokeString);
    CHECK_NAME(glutWireRhombicDodecahedron);
    CHECK_NAME(glutSolidRhombicDodecahedron);
    CHECK_NAME(glutWireSierpinskiSponge);
    CHECK_NAME(glutSolidSierpinskiSponge);
    CHECK_NAME(glutWireCylinder);
    CHECK_NAME(glutSolidCylinder);
    CHECK_NAME(glutGetProcAddress);
//	  CHECK_NAME(glutMouseWheelFunc);
    return NULL;
}


GLUTproc glutGetProcAddress( const char *procName )
{
    GLUTproc p;

    /* Try GLUT functions first */
	p = getProcAddress( procName );
    if( p != NULL )
        return p;

    /* Try core GL functions */
	return(GLUTproc)mglGetProcAddress(procName );
}
