#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <time.h>
#include <stdio.h>

// Overall dimensions of initial window.
const int INITIAL_WIDTH = 800;
const int INITIAL_HEIGHT = 600;

HPALETTE hPalette = NULL;

static LPCTSTR lpszAppName = "Skeleton";
static HINSTANCE hInstance;

// Mouse coordinates
int xMousePos = 0;
int yMousePos = 0;

// Dimensions of viewing window.
int viewHeight;
int viewWidth;

// Object data for testing.
GLUquadricObj *sphere;
GLfloat rotation;
GLfloat tilt = 45.0f;
GLfloat delta = 0.3f;
GLfloat zoom = 45.0f;

// Change viewing volume and viewport.  Called when window is resized
void ChangeSize(GLsizei width, GLsizei height)
	{
	viewWidth = width;
	viewHeight = height;
	if(height == 0)
		height = 1;
	glViewport(0, 0, width, height);
	}


// Initialize the Rendering Context
void SetupRC(void)
{
	sphere = gluNewQuadric();
	gluQuadricDrawStyle(sphere, GLU_LINE);
	glEnable(GL_DEPTH_TEST);
	glClearColor(0.0, 0.0, 0.2f, 0.0);
	glColor3f(1.0f, 0.8f, 0.0);
}


void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(zoom, (GLfloat)viewWidth/(GLfloat)viewHeight, 20, 600);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glPushMatrix();
	glTranslatef(0.0, 0.0, -50.0);
	glRotatef(tilt, 1.0, 0.0, 0.0);
	glRotatef(rotation, 0.0, 0.0, 1.0);
	gluSphere(sphere, 20.0, 40, 40);
	glPopMatrix();
}


// Select the pixel format for a given device context
void SetDCPixelFormat(HDC hDC)
	{
	int nPixelFormat;
	static PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),  // Size of this structure
		1,                              // Version of this structure    
		PFD_DRAW_TO_WINDOW |            // Draw to Window (not to bitmap)
		PFD_SUPPORT_OPENGL |			// Support OpenGL calls in window
		PFD_DOUBLEBUFFER,               // Double buffered
		PFD_TYPE_RGBA,                  // RGBA Color mode
		24,                             // Want 24bit color if available
		0,0,0,0,0,0,                    // Not used to select mode
		0,0,                            // Not used to select mode
		0,0,0,0,0,                      // Not used to select mode
		16,                             // Size of depth buffer (16 is sufficient)
		0,                              // Not used to select mode
		0,                              // Not used to select mode
		PFD_MAIN_PLANE,                 // Draw in main plane
		0,                              // Not used to select mode
		0,0,0 };                        // Not used to select mode
	nPixelFormat = ChoosePixelFormat(hDC, &pfd);
	SetPixelFormat(hDC, nPixelFormat, &pfd);
	}


HPALETTE GetOpenGLPalette(HDC hDC)
{
	HPALETTE hRetPal = NULL;	// Handle to palette to be created
	PIXELFORMATDESCRIPTOR pfd;	// Pixel Format Descriptor
	LOGPALETTE *pPal;			// Pointer to memory for logical palette
	int nPixelFormat;			// Pixel format index
	int nColors;				// Number of entries in palette
	int i;						// Counting variable
	BYTE RedRange,GreenRange,BlueRange;
								// Range for each color entry (7,7,and 3)
	nPixelFormat = GetPixelFormat(hDC);
	DescribePixelFormat(hDC, nPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	if(!(pfd.dwFlags & PFD_NEED_PALETTE))
		return NULL;
	nColors = 1 << pfd.cColorBits;	
	pPal = (LOGPALETTE*)malloc(sizeof(LOGPALETTE) +nColors*sizeof(PALETTEENTRY));
	pPal->palVersion = 0x300;		// Windows 3.0
	pPal->palNumEntries = nColors; // table size
	RedRange = (1 << pfd.cRedBits) -1;
	GreenRange = (1 << pfd.cGreenBits) - 1;
	BlueRange = (1 << pfd.cBlueBits) -1;
	for(i = 0; i < nColors; i++)
		{
		// Fill in the 8-bit equivalents for each component
		pPal->palPalEntry[i].peRed = (i >> pfd.cRedShift) & RedRange;
		pPal->palPalEntry[i].peRed = (unsigned char)(
			(double) pPal->palPalEntry[i].peRed * 255.0 / RedRange);

		pPal->palPalEntry[i].peGreen = (i >> pfd.cGreenShift) & GreenRange;
		pPal->palPalEntry[i].peGreen = (unsigned char)(
			(double)pPal->palPalEntry[i].peGreen * 255.0 / GreenRange);

		pPal->palPalEntry[i].peBlue = (i >> pfd.cBlueShift) & BlueRange;
		pPal->palPalEntry[i].peBlue = (unsigned char)(
			(double)pPal->palPalEntry[i].peBlue * 255.0 / BlueRange);

		pPal->palPalEntry[i].peFlags = (unsigned char) NULL;
		}
		
	hRetPal = CreatePalette(pPal);
	SelectPalette(hDC,hRetPal,FALSE);
	RealizePalette(hDC);
	free(pPal);
	return hRetPal;
}


LRESULT CALLBACK WndProc(   HWND    hWnd,
							UINT    message,
							WPARAM  wParam,
							LPARAM  lParam)
{
	static HGLRC hRC;
	static HDC hDC;
	switch (message)
	{
		// Window creation, setup for OpenGL
		case WM_CREATE:
			hDC = GetDC(hWnd);              
			SetDCPixelFormat(hDC);          
			hPalette = GetOpenGLPalette(hDC);
			hRC = wglCreateContext(hDC);
			wglMakeCurrent(hDC, hRC);
			SetupRC();
			break;

		// Window is being destroyed, cleanup
		case WM_DESTROY:
			wglMakeCurrent(hDC,NULL);
			wglDeleteContext(hRC);
			ReleaseDC(hWnd, hDC);
			if(hPalette != NULL)
				DeleteObject(hPalette);
			PostQuitMessage(0);
			break;

		// Window is resized.
		case WM_SIZE:
			ChangeSize(LOWORD(lParam), HIWORD(lParam));
			break;

		case WM_PAINT:
			{
				rotation += delta;
				if (rotation > 360.0f)
					rotation -= 360.0f;
				display();
				SwapBuffers(hDC);

				static int frames;
				static clock_t oldtime;
				static float rate;
				frames++;
				if (frames == 100)
				{
					frames = 0;
					clock_t newtime = clock();
					rate = 100.0f / ((newtime - oldtime) / float(CLOCKS_PER_SEC));
					oldtime = newtime;
				}
				char buffer[120];
				sprintf(buffer,"Size (%d,%d)   Mouse (%d,%d)   Zoom %3.1f   FPS %3.1f", 
					viewWidth, viewHeight, xMousePos, yMousePos, zoom, rate);
				SetWindowText(hWnd, buffer);
			}
			break;

		case WM_QUERYNEWPALETTE:
			if(hPalette)
			{
				int nRet;
				SelectPalette(hDC, hPalette, FALSE);
				nRet = RealizePalette(hDC);
				InvalidateRect(hWnd,NULL,FALSE);
				return nRet;
			}
			break;

	
		case WM_PALETTECHANGED:
			if((hPalette != NULL) && ((HWND)wParam != hWnd))
			{
				SelectPalette(hDC,hPalette,FALSE);
				RealizePalette(hDC);
				UpdateColors(hDC);
				return 0;
			}
			break;

		case WM_KEYDOWN:
			switch (wParam)
			{
			case VK_UP:
				if (zoom < 178.0f)
					zoom += 2.0f;
				break;
			case VK_DOWN:
				if (zoom > 3.0f)
					zoom -= 2.0f;
				break;
			case VK_LEFT:
				tilt += 5.0f;
				break;
			case VK_RIGHT:
				tilt -= 5.0f;
				break;
			case VK_F1:
				break;
			case VK_F2:
				break;
			case VK_F3:
				break;
			case VK_F4:
				break;
			case VK_F5:
				break;
			case VK_F6:
				break;
			case VK_ESCAPE:
				SendMessage(hWnd, WM_CLOSE, 0, 0L);
				break;
			}
			InvalidateRect(hWnd,NULL,FALSE);
			break;

		case WM_MOUSEMOVE:
			xMousePos = LOWORD(lParam);
			yMousePos = HIWORD(lParam);
			break;

		default:
		    return (DefWindowProc(hWnd, message, wParam, lParam));

	}

    return (0L);
}


int APIENTRY WinMain(   HINSTANCE       hInst,
						HINSTANCE       hPrevInstance,
						LPSTR           lpCmdLine,
						int             nCmdShow)
{
	MSG        msg;
	WNDCLASS   wc;
	HWND       hWnd;

	hInstance = hInst;

	wc.style                = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc          = (WNDPROC) WndProc;
	wc.cbClsExtra           = 0;
	wc.cbWndExtra           = 0;
	wc.hInstance            = hInstance;
	wc.hIcon                = NULL;
	wc.hCursor              = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground        = NULL;   // Don't need background for OpenGL.
	wc.lpszMenuName         = NULL;
	wc.lpszClassName        = lpszAppName;
	if(RegisterClass(&wc) == 0)
		return FALSE;
	hWnd = CreateWindow(
				lpszAppName,
				lpszAppName,
				WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
				CW_USEDEFAULT, 
				CW_USEDEFAULT,
				INITIAL_WIDTH, INITIAL_HEIGHT,
				NULL,
				NULL,
				hInstance,
				NULL);
	if(hWnd == NULL)
		return FALSE;
	ShowWindow(hWnd,SW_SHOW);
	UpdateWindow(hWnd);
	while( GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}


