/*
 *  menu.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 */


/* globals */
BOOL  Rotate = TRUE;			/* rotating? */
HMENU hPopup = NULL;			/* popup menu */


/* functions */

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

    switch(uMsg) {
    case WM_CREATE:
        break; 

    case WM_DESTROY:
        break; 

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

    case WM_RBUTTONDOWN:
        point.x = LOWORD(lParam);
        point.y = HIWORD(lParam);
	ClientToScreen(hWnd, &point);
	TrackPopupMenu(hPopup, TPM_LEFTALIGN, point.x, point.y,
		       0, hWnd, NULL);
	break;

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

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

    case WM_COMMAND:
	printf("WM_COMMAND: %c\n", LOWORD(wParam));
	switch(LOWORD(wParam)) {
	case 's':
	    Rotate = FALSE;
	    break;
	case 'r':
	    Rotate = TRUE;
	    break;
	case 'x':
	    PostQuitMessage(0);
	    break;
	}
	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 | /* resize handles, etc */
			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;
}    

/* menubar()
 * create a menubar for the window
 */
void menubar(HWND hWnd)
{
    HMENU        hFileMenu;		/* file menu handle */
    HMENU        hDrawMenu;		/* draw menu handle */
    HMENU        hMenu;			/* menu bar handle */
    MENUITEMINFO item;			/* item info */

    /* create the menus */
    hMenu     = CreateMenu();
    hFileMenu = CreateMenu();
    hDrawMenu = CreateMenu();

    /* fill up the file menu */
    item.cbSize      = sizeof(MENUITEMINFO);
    item.fMask       = MIIM_ID | MIIM_TYPE | MIIM_SUBMENU;
    item.fType       = MFT_STRING;
    item.hSubMenu    = NULL;

    item.wID        = 'x';
    item.dwTypeData = "E&xit";
    item.cch        = strlen("E&xit");
    InsertMenuItem(hFileMenu, 0, FALSE, &item);

    /* now do the draw menu */
    item.wID        = 'r';
    item.dwTypeData = "&Rotate";
    item.cch        = strlen("&Rotate");
    InsertMenuItem(hDrawMenu, 0, FALSE, &item);
    item.wID        = 's';
    item.dwTypeData = "&Don't Rotate";
    item.cch        = strlen("&Don't Rotate");
    InsertMenuItem(hDrawMenu, 1, FALSE, &item);

    /* now do the main menu */
    item.wID        = 0;
    item.dwTypeData = "&File";
    item.cch        = strlen("&File");
    item.hSubMenu   = hFileMenu;
    InsertMenuItem(hMenu, 0, FALSE, &item);
    item.wID        = 0;
    item.dwTypeData = "&Draw";
    item.cch        = strlen("&Draw");
    item.hSubMenu   = hDrawMenu;
    InsertMenuItem(hMenu, 1, FALSE, &item);

    /* attach the menu to the window */
    SetMenu(hWnd, hMenu);

    /* use the draw menu as a popup menu */
    hPopup = hDrawMenu;
}

/* 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);

    /* set up a menu bar */
    menubar(hWnd);

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

    /* set the pixel format */
    if (oglSetPixelFormat(hDC, PFD_TYPE_RGBA, PFD_DOUBLEBUFFER) == 0)
      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 and possibly rotate a triangle around */
	glClear(GL_COLOR_BUFFER_BIT);
	if(Rotate)
	    glRotatef(1.0, 0.0, 0.0, 1.0);
	glBegin(GL_TRIANGLES);
	glColor3f(1.0, 0.0, 0.0);
	glVertex2i( 0,  1);
	glColor3f(0.0, 1.0, 0.0);
	glVertex2i(-1, -1);
	glColor3f(0.0, 0.0, 1.0);
	glVertex2i( 1, -1);
	glEnd();
	glFlush();
	SwapBuffers(hDC);		/* nop if singlebuffered */
    }

quit:

    /* clean up */
    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;
}
