/*
 *  index.c
 *
 *  WGL/OpenGL program
 *
 *  Copyright (C) 1997 by Nate Robins (ndr@pobox.com)
 *
 *  This program is freely distributable without licensing fees and is
 *  provided without guarantee or warrantee expressed or implied. This
 *  program is not in the public domain.
 */


/* includes */
#include <windows.h>			/* must include this before GL/gl.h */
#include <GL/gl.h>			/* OpenGL header file */
#include <stdio.h>


/* pragmas */
#pragma warning(disable : 4244)		/* disable conversion warnings */
#pragma warning(disable : 4047)		/* disable conversion warnings */


/* globals */
HPALETTE     hPalette;			/* handle to custom palette */


/* functions */

/* WindowProc()
 *  Minimum Window Procedure
 */
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ 
    LONG        lRet = 1;
    PAINTSTRUCT ps;

    switch(uMsg) {
    case WM_CREATE:
        break; 

    case WM_DESTROY:
        break; 

    case WM_PAINT: 
        BeginPaint(hWnd, &ps); 
        EndPaint(hWnd, &ps); 
        break; 

    case WM_QUERYNEWPALETTE:
	SelectPalette(GetDC(hWnd), hPalette, FALSE);/* select custom palette */
	lRet = RealizePalette(GetDC(hWnd));
	break;

    case WM_PALETTECHANGED:
	if(hWnd == (HWND)wParam)        /* make sure we don't loop forever */
	    break;
	SelectPalette(GetDC(hWnd), hPalette, FALSE);/* select custom palette */
	RealizePalette(GetDC(hWnd));	/* remap the custom palette */
	UpdateColors(GetDC(hWnd));
	lRet = 0;
	break;

    case WM_CHAR:
	if(wParam == 27)		/* ESC */
	    PostQuitMessage(0);
	break;

    case WM_SIZE:
	glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
	break;

    case WM_CLOSE:
	PostQuitMessage(0);
	break;

    default: 
        lRet = DefWindowProc(hWnd, uMsg, wParam, lParam); 
        break; 
    }

    return lRet; 
} 


/* oglCreateWindow
 *  Create a window suitable for OpenGL rendering
 */
HWND oglCreateWindow(char* title, int x, int y, int width, int height)
{
    WNDCLASS  wc;
    HWND      hWnd;
    HINSTANCE hInstance;

    /* get this modules instance */
    hInstance = GetModuleHandle(NULL);

    /* fill in the window class structure */
    wc.style         = 0;                           /* no special styles */
    wc.lpfnWndProc   = (WNDPROC)WindowProc;         /* event handler */
    wc.cbClsExtra    = 0;                           /* no extra class data */
    wc.cbWndExtra    = 0;                           /* no extra window data */
    wc.hInstance     = hInstance;                   /* instance */
    wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO); /* load a default icon */
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); /* load a default cursor */
    wc.hbrBackground = NULL;                        /* redraw our own bg */
    wc.lpszMenuName  = NULL;                        /* no menu */
    wc.lpszClassName = title;                       /* use a special class */

    /* register the window class */
    if (!RegisterClass(&wc)) {
      MessageBox(NULL, 
		   "RegisterClass() failed:  Cannot register window class,",
		   "Error", MB_OK);
	return NULL;
    }

    /* create a window */
    hWnd = CreateWindow(title,          /* class */
			title,          /* title (caption) */
			WS_OVERLAPPEDWINDOW |
			WS_CLIPSIBLINGS | WS_CLIPCHILDREN,  /* style */
			x, y, width, height, /* dimensions */
			NULL,		/* no parent */
			NULL,		/* no menu */
			hInstance,	/* instance */
			NULL);		/* don't pass anything to WM_CREATE */

    /* make sure we got a window */
    if (hWnd == NULL) {
	MessageBox(NULL,
		   "CreateWindow() failed:  Cannot create a window.",
		   "Error", MB_OK);
	return NULL;
    }

    /* show the window (map it) */
    ShowWindow(hWnd, SW_SHOW);

    /* send an initial WM_PAINT message (expose) */
    UpdateWindow(hWnd);

    return hWnd;
}

/* oglPixelFormat()
 *  Sets the pixel format for the context
 */
int oglSetPixelFormat(HDC hDC, BYTE type, DWORD flags)
{
    int pf;
    PIXELFORMATDESCRIPTOR pfd;

    /* fill in the pixel format descriptor */
    pfd.nSize        = sizeof(PIXELFORMATDESCRIPTOR);
    pfd.nVersion     = 1;		    /* version (should be 1) */
    pfd.dwFlags      = PFD_DRAW_TO_WINDOW | /* draw to window (not bitmap) */
                       PFD_SUPPORT_OPENGL | /* draw using opengl */
                       flags;               /* user supplied flags */
    pfd.iPixelType   = type;                /* PFD_TYPE_RGBA or COLORINDEX */
    pfd.cColorBits   = 24;
    
    /* get the appropriate pixel format */
    pf = ChoosePixelFormat(hDC, &pfd);
    if (pf == 0) {
       MessageBox(NULL,
		  "ChoosePixelFormat() failed:  Cannot find format specified.",
		  "Error", MB_OK); 
       return 0;
    } 
 
    /* set the pixel format */
    if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
	MessageBox(NULL,
		   "SetPixelFormat() failed:  Cannot set format specified.",
		   "Error", MB_OK);
        return 0;
    } 

    return pf;
}    

/* oglSetPalette()
 *  Sets the palette
 */
BOOL oglSetPalette(HDC hDC)
{
    LOGPALETTE   lgpal;			/* custom logical palette */
    int          nEntries = 5;		/* number of entries in palette */
    PALETTEENTRY peEntries[5] = {	/* entries in custom palette */
        0,   0,   0, NULL,		/* black */
      255,   0,   0, NULL,		/* red */
        0, 255,   0, NULL,		/* green */
        0,   0, 255, NULL,		/* blue */
     255, 255, 255, NULL		/* white */
    };

    /* create a logical palette (for color index mode) */
    lgpal.palVersion    = 0x300;	/* version should be 0x300 */
    lgpal.palNumEntries = nEntries;	/* number of entries in palette */
    if((hPalette = CreatePalette(&lgpal)) == NULL) {
	MessageBox(NULL,
		   "CreatePalette() failed:  Cannot create palette.",
		   "Error", MB_OK);
	return FALSE;
    }

    /* set the palette entries */
    SetPaletteEntries(hPalette, 0, nEntries, peEntries);

    /* select the palette */
    SelectPalette(hDC, hPalette, TRUE); /* map logical into physical palette */

    /* realize the palette */
    RealizePalette(hDC);

    return TRUE;
}

/* main()
 *  Entry point
 */
int main(int argc, char** argv)
{
    HDC       hDC;			/* device context */
    HGLRC     hRC;			/* opengl context */
    HWND      hWnd;			/* window */
    MSG       msg;			/* message */

    /* create a window */
    hWnd = oglCreateWindow("OpenGL", 0, 0, 200, 200);
    if (hWnd == NULL)
      exit(1);

    /* get the device context */
    hDC = GetDC(hWnd);

    /* set the pixel format */
    if (oglSetPixelFormat(hDC, PFD_TYPE_COLORINDEX, PFD_DOUBLEBUFFER) == 0)
      exit(1);
      
    /* create and set the palette */
    if (!oglSetPalette(hDC))
      exit(1);

    /* create an OpenGL context */
    hRC = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hRC);

    /* now we can start changing state & rendering */
    while(1) {
        /* first, check for (and process) messages in the queue */
	while(PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) {
	    if(GetMessage(&msg, hWnd, 0, 0)) {
		TranslateMessage(&msg); /* translate virtual-key messages */
		DispatchMessage(&msg);	/* call the window proc */
	    } else {
		goto quit;
	    }
	}

	/* draw the OpenGL */
	glClear(GL_COLOR_BUFFER_BIT);
	glRotatef(1.0, 0.0, 0.0, 1.0);
	glBegin(GL_TRIANGLES);
	glIndexi(1);
	glVertex2i( 0,  1);
	glIndexi(2);
	glColor3f(0.0, 1.0, 0.0);
	glVertex2i(-1, -1);
	glIndexi(3);
	glVertex2i( 1, -1);
	glEnd();
	glFlush();
	SwapBuffers(hDC);		/* nop if singlebuffered */
	Sleep(500);
    }

quit:

    /* clean up */
    DeleteObject(hPalette);		/* delete the palette */
    wglMakeCurrent(NULL, NULL);		/* make our context 'un-'current */
    ReleaseDC(hDC, hWnd);		/* release handle to DC */
    wglDeleteContext(hRC);		/* delete the rendering context */
    DestroyWindow(hWnd);		/* destroy the window */

    return 0;
}
