/*

  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.

*/

// 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")

#include <windows.h>
#include <stdio.h>

#include <gl\gl.h>									
#include <gl\glu.h>

// Declare some variables and function prototypes for further usage

HDC dc;

HWND w_handle;
HINSTANCE inst;

HGLRC _glrc;

LRESULT CALLBACK w_process(HWND wnd_handle,UINT system_msg,WPARAM w_param,LPARAM l_param);

WPARAM _process(HWND w_handle);

void draw();

void setup_screen(int width,int height);

int _pixel_format(HDC dc);

/* NEW */

void load_data();

unsigned int get_tga_file(char* t_file);

unsigned int t_id;

// Below functions we need

void load_data()
{
	t_id  = get_tga_file("texture.tga");	

	// Report if error occured while loading TGA file, using t_id value

	if(t_id == 0)
	{
	MessageBox(NULL,"Can't load texture",NULL,MB_OK);
	}

}

/* END */

// 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

	// 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);

	/* NEW */

	load_data();

	/* 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
	}
    
	// Below we draw our stuff
	else
	{
	draw();

	// Swap buffers
	SwapBuffers(dc);
	}

	}

	return(msg.wParam);	
}


// 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();
}

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);

	/* NEW */

	// Depth test to grab info how far objects are located from
	glEnable(GL_DEPTH_TEST);
	// Enable 2D texturing
	glEnable(GL_TEXTURE_2D);


	// Check is texture id valid
	if(t_id > 0)
	{
	glBindTexture(GL_TEXTURE_2D,t_id);
	}

	// Start to draw quads
	glBegin(GL_QUADS);
	
	// Below we set up color for quad. We draw the quad by passing it's 4 vertices to GL
	
	glColor3ub(255,255,255);

	glTexCoord2f(0.0, 1.0); 
	glVertex3f(-1, 1, 0);

	glTexCoord2f(0.0, 0.0); 
	glVertex3f(-1, -1, 0);

	glTexCoord2f(1.0, 0.0); 
	glVertex3f(1, -1, 0);

	glTexCoord2f(1.0, 1.0);
	glVertex3f(1, 1, 0);
		
	// After we 're done with drawing, we need to stop it.
	glEnd();
	
	// And disable depth testing then
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_2D);

	/* END */
}

/* NEW */

unsigned int get_tga_file(char* t_file)
{
	// Below variables to hold data we will need.
    FILE *_file;

    long size;

    int color;
  
    unsigned char header[18];

	// Open texture file in RB mode (Read/Binary)
    _file = fopen(t_file, "rb");

	// If unavailable, then return 0
    if(!_file)
    {
    return 0;
    }

	// Otherwise, get header from TGA
    fread(header,1,sizeof(header),_file);

	// If not RGB image, return 0
	if(header[2] != 2) { return 0;}

    int width = header[13] * 256 + header[12];
    int height = header[15] * 256 + header[14];

	// Compute how many bits per texture pixel this TGA has available.
	// Convert this data to channels amount, e.g. 3 or 4 channels (RGB or RGBA)
    color = header[16] / 8;

	// Get array size to hold image. We compute total amount of pixels, i.e. width * height, after that we
	// need to allocate space for each pixel, so we multiply previous result with bits per pixel variable
    size = width * height * color;

	// After we know the size of image data array, we declare it
    unsigned char *image = new unsigned char[sizeof(unsigned char) * size];

	// Read pixel data from image to our image array
    fread(image,sizeof(unsigned char),size,_file);

	// TGA holds BGR data, but we want it to be RGB instead. So we need to swap channels.
    for(long i = 0; i < size; i += color)
    {
    unsigned char tmp = image[i];

    image[i] = image[i+2];

    image[i+2] = tmp;
    }

	// Close file after done
    fclose(_file);

	// Declare variable to hold texture ID
    unsigned int tex_id;

	// Generate texture and return it's ID
    glGenTextures(1,&tex_id);
   
	// And bind it
    glBindTexture(GL_TEXTURE_2D,tex_id);

	// We want only texture colors to operate with, i.e. no color mixing required
	// You can set GL_MODULATE though, to mix texture color with color you want.
    glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);

	// Set texture filtering
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
   
	// Set texture mapping mode
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

	// Select texture channels info using data we got from texture file. Shulda be 3 or 4 channels (RGB or RGBA)
	// Otherwise return 0
    if(color == 3) { color = GL_RGB;}
    else if(color == 4) { color = GL_RGBA;}
    else { return 0;}

	// Build mipmaps by passing data we collected above
    gluBuild2DMipmaps(GL_TEXTURE_2D,color,width,height,color,GL_UNSIGNED_BYTE,image);

	// If everything is OK, return txeture ID then.
    return tex_id;
}

/* END */


/* TGA HEADER, 18 BYTES LENGTH TOTAL

BYTE    ITEM         SIZE    DESCRIPTION
****    ****         ****    *********** 
 0      Offset         1     Usually 0, add 18 to this value to find the start of the palette/image data.
 1      ColorType      1     Image type. 0 = RGB, 1 = Indexed.
 2      ImageType      1     0 = None, 1 = Indexed, 2 = RGB, 3 = Greyscale, 8 = RLE encoded.
 3      PaletteStart   2     Start of palette.
 5      PaletteLen     2     Number of palette entries.
 7      PalBits        1     Bits per colour entry.
 8      X Origin       2     Image X Origin
10      Y Origin       2     Image Y Origin
12      Width          2     Image width (Pixels).
14      Height         2     Image height (Pixels)
16      BPP            1     Bits per pixel (8,16,24 or 32)
17      Orientation    1     If Bit 5 is set, the image will be upside down (like BMP)

*/