////////////////////////////////////////////////////////
//
// GEM - Graphics Environment for Multimedia
//
// mark@danks.org
//
// Implementation file
//
//    Copyright (c) 1997-2000 Mark Danks.
//    For information on usage and redistribution, and for a DISCLAIMER OF ALL
//    WARRANTIES, see the file, "GEM.LICENSE.TERMS" in this distribution.
//
/////////////////////////////////////////////////////////
 
#include "GemMan.h"

#ifdef unix
#include <sys/time.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#elif _WINDOWS
#include <stdlib.h>
// I hate Microsoft...I shouldn't have to do this!
#endif

#include "GemVersion.h"
#include "GemState.h"
#include "GemWinCreate.h"
#include "GemEvent.h"

#include "Controls/gemhead.h"

static WindowInfo gfxInfo;
static WindowInfo constInfo;

// static member data
int GemMan::m_height = 500;
int GemMan::m_width = 500;
int GemMan::m_border = 1;
int GemMan::m_stereo = 0;
int GemMan::m_buffer = 2;
int GemMan::m_profile = 0;
GLfloat GemMan::m_clear_color[4];
GLfloat GemMan::m_mat_ambient[4];
GLfloat GemMan::m_mat_specular[4];
GLfloat GemMan::m_mat_shininess;
GLfloat GemMan::m_stereoSep = -15.f;
GLfloat GemMan::m_stereoFocal = 0.f;
int GemMan::m_windowState = 0;
int GemMan::m_windowNumber = 0;
float GemMan::m_perspect[6];
double GemMan::m_lastRenderTime = 0.;

// static data
static const int NUM_LIGHTS = 8;   	// the maximum number of lights
static int s_lightState = 0;        // is lighting on or off
static int s_lights[NUM_LIGHTS];    // the lighting array

static t_clock *s_clock = NULL;
static double s_deltime = 50.;
static int s_hit = 0;
static int s_rendering = 0;

static gemheadLink *s_linkHead = NULL;

class gemheadLink
{
    public:
    	gemheadLink(const gemheadLink &s)
    	    	: base(s.base), next(s.next), priority(s.priority) {}
    	gemheadLink(gemhead *base_, int priority_)
    	    	: base(base_), next(NULL), priority(priority_) {}
    	gemheadLink(gemhead *base_, int priority_, gemheadLink *link)
    	    	: base(base_), priority(priority_)
    	    	{ this->next = link->next; link->next = this; }
    	
    	gemhead *base;
    	gemheadLink *next;
    	const int priority;
    private:
    	gemheadLink();
};

static int createConstWindow();
// static int destroyConstWindow();

GEM_EXTERN void gemAbortRendering()
{
	GemMan::stopRendering();
}

static t_clock *s_windowClock = NULL;
static int s_windowDelTime = 10;

#ifdef _WINDOWS
static int s_windowRun = 0;
static int s_singleContext = 0;

/////////////////////////////////////////////////////////
// dispatchGemWindowMessages
//
/////////////////////////////////////////////////////////
static void dispatchGemWindowMessages()
{
    if (!s_windowRun)
		return;

    MSG msg;
    while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
    {
        TranslateMessage(&msg);
	    DispatchMessage(&msg);
    }
    clock_delay(s_windowClock, s_windowDelTime);
}
#elif unix 
static void dispatchGemWindowMessages()
{
    WindowInfo win; 
    XEvent event; 
    XButtonEvent* eb = (XButtonEvent*)&event; 
    win = GemMan::getWindowInfo(); 

    while 
		(XCheckWindowEvent(win.dpy,win.win,PointerMotionMask | ButtonMotionMask |
                             ButtonPressMask | ButtonReleaseMask,&event))
	{
        switch (event.type)
		{
			case ButtonPress: 
				triggerButtonEvent(eb->button-1, 1, eb->x, eb->y); 
				break; 
			case ButtonRelease: 
				triggerButtonEvent(eb->button-1, 0, eb->x, eb->y); 
				break; 
			case MotionNotify: 
				triggerMotionEvent(eb->x, eb->y); 
				break; 
			default:
				break; 
		}
    }
    clock_delay(s_windowClock, s_windowDelTime);  
} 
#endif // for Unix

static void resizeCallback(int xSize, int ySize, void *)
{
    float xDivy = (float)xSize / (float)ySize;
	GemMan::m_height = ySize;
	GemMan::m_width = xSize;

    // setup the viewpoint
    glViewport(0, 0, xSize, ySize);
	// setup the matrices
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(GemMan::m_perspect[0] * xDivy, GemMan::m_perspect[1] * xDivy,	// left, right
				GemMan::m_perspect[2], GemMan::m_perspect[3],				// bottom, top
				GemMan::m_perspect[4], GemMan::m_perspect[5]);				// front, back
 
    glMatrixMode(GL_MODELVIEW);
}

/////////////////////////////////////////////////////////
//
// GemMan
//
/////////////////////////////////////////////////////////
// initGem
//
/////////////////////////////////////////////////////////
void GemMan :: initGem()
{
    static int alreadyInit = 0;
    if (alreadyInit)
		return;
    alreadyInit = 1;
    
    // clear the light array
    for (int i = 0; i < NUM_LIGHTS; i++)
		s_lights[i] = 0;
    
    m_clear_color[0] = 0.0;
    m_clear_color[1] = 0.0;
    m_clear_color[2] = 0.0;
    m_clear_color[3] = 0.0;
    m_mat_ambient[0] = 0.0f;
    m_mat_ambient[1] = 0.0f;
    m_mat_ambient[2] = 0.0f;
    m_mat_ambient[3] = 1.0f;
    m_mat_specular[0] = 1.0;
    m_mat_specular[1] = 1.0;
    m_mat_specular[2] = 1.0;
    m_mat_specular[3] = 1.0;
    m_mat_shininess = 100.0;

    s_clock = clock_new(NULL, (t_method)&GemMan::render);

    post("GEM: Mark Danks");
    post("GEM: ver: %s", GEM_VERSION);
    post("GEM: compiled: " __DATE__);

	// setup the perspective values
	m_perspect[0] = -1.f;	// left
	m_perspect[1] =  1.f;	// right
	m_perspect[2] = -1.f;	// bottom
	m_perspect[3] =  1.f;	// top
	m_perspect[4] =  1.f;	// front
	m_perspect[5] = 20.f;	// back

#ifdef unix
    Display *dummyDpy;
    if ( (dummyDpy = XOpenDisplay(NULL)) == NULL)
    { 
	    error("GEM: could not open X display");
	    return;
    }
    
    int dummy, dummy2;
    if ( !glXQueryExtension(dummyDpy, &dummy, &dummy2) )
    {
	    error("GEM: X server has no OpenGL GLX extension");
	    XCloseDisplay(dummyDpy);
	    return;
    }
#elif _WINDOWS
	// can we only have one context?
	if (getenv("GEM_SINGLE_CONTEXT") &&
		!strcmp("1", getenv("GEM_SINGLE_CONTEXT")))
	{
		post("GEM: using GEM_SINGLE_CONTEXT");
		s_singleContext = 1;
		m_width = 640;
		m_height = 480;
	}
#endif

    s_windowClock = clock_new(NULL, (t_method)dispatchGemWindowMessages);

    if (!createConstWindow())
    {
    	error("GEM: A serious error occured creating const Context");
    	error("GEM: Do not continue!");
    }
    setResizeCallback(resizeCallback, NULL);
}

/////////////////////////////////////////////////////////
// addObj
//
/////////////////////////////////////////////////////////
void GemMan :: addObj(gemhead *obj, int priority)
{
    // find the place where the priority is a higher number
    gemheadLink *linkPtr = s_linkHead;
    
    // unique case if there is no s_linkHead
    if (!linkPtr)
    {
    	s_linkHead = new gemheadLink(obj, priority);
    	return;
    }
    
    // unique case if the s_linkHead has a worse priority number
    if (linkPtr->priority > priority)
    {
    	s_linkHead = new gemheadLink(obj, priority);
    	s_linkHead->next = linkPtr;
    	return;
    }
    while (linkPtr->next && linkPtr->next->priority <= priority)
    	linkPtr = linkPtr->next;
       
    linkPtr = new gemheadLink(obj, priority, linkPtr);
}

/////////////////////////////////////////////////////////
// removeObj
//
/////////////////////////////////////////////////////////
void GemMan :: removeObj(gemhead *obj)
{
    gemheadLink *linkPtr = s_linkHead;
    if (!linkPtr) return;
    
    // unique case if the object is the s_linkHead
    if (linkPtr->base == obj)
    {
    	gemheadLink *nextPtr = linkPtr->next;
    	delete s_linkHead;
    	s_linkHead = nextPtr;
    	return;
    }
    
    while (linkPtr->next && linkPtr->next->base != obj)
        linkPtr = linkPtr->next;
    
    // didn't find anything
    if ( !linkPtr->next ) return;
    
    gemheadLink *removePtr = linkPtr->next;
    linkPtr->next = removePtr->next;
    delete [] removePtr;
}

/////////////////////////////////////////////////////////
// resetValues
//
/////////////////////////////////////////////////////////
void GemMan :: resetValues()
{
    if (s_lightState)
    {
	    glEnable(GL_LIGHTING);
	    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	    glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
	    glEnable(GL_COLOR_MATERIAL);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, GemMan::m_mat_ambient);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, GemMan::m_mat_specular);
	    glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &GemMan::m_mat_shininess);
	    glEnable(GL_AUTO_NORMAL);
	    glEnable(GL_NORMALIZE);
	    glShadeModel(GL_SMOOTH);
    }
    else
    {
	    glDisable(GL_LIGHTING);
	    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
	    glDisable(GL_COLOR_MATERIAL);
	    glDisable(GL_AUTO_NORMAL);
	    glDisable(GL_NORMALIZE);
	    glShadeModel(GL_FLAT);
    }

	// setup the transformation matrices
    float xDivy = (float)m_width / (float)m_height;
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(m_perspect[0] * xDivy, m_perspect[1] * xDivy,	// left, right
				m_perspect[2], m_perspect[3],				// bottom, top
				m_perspect[4], m_perspect[5]);				// front, back
    
	glMatrixMode(GL_MODELVIEW);
    gluLookAt(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

/////////////////////////////////////////////////////////
// fillGemState
//
/////////////////////////////////////////////////////////
void GemMan :: fillGemState(GemState &state)
{
    if (s_lightState)
    {
    	state.lighting = 1;
    	state.smooth = 1;
    }
}

/////////////////////////////////////////////////////////
// resetState
//
/////////////////////////////////////////////////////////
void GemMan :: resetState()
{
    m_clear_color[0] = 0.0;
    m_clear_color[1] = 0.0;
    m_clear_color[2] = 0.0;
    m_clear_color[3] = 0.0;
    glClearColor(m_clear_color[0], m_clear_color[1], m_clear_color[2], m_clear_color[3]);
    m_mat_ambient[0] = 0.1f;
    m_mat_ambient[1] = 0.1f;
    m_mat_ambient[2] = 0.1f;
    m_mat_ambient[3] = 1.0f;
    m_mat_specular[0] = 1.0;
    m_mat_specular[1] = 1.0;
    m_mat_specular[2] = 1.0;
    m_mat_specular[3] = 1.0;
    m_mat_shininess = 100.0;

    s_lightState = 0;
    m_height = 500;
    m_width = 500;
    m_buffer = 2;

	// setup the perspective values
	m_perspect[0] = -1.f;	// left
	m_perspect[1] =  1.f;	// right
	m_perspect[2] = -1.f;	// bottom
	m_perspect[3] =  1.f;	// top
	m_perspect[4] =  1.f;	// front
	m_perspect[5] = 20.f;	// back
}

/////////////////////////////////////////////////////////
// render
//
/////////////////////////////////////////////////////////
void GemMan :: render(void *)
{
#ifdef _WINDOWS
	static int firstTime = 1;
	static float countFreq = 0;
#endif

    if (!m_windowState)
		return;

	// are we profiling?
#ifdef _WINDOWS
	if (firstTime)
	{
		LARGE_INTEGER freq;
		if (!QueryPerformanceFrequency(&freq))
			countFreq = 0;
		else
			countFreq = (float)(freq.QuadPart);
		firstTime = 0;
	}
	LARGE_INTEGER startTime;
//	if (m_profile == 1 || m_profile == 2)
		QueryPerformanceCounter(&startTime);
#elif unix
	timeval startTime;
//	if (m_profile == 1 || m_profile == 2)
	{
	    gettimeofday(&startTime, 0);
	}
#else
#error Define OS specific profiling
#endif
    
    s_hit = 0;
    resetValues();

	GemState currentState;
	// fill in the elapsed time
	if (m_buffer == 1)
		currentState.tickTime = 50.f;
	else
		currentState.tickTime = (float)(clock_gettimesince(m_lastRenderTime));
	m_lastRenderTime = clock_getsystime();

	// if stereoscopic rendering
	if (m_stereo)
	{
		int xSize = m_width / 2;
		int ySize = m_height;
	    float xDivy = (float)xSize / (float)ySize;

		// setup the left viewpoint
		glViewport(0, 0, xSize, ySize);
		// setup the matrices
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glFrustum(GemMan::m_perspect[0] * xDivy, GemMan::m_perspect[1] * xDivy,	// left, right
				GemMan::m_perspect[2], GemMan::m_perspect[3],				// bottom, top
				GemMan::m_perspect[4], GemMan::m_perspect[5]);				// front, back
 
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(-m_stereoSep / 100.f, 0.0, 4.0, 0.0, 0.0, m_stereoFocal, 0.0, 1.0, 0.0);

		// render left view
		fillGemState(currentState);
		currentState.stereo = 1;
		gemheadLink *head = s_linkHead;
    
		while (head)
		{
    		head->base->renderGL(&currentState);
    		head = head->next;
		}

		// setup the right viewpoint
		glViewport(xSize, 0, xSize, ySize);
		// setup the matrices
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glFrustum(GemMan::m_perspect[0] * xDivy, GemMan::m_perspect[1] * xDivy,	// left, right
				GemMan::m_perspect[2], GemMan::m_perspect[3],				// bottom, top
				GemMan::m_perspect[4], GemMan::m_perspect[5]);				// front, back
 
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(m_stereoSep / 100.f, 0.0, 4.0, 0.0, 0.0, m_stereoFocal, 0.0, 1.0, 0.0);

		// render right view
		fillGemState(currentState);
		currentState.stereo = 2;
		head = s_linkHead;
    
		while (head)
		{
    		head->base->renderGL(&currentState);
    		head = head->next;
		}

		// draw a line between the views
	    glDisable(GL_LIGHTING);
		glViewport(0, 0, m_width, m_height);
		// setup the matrices
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
	    xDivy = (float)m_width / (float)ySize;
		glFrustum(GemMan::m_perspect[0] * xDivy, GemMan::m_perspect[1] * xDivy,	// left, right
				GemMan::m_perspect[2], GemMan::m_perspect[3],				// bottom, top
				GemMan::m_perspect[4], GemMan::m_perspect[5]);				// front, back

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		gluLookAt(0.f, 0.0, 4.0, 0.0, 0.0, 0.f, 0.0, 1.0, 0.0);
		glLineWidth(2.f);
		glColor3f(1.f, 1.f, 1.f);
		glBegin(GL_LINES);
			glVertex2f(0.f, -6.f);
			glVertex2f(0.f, 6.f);
		glEnd();
		glLineWidth(1.0f);
    }
	// normal rendering
	else
	{
		fillGemState(currentState);
		gemheadLink *head = s_linkHead;
    
		while (head)
		{
    		head->base->renderGL(&currentState);
    		head = head->next;
		}
	}

    // only want to swap if we are in double buffer mode
    if (GemMan::m_buffer == 2)
		swapBuffers();

	// are we profiling?
	if (m_profile == 1 || m_profile == 2)
#ifdef _WINDOWS
	{
		LARGE_INTEGER endTime;
		QueryPerformanceCounter(&endTime);
		if (countFreq)
			post("GEM: time: %f",
				(float)(endTime.QuadPart - startTime.QuadPart)/countFreq * 1000.f);
		else
			error("GEM: unable to profile");
	}
#elif unix
	{
		timeval endTime;
	    gettimeofday(&endTime, 0);
		float seconds = (endTime.tv_sec - startTime.tv_sec) +
    					(endTime.tv_usec - startTime.tv_usec) * 0.000001;
		post("GEM: time: %f", seconds);
	}
#else
#error Define OS specific profiling
#endif

    // only keep going if no one set the s_hit (could be hit if scheduler gets
    //	    ahold of a stopRendering command)
    if (!s_hit)
		clock_delay(s_clock, s_deltime);
}

/////////////////////////////////////////////////////////
// startRendering
//
/////////////////////////////////////////////////////////
void GemMan :: startRendering()
{
    if (!m_windowState)
    {
    	error("GEM: Create window first!");
    	return;
    }
    
    if (s_rendering)
		return;
    
    post("GEM: Start rendering");
    
    // set up all of the gemheads
    gemheadLink *head = s_linkHead;
    while(head)
    {
    	head->base->startRendering();
    	head = head->next;
    }

    s_rendering = 1;
    
    // if only single buffering then just return
    if (GemMan::m_buffer == 1)
		return;

	m_lastRenderTime = clock_getsystime();
    render(NULL);
}

/////////////////////////////////////////////////////////
// stopRendering
//
/////////////////////////////////////////////////////////
void GemMan :: stopRendering()
{
    if (!s_rendering) return;

    s_rendering = 0;
    clock_unset(s_clock);
    s_hit = 1;

    // clean out all of the gemheads
    gemheadLink *head = s_linkHead;
    while(head)
    {
    	head->base->stopRendering();
    	head = head->next;
    }

    post("GEM: Stop rendering");
}

/////////////////////////////////////////////////////////
// windowInit
//
/////////////////////////////////////////////////////////
void GemMan :: windowInit()
{
    glDisable(GL_ALPHA_TEST);
    glDisable(GL_BLEND);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glClearDepth(1.0);    
    glClearColor(m_clear_color[0], m_clear_color[1], m_clear_color[2], m_clear_color[3]);
 
    resetValues();
}

/////////////////////////////////////////////////////////
// windowCleanup
//
/////////////////////////////////////////////////////////
void GemMan :: windowCleanup()
{ }

/////////////////////////////////////////////////////////
// createWindow
//
/////////////////////////////////////////////////////////
int GemMan :: createWindow()
{
    if ( m_windowState ) return(0);

    WindowHints myHints;
	myHints.border = m_border;
    myHints.buffer = m_buffer;
    myHints.width = m_width;
    myHints.height = m_height;
    myHints.shared = constInfo.context;
    myHints.actuallyDisplay = 1;

    if (!createGemWindow(gfxInfo, myHints) )
    {
        error("GEM: Unable to create window");
        return(0);
    }
    m_windowState = 1;
    m_windowNumber++;

    windowInit();
    
    clock_delay(s_windowClock, s_windowDelTime);

#ifdef _WINDOWS
    s_windowRun = 1;
#endif
    return(1);
}

/////////////////////////////////////////////////////////
// destroyWindow
//
/////////////////////////////////////////////////////////
void GemMan :: destroyWindow()
{
#ifdef _WINDOWS
	// don't want to get rid of this
	if (s_singleContext)
		return;
#endif

    if (!m_windowState) return;

    stopRendering();
    
    glFlush();
    glFinish();

    destroyGemWindow(gfxInfo);

    m_windowState = 0;
    
    windowCleanup();

    // reestablish the const glxContext
#ifdef unix                 // for Unix
    glXMakeCurrent(constInfo.dpy, constInfo.win, constInfo.context);   
#elif _WINDOWS              // for Windows
    wglMakeCurrent(constInfo.dc, constInfo.context);
    s_windowRun = 0;
#else
#error Define OS specific OpenGL context make current
#endif
}

/////////////////////////////////////////////////////////
// createConstWindow
//
/////////////////////////////////////////////////////////
int createConstWindow()
{
#ifdef _WINDOWS
	// can we only have one context?
	if (s_singleContext)
	{
		return(GemMan::createWindow());		
	}
#endif

    WindowHints myHints;
	myHints.border = 1;
    myHints.buffer = 1;
    myHints.width = GemMan::m_width;
    myHints.height = GemMan::m_height;
    myHints.shared = NULL;
    myHints.actuallyDisplay = 0;

    if (!createGemWindow(constInfo, myHints) )
    {
        error("GEM: Error creating const context");
        return(0);
    }
    return(1);
}

/////////////////////////////////////////////////////////
// destroyConstWindow
//
/////////////////////////////////////////////////////////
void destroyConstWindow()
{
#ifdef _WINDOWS
	if (s_singleContext)
	{
	}
	else
#endif
    destroyGemWindow(constInfo);
}

/////////////////////////////////////////////////////////
// swapBuffers
//
/////////////////////////////////////////////////////////
void GemMan :: swapBuffers()
{
    if (!m_windowState) return;
    if (GemMan::m_buffer == 2)
#ifdef unix             // for Unix
        glXSwapBuffers(gfxInfo.dpy, gfxInfo.win);
#elif _WINDOWS          // for WinNT
        SwapBuffers(gfxInfo.dc);
#else                   // everyone else
#error Define OS specific swap buffer
#endif
    else glFlush();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0);
    glLoadIdentity();

    if (GemMan::m_buffer == 1)
	{
        glFlush();
		// setup the transformation matrices
		float xDivy = (float)m_width / (float)m_height;
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glFrustum(m_perspect[0] * xDivy, m_perspect[1] * xDivy,	// left, right
				  m_perspect[2], m_perspect[3],					// bottom, top
				  m_perspect[4], m_perspect[5]);				// front, back
    
		glMatrixMode(GL_MODELVIEW);
		gluLookAt(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
	}
}

/////////////////////////////////////////////////////////
// lightingOnOff
//
/////////////////////////////////////////////////////////
void GemMan :: lightingOnOff(int state)
{
    if (state) s_lightState = 1;
    else s_lightState = 0;
}

/////////////////////////////////////////////////////////
// frameRate
//
/////////////////////////////////////////////////////////
void GemMan :: frameRate(float framespersecond)
{
    if (framespersecond <= 0.)
    {
    	error("GEM: Invalid frame rate: %f", framespersecond);
    	framespersecond = 20;
    }
    s_deltime = 1000. / framespersecond;
}

/////////////////////////////////////////////////////////
// requestLight
//
/////////////////////////////////////////////////////////
GLenum GemMan :: requestLight(int specific)
{
    int i = 0;
	if (specific > 0)
		i = specific - 1;
	else
	{
		while(s_lights[i])
		{
			i++;
			if (i >= NUM_LIGHTS)
			{
				error("GEM: Unable to allocate light");
				return((GLenum)0);
			}
		}
	}
    s_lights[i]++;
	GLenum retLight;
    switch(i)
    {
	    case (0) :
	        retLight = GL_LIGHT0;
	        break;
	    case (1) :
	        retLight = GL_LIGHT1;
	        break;
	    case (2) :
	        retLight = GL_LIGHT2;
	        break;
	    case (3) :
	        retLight = GL_LIGHT3;
	        break;
	    case (4) :
	        retLight = GL_LIGHT4;
	        break;
	    case (5) :
	        retLight = GL_LIGHT5;
	        break;
	    case (6) :
	        retLight = GL_LIGHT6;
	        break;
	    case (7) :
	        retLight = GL_LIGHT7;
	        break;
	    default :
	        error("GEM: Unable to allocate world_light");
	        return((GLenum)0);
	        // break;
    }
    return(retLight);
}

/////////////////////////////////////////////////////////
// freeLight
//
/////////////////////////////////////////////////////////
void GemMan :: freeLight(GLenum lightNum)
{
	int i = 0;
    switch(lightNum)
    {
    	case(GL_LIGHT0):
    	    i = 0;
			break;
    	case(GL_LIGHT1):
			i = 1;
			break;
    	case(GL_LIGHT2):
			i = 2;
			break;
    	case(GL_LIGHT3):
			i = 3;
			break;
    	case(GL_LIGHT4):
			i = 4;
			break;
    	case(GL_LIGHT5):
			i = 5;
			break;
    	case(GL_LIGHT6):
			i = 6;
			break;	
    	case(GL_LIGHT7):
			i = 7;
			break;
    	default:
    	    error("GEM: Error freeing a light - bad number");
			return;
			// break;
    }
	s_lights[i]--;
	if (s_lights[i] < 0)
	{
		error("GEM: light ref count below zero: %d", i);
		s_lights[i] = 0;
	}
}

/////////////////////////////////////////////////////////
// printInfo
//
/////////////////////////////////////////////////////////
void GemMan :: printInfo()
{
    post("GEM information");
    post("---------------");
    post("OpenGL info");
    post("Vendor: %s", glGetString(GL_VENDOR));
    post("Renderer: %s", glGetString(GL_RENDERER));
    post("Version: %s", glGetString(GL_VERSION));
    post("Extensions: %s", glGetString(GL_EXTENSIONS));
    post("---------------");
    post("window state: %d", m_windowState);
	post("profile: %d", m_profile);
    post("buffer: %d", m_buffer);
    post("stereo: %d", m_stereo);
    post("width: %d, height %d", m_width, m_height);
    post("frame rate: %f", 1000. / s_deltime);

    GLint bitnum = 0;
	glGetIntegerv(GL_RED_BITS, &bitnum);
	post("red: %d", bitnum);
	glGetIntegerv(GL_GREEN_BITS, &bitnum);
	post("green: %d", bitnum);
	glGetIntegerv(GL_BLUE_BITS, &bitnum);
	post("blue: %d", bitnum);
   	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &bitnum);
    post("max texture: %d", bitnum);

    post("lighting %d", s_lightState);
    for (int i = 0; i < NUM_LIGHTS; i++)
    {
	    if (s_lights[i])
            post("light%d: on", i);
    }
    post("");
}

/////////////////////////////////////////////////////////
// getWindowInfo
//
/////////////////////////////////////////////////////////
WindowInfo &GemMan :: getWindowInfo()
{
    return(gfxInfo);
}

/////////////////////////////////////////////////////////
// getConstWindowInfo
//
/////////////////////////////////////////////////////////
WindowInfo &GemMan :: getConstWindowInfo()
{
    return(constInfo);
}
