/*

  This is early beginning tutorial, explaining OpenGL initialization and drawing the simple scene(pyramid)

  LICENSE AGREEMENT

  All the parts of this file are provided AS IS and completely WITHOUT ANY WARRANTY OF ANY KIND,
  AND WITHOUT ANY RESPONSIBILITY OF ANY KIND.

  In no event shall author be liable for any indirect, incidental or any other damages (Including
  loss of data, loss of any information, loss of any profit of any kind, loss of any business of any kind,
  and the like), even if arised from any error or any defect in this file.

  Products, trademark names and company names may be appearing in this file may or may not be registered
  trademarks or copyrights of their respective companies or owners of any kind.

*/

/* NEW CODE */

// Add required libs on fly, or you can set 'em up in Settings->Link menu

#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")

/* END */

#include <windows.h>

/* NEW CODE */

#include <gl\gl.h>									
#include <gl\glu.h>

/* END */

// Declare some variables and function prototypes for further usage

HDC dc;

HWND w_handle;
HINSTANCE inst;

/* NEW CODE */

HGLRC _glrc;

/* END */

LRESULT CALLBACK w_process(HWND wnd_handle,UINT system_msg,WPARAM w_param,LPARAM l_param);

WPARAM _process(HWND w_handle);

/* NEW CODE */

void draw();

void setup_screen(int width,int height);

int _pixel_format(HDC dc);

/* END */


// Below functions we need

// Main function
int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev_inst,LPSTR line,int show)
{
	// We want window styles at first

	DWORD ex_style;
	DWORD style;

	// We 'll need it later to set up window size and position.

	RECT rect;
	
	// And at last we need to set a window class

	WNDCLASSEX w_class;

	// Allocate memory for class structure

    memset(&w_class,0,sizeof(WNDCLASSEX));

	// Set up class variables below

	w_class.cbClsExtra = 0; // extra bytes, we want it to be 0
	w_class.cbSize = sizeof(WNDCLASSEX); // size of the whole structure
	w_class.cbWndExtra = 0; // yet another extra bytes, shulda be 0 in our case
	w_class.hbrBackground = (HBRUSH)(COLOR_WINDOW+2); // set up background color, it 'll be black. 
	//if you want it to be white, set +1 instead +2 then
	w_class.hCursor = LoadCursor(NULL,IDC_ARROW); // we 'll set standard cursor, but you can set your own one
    w_class.hIcon = LoadIcon(NULL,IDI_APPLICATION); // standard icon, but culda be NULL instead of it also
    w_class.hIconSm = LoadIcon(NULL,IDI_WINLOGO); // same as above
	w_class.hInstance = inst; // handle to instance
	w_class.lpfnWndProc = w_process; // pointer to window procedure
	w_class.lpszClassName = "test"; // class name
	w_class.lpszMenuName = NULL; // menu name, but if set to NULL, window have no default menu then
	w_class.style = CS_HREDRAW | CS_VREDRAW; // set class style(s)

	// After class infromation is OK and available, it's time to register the class

	RegisterClassEx(&w_class);
	
	// Window styles below

	style = WS_OVERLAPPEDWINDOW;
	ex_style = WS_EX_APPWINDOW;

	// Window rect to set window size (width and height), and window position (left and top)
	// Note that 0,0 yields a top left corner of the screen

	rect.bottom = 480; 
	rect.left = 0;
	rect.right = 640; 
	rect.top = 0;

	// Before creating the window we also need to set up window rectangle and style
 
	AdjustWindowRect(&rect,style,FALSE); // This one only valid before window is created, to change rect of existing window
	// you shulda be using SetWindowPos function from WinAPI

	// Ok, we're ready to create it

	w_handle = CreateWindowEx(
	ex_style, // window style (extended), we had to set up before
	"test", // class name
	"Test", // window name
	style, // yet another window style
	0, // horisontal window position
	0, // vertical window position
	rect.right - rect.left, // window width
	rect.bottom - rect.top, // window height
	NULL, // handle for parent window, it's NULL 'coz we have no such window
	NULL, // handle for menu, it's NULL as well
	inst, // handle to instance
	NULL); // Additional info, but we don't want it at all, so it's NULL too

	// Now let's set window position as well, but note it ain't really necessary, if we 'll skip this, window 'll
	// be applied to the top left of screen

	SetWindowPos(      
    w_handle, // window handle
    HWND_TOP, // how it shulda behave, we wanna it on the top of
    240, // where the left side shulda be
    320, // same for top, note both variables given in client coords
    NULL, // specify new width of the window
    NULL, // same for height, this one and above shulda be given in pixels, but note we don't want it that way
    SWP_NOSIZE); // 'coz we want 2 items we have above to bee ignored, we set NOSIZE to remain window as it is

	/* NEW CODE */

	// Now we need device context

	dc = GetDC(w_handle);

	// Pass it to our pixel format setup func

	_pixel_format(dc);

	// After done, we want to setup GL stuff, i.e. perspective and etc.

	setup_screen(640,480);

	/* END */

	// We now want to set up window show state

	ShowWindow(w_handle,SW_SHOW); // window handle and yet another param to make our window active

	ShowCursor(true); // show mouse cursor too, if no longer required it culda be set to FALSE then

	UpdateWindow(w_handle); // update window

	return _process(w_handle); // return result back to, after it's handled by _process function
}

// Shulda be declared within the class in WinMain function
LRESULT CALLBACK w_process(HWND w_handle,UINT _msg,WPARAM wparam,LPARAM lparam)
{
	LONG lret = 0;

	// Below we need to handle system messages, but we only take care about WM_CLOSE

	switch(_msg)
	{

	case WM_CLOSE : // Exit from application if WM_CLOSE command incoming
	{

	ReleaseDC(w_handle,dc); // Release device context from memory

	UnregisterClass("Test",inst); // Remove our class too

	PostQuitMessage(0); // Kill application
	}
	break;

    default : 

	lret = DefWindowProc(w_handle,_msg,wparam,lparam); // Default processing for all the messages not processed by
	// our application, to make sure that all the messages are processed anyway.

	break;
	}

	return lret;
}

// We will pass window handle to this one in WinMain function (at the end of)
WPARAM _process(HWND w_handle)
{
	MSG msg;

	// This one is the main application loop, it's infinite.

	while(1)										
	{		
	if(PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Dispatch system messages
    { 
	if(msg.message == WM_QUIT) break; // If got WM_QUIT message then break the loop and exit from application

    TranslateMessage(&msg);	// Otherwise, let's find out what the message is exactly				
    DispatchMessage(&msg); // And execute it
	}
    
	/* NEW CODE */

	// Below we draw our stuff
	else
	{
	draw();

	// Swap buffers
	SwapBuffers(dc);
	}

	/* END */

	}

	return(msg.wParam);	
}

/* NEW CODE */

// Below func to set up pixel format and GL context

int _pixel_format(HDC dc) 
{ 
	// Our pixel format struct

    PIXELFORMATDESCRIPTOR desc = {0}; 

	// Int variable to get ChoosePixelFormat func result

    int p_format; 

	// It's strongly recommended you to advance to MSDN for more info about this one below
	// Note : not all the existing variables are set up here, but only required ones
 
    desc.nSize = sizeof(PIXELFORMATDESCRIPTOR);	// Size of struct shulda be set to sizeof(PIXELFORMATDESCRIPTOR)		
    desc.nVersion = 1; // Version shulda be set to 1									
													
    desc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; // Properties of pixel format
    desc.dwLayerMask = PFD_MAIN_PLANE; // Although this was only used in earlier GL versions, we 'll set it up too					
    desc.iPixelType = PFD_TYPE_RGBA; // Type of pixel data, we need RGBA (Red,Green,Blue, Alpha channel value)						
    desc.cColorBits = 32; // If in RGBA mode, then it's size of color buffer excluding alpha values					
    desc.cDepthBits = 24; // Depth of z-buffer (depth buffer)					
    desc.cAccumBits = 0; // Accumulation buffer								
    desc.cStencilBits = 0; // Stencil buffers							
 
	// Check does pixel format match given device context or it ain't

    if((p_format = ChoosePixelFormat(dc,&desc)) == FALSE ) 
    { 
    return 0; 
    } 

	// If results from above are OK, we want to set our pixel format then
    if (SetPixelFormat(dc,p_format,&desc) == FALSE) 
    {  
    return 0; 
    } 
 
	// If we're OK with pixel format, now we need to create GL rendering context for device context
	_glrc = wglCreateContext(dc);

	// We want to make our GL context to be current GL context, although we have no another contexts, we
	// need to call this func in any case.
    wglMakeCurrent(dc,_glrc);

    return 1;
}

void setup_screen(int width,int height)
{
	// Viewport dimensions
	glViewport(0,0,width,height);

	// Projection matrix
	glMatrixMode(GL_PROJECTION);

	// Identity matrix
	glLoadIdentity();

	// Perspective projection matrix
	// Variables : FOV (field of view in angles), aspect ratio (x to y), Z near, Z far (both are clipping planes)
	gluPerspective(90.0f,(float)width/(float)height,1.0f,10000.0f);

	// Model view matrix
	glMatrixMode(GL_MODELVIEW);	
	
	// Identity matrix
	glLoadIdentity();
}

float d = 0.0f;

void draw() 
{
	// Clear the screen and load identity matrix
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	
	glLoadIdentity();								

	// View transform
	// Variables : eye position (3 vars), view direction (3 vars), up vector (3 vars)
	// Explanations : eye position is point from we're looking at, view direction is point we are 
	// looking at, and up vector describes our coordinate system, we set Y to be pointed to the up
	gluLookAt(0,0,2.5,0,0,0,0,1,0);

	// Just a variable to change angle value (in degrees) to rotate our pyramid to
	d = d + 0.5f;

	// Rotate the pyramid using Z axis and Y axis, we pass our "d" variable below
	// Variables : angle, axis (3 vars : x,y and z respectively)
	glRotatef(d,0,0,1);
	glRotatef((d*0.2f),0,1,0);

	// Depth test to grab info how far objects are located from
	glEnable(GL_DEPTH_TEST);

	// Start to draw triangles
	glBegin (GL_TRIANGLES);	

	// Below we set up color for EACH triangle. We draw only 4 sides of pyramid, without drawing it's bottom.
	// You can draw the bottom using GL_QUADS (you will need 1 quad then), or using GL_TRIANGLES (2 triangles to draw then)
	
	glColor3ub(255,255,0);

	// We draw triangle by setting up it's vertices, 3 at all.

	// Note the pyramid has 5 vertices, 1 at the top, and 4 at the bottom. the top vertex is shared between all 4 sides (4 triangles as well)
		
	glVertex3f(0, 1, 0);			
	glVertex3f(-1, -1, -1);	
	glVertex3f(1, -1, -1);

	glColor3ub(255,0,0);

	glVertex3f(0, 1, 0);			
	glVertex3f(-1, -1, 1);	
	glVertex3f(1, -1, 1);

	glColor3ub(0,255,0);

	glVertex3f(0, 1, 0);			
	glVertex3f(-1, -1, -1);	
	glVertex3f(-1, -1, 1);

	glColor3ub(0,0,255);

	glVertex3f(0, 1, 0);			
	glVertex3f(1, -1, 1);	
	glVertex3f(1, -1, -1);

	// After we 're done with drawing, we need to stop it.
	glEnd();
	
	// And disable depth testing then
	glDisable(GL_DEPTH_TEST);
}

/* END */