/*
 * Modified version of the NeHe tutorial 5.
 * The original code was created by Jeff Molofee 2000
 * nehe.gamedev.net
 *
 * Change set 1:
 * Changed entry point from WinMain to WinMainCRTStartup
 * Removed call to memset()
 *
 * Change set 2:
 * Eliminated windowed mode
 * Locked the window size
 * Merged InitGL and ReSizeGLScene functions into CreateGLWindow
 * Simplified keyboard processing with GetAsyncKeyState
 * Removed code to handle screensaver and monitor-power events
 * Removed render-when-active switch
 * Simplified OpenGL state setup
 */

#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>

/*
 * Constants:
 */
#define MAIN_X (800)
#define MAIN_Y (600)
#define MAIN_BITS (16)

HDC      hDC=NULL;
HGLRC    hRC=NULL;
HWND     hWnd=NULL;
HINSTANCE  hInstance;

GLfloat  rtri;
GLfloat  rquad;

LRESULT  CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int DrawGLScene(GLvoid)
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  /* Draw pyramid: */
  glLoadIdentity();                  
  glTranslatef(-1.5f,0.0f,-6.0f);    
  glRotatef(rtri,0.0f,1.0f,0.0f);    
  glBegin(GL_TRIANGLES);             
    glColor3f(1.0f,0.0f,0.0f);       
    glVertex3f( 0.0f, 1.0f, 0.0f);   
    glColor3f(0.0f,1.0f,0.0f);       
    glVertex3f(-1.0f,-1.0f, 1.0f);   
    glColor3f(0.0f,0.0f,1.0f);       
    glVertex3f( 1.0f,-1.0f, 1.0f);   
    glColor3f(1.0f,0.0f,0.0f);       
    glVertex3f( 0.0f, 1.0f, 0.0f);   
    glColor3f(0.0f,0.0f,1.0f);       
    glVertex3f( 1.0f,-1.0f, 1.0f);   
    glColor3f(0.0f,1.0f,0.0f);       
    glVertex3f( 1.0f,-1.0f, -1.0f);  
    glColor3f(1.0f,0.0f,0.0f);       
    glVertex3f( 0.0f, 1.0f, 0.0f);   
    glColor3f(0.0f,1.0f,0.0f);       
    glVertex3f( 1.0f,-1.0f, -1.0f);  
    glColor3f(0.0f,0.0f,1.0f);       
    glVertex3f(-1.0f,-1.0f, -1.0f);  
    glColor3f(1.0f,0.0f,0.0f);       
    glVertex3f( 0.0f, 1.0f, 0.0f);   
    glColor3f(0.0f,0.0f,1.0f);       
    glVertex3f(-1.0f,-1.0f,-1.0f);   
    glColor3f(0.0f,1.0f,0.0f);       
    glVertex3f(-1.0f,-1.0f, 1.0f);   
  glEnd();

  /* Draw cube: */
  glLoadIdentity();                  
  glTranslatef(1.5f,0.0f,-7.0f);     
  glRotatef(rquad,1.0f,1.0f,1.0f);   
  glBegin(GL_QUADS);                 
    glColor3f(0.0f,1.0f,0.0f);       
    glVertex3f( 1.0f, 1.0f,-1.0f);   
    glVertex3f(-1.0f, 1.0f,-1.0f);   
    glVertex3f(-1.0f, 1.0f, 1.0f);   
    glVertex3f( 1.0f, 1.0f, 1.0f);   
    glColor3f(1.0f,0.5f,0.0f);       
    glVertex3f( 1.0f,-1.0f, 1.0f);   
    glVertex3f(-1.0f,-1.0f, 1.0f);   
    glVertex3f(-1.0f,-1.0f,-1.0f);   
    glVertex3f( 1.0f,-1.0f,-1.0f);   
    glColor3f(1.0f,0.0f,0.0f);       
    glVertex3f( 1.0f, 1.0f, 1.0f);   
    glVertex3f(-1.0f, 1.0f, 1.0f);   
    glVertex3f(-1.0f,-1.0f, 1.0f);   
    glVertex3f( 1.0f,-1.0f, 1.0f);   
    glColor3f(1.0f,1.0f,0.0f);       
    glVertex3f( 1.0f,-1.0f,-1.0f);   
    glVertex3f(-1.0f,-1.0f,-1.0f);   
    glVertex3f(-1.0f, 1.0f,-1.0f);   
    glVertex3f( 1.0f, 1.0f,-1.0f);   
    glColor3f(0.0f,0.0f,1.0f);       
    glVertex3f(-1.0f, 1.0f, 1.0f);   
    glVertex3f(-1.0f, 1.0f,-1.0f);   
    glVertex3f(-1.0f,-1.0f,-1.0f);   
    glVertex3f(-1.0f,-1.0f, 1.0f);   
    glColor3f(1.0f,0.0f,1.0f);       
    glVertex3f( 1.0f, 1.0f,-1.0f);   
    glVertex3f( 1.0f, 1.0f, 1.0f);   
    glVertex3f( 1.0f,-1.0f, 1.0f);   
    glVertex3f( 1.0f,-1.0f,-1.0f);   
  glEnd();

  rtri+=0.2f;
  rquad-=0.15f;
  return TRUE;
}

GLvoid KillGLWindow(GLvoid)
{
  ChangeDisplaySettings(NULL,0);
  ShowCursor(TRUE);

  if (hRC)
  {
    if (!wglMakeCurrent(NULL,NULL))
    {
      MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    }

    if (!wglDeleteContext(hRC))
    {
      MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    }
    hRC=NULL;
  }

  if (hDC && !ReleaseDC(hWnd,hDC))
  {
    MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hDC=NULL;
  }

  if (hWnd && !DestroyWindow(hWnd))
  {
    MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hWnd=NULL;
  }

  if (!UnregisterClass("OpenGL",hInstance))
  {
    MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
    hInstance=NULL;
  }
}

/*  This Code Creates Our OpenGL Window. */
 
BOOL CreateGLWindow()
{
  GLuint    PixelFormat;
  WNDCLASS  wc;

  /* Register the window class: */
  hInstance         = GetModuleHandle(NULL);
  wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  wc.lpfnWndProc    = (WNDPROC) WndProc;
  wc.cbClsExtra     = 0;
  wc.cbWndExtra     = 0;
  wc.hInstance      = hInstance;
  wc.hIcon          = LoadIcon(NULL, IDI_WINLOGO);
  wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground  = NULL;
  wc.lpszMenuName   = NULL;
  wc.lpszClassName  = "OpenGL";
  if (!RegisterClass(&wc))
  {
    MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }
  
  /* Change the display settings: */
  static DEVMODE dmScreenSettings = {0};
  dmScreenSettings.dmSize=sizeof(dmScreenSettings);
  dmScreenSettings.dmPelsWidth    = MAIN_X;
  dmScreenSettings.dmPelsHeight   = MAIN_Y;
  dmScreenSettings.dmBitsPerPel   = MAIN_BITS;
  dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
  if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
  {
    MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
    return FALSE;
  }
	ShowCursor(FALSE);

  /* Create the window: */
  if (!(hWnd=CreateWindowEx(
    WS_EX_APPWINDOW,
    "OpenGL",
    "NeHe's Solid Object Tutorial",
    WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
    0, 0,
    MAIN_X,
    MAIN_Y,
    NULL,
    NULL,
    hInstance,
    NULL)))
  {
    KillGLWindow();
    MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }
  
  /* Obtain the device context for the window: */
  if (!(hDC=GetDC(hWnd)))
  {
    KillGLWindow();
    MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }

  /* Find an acceptable pixel format: */
  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),  /* nSize */
    1,                              /* nVersion */
    PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, /* dwFlags */
    PFD_TYPE_RGBA,                  /* iPixelType */
    MAIN_BITS,                      /* cColorBits */
    0, 0,                           /* cRedBits,   cRedShift */
    0, 0,                           /* cGreenBits, cGreenShift */
    0, 0,                           /* cBlueBits,  cBlueShift */
    0, 0,                           /* cAlphaBits, cAlphaShift */   
    0,                              /* cAccumBits */
    0,                              /* cAccumRedBits */
    0,                              /* cAccumGreenBits */
    0,                              /* cAccumBlueBits */
    0,                              /* cAccumAlphaBits */
    16,                             /* cDepthBits */
    0,                              /* cStencilBits */
    0,                              /* cAuxBuffers */
    PFD_MAIN_PLANE,                 /* iLayerType */
    0,                              /* bReserved */
    0,                              /* dwLayerMask */
    0,                              /* dwVisibleMask */
    0                               /* dwDamageMask */
  };
  if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))
  {
    KillGLWindow();
    MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }

  /* Use the pixel format: */
  if(!SetPixelFormat(hDC,PixelFormat,&pfd))
  {
    KillGLWindow();
    MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }

  /* Create the OpenGL instance: */
  if (!(hRC=wglCreateContext(hDC)))
  {
    KillGLWindow();
    MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }

  /* Make the instance active: */
  if(!wglMakeCurrent(hDC,hRC))
  {
    KillGLWindow();
    MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
    return FALSE;
  }

  /* Set up the window: */
  ShowWindow(hWnd,SW_SHOW);
  SetForegroundWindow(hWnd);
  SetFocus(hWnd);

  /* Set up the OpenGL options: */
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0f, (float)MAIN_X/MAIN_Y, 0.1f, 100.0f);
  glMatrixMode(GL_MODELVIEW);

  glClearDepth(1.0f);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);

  return TRUE;
}

LRESULT CALLBACK WndProc(HWND  hWnd, UINT  uMsg, WPARAM  wParam, LPARAM  lParam)
{
  switch (uMsg)
  {
    case WM_CLOSE:
    {
      PostQuitMessage(0);
      return 0;
    }
  }

  /* Pass All Unhandled Messages To DefWindowProc: */
  return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

/**
 * Native program entry point
 */
void WINAPI WinMainCRTStartup()
{
  MSG    msg;
  BOOL  done=FALSE;

  /* Create the OpenGL Window: */
  if (!CreateGLWindow())
  {
    return;
  }

  /* Message loop: */
  while(!done)
  {
    if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
    {
      if (msg.message==WM_QUIT)
      {
        done=TRUE;
      }
      else
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
    else
    {
      if ((!DrawGLScene()) || GetAsyncKeyState(VK_ESCAPE))
      {
        done=TRUE;
      }
      else
      {
        SwapBuffers(hDC);
      }
    }
  }

  /* Quit: */
  KillGLWindow();
  return;
}
