#include "lmfeffort.h"
#include <stdio.h>
#include <GL\GL.h>
#include <time.h>

#pragma comment( lib, "user32.lib" )
#pragma comment( lib, "opengl32.lib" )
#pragma comment( lib, "gdi32.lib" )

HGLRC hGLRenderContext;
HDC ____hdc;
lmf_file_t* pModelFile;

/***************************************************/
/*					   MODELS					   */
/***************************************************/

void M_ConvertToLMF( char* output ) {
	FILE* pLMF;
	BYTE bByte; INT num;

	printf( "Creating %s...\n", output );

	pLMF = fopen( output, "wb" );
	if( !pLMF ) {
		MessageBoxA( NULL, "Couldn't open the LMF for writing", "Error", MB_ICONERROR );
		exit( -1 );
	}

	num = pModelFile->numfaces*3;
	fputs( "LMF0", pLMF );
	bByte = 0xFF;
	for( INT i = 0; i < 4; i++ ) fwrite( &bByte, 1, 1, pLMF );
	fwrite( &num, 4, 1, pLMF );
	
	for( INT f = 0; f < pModelFile->numfaces; f++ ) {
		for( INT i = 0; i < 3; i++ ) {
			fwrite( &pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].x,
				4, 1, pLMF );
			fwrite( &pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].y,
				4, 1, pLMF );
			fwrite( &pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].z,
				4, 1, pLMF );

			printf( "Writing vertexes: %f %f %f\n", pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].x,
				pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].y, pModelFile->vertexes[ pModelFile->faces[f].vertexes[i]-1 ].z );
		}
	}

	fclose( pLMF );

	printf( "\n--------------------\n" );
	printf( "Written LMF with %i vertexes\n\n", pModelFile->numvertexes );
}

void M_LoadLMF( char* filename ) {
	FILE* pLMFFile;
	lmf_file_t* pModel;
	BYTE bByte;

	printf( "Loading %s...\n", filename );

	pLMFFile = fopen( filename, "rb" );
	if( !pLMFFile ) {
		MessageBoxA( NULL, "Couldn't open the LMF", "Error", MB_ICONERROR );
		exit( -1 );
	}

	pModel = (lmf_file_t*)malloc( sizeof(lmf_file_t) );
	pModel->vertexes = NULL;

	bByte = 0; pModel->numvertexes = 0;

	fseek( pLMFFile, 8, 0 );
	fread( &pModel->numvertexes, 4, 1, pLMFFile );

	pModel->vertexes = (lmf_vec_t*)malloc( pModel->numvertexes*sizeof(lmf_vec_t) );
	for( INT v = 0; v < pModel->numvertexes; v++ ) {
		fread( &pModel->vertexes[v].x, 4, 1, pLMFFile );
		fread( &pModel->vertexes[v].y, 4, 1, pLMFFile );
		fread( &pModel->vertexes[v].z, 4, 1, pLMFFile );
		printf( "Reading vertexes: %f %f %f\n", pModel->vertexes[v].x, pModel->vertexes[v].y, pModel->vertexes[v].z );
	}

	fclose( pLMFFile );

	pModel->colors = (BYTE*)malloc( pModel->numvertexes*3 );
	for( INT i = 0; i < pModel->numvertexes; i+=3 ) {
		srand( time(NULL) );
		pModel->colors[i] = rand()%255;
		pModel->colors[i+1] = rand()%255;
		pModel->colors[i+2] = rand()%255;
	}

	pModel->type = EFFORT_LMF;

	printf( "\n--------------------\n" );
	printf( "Model finished with %i vertexes\n\n", pModel->numvertexes );

	pModelFile = pModel;
}

void M_LoadOBJ( char* filename ) {
	FILE* pLMFFile;
	lmf_file_t* pModel;
	char pLine[128];
	BYTE bByte, bPosition;

	INT placeholder;

	printf( "Loading %s...\n", filename );

	pLMFFile = fopen( filename, "rb" );
	if( !pLMFFile ) {
		MessageBoxA( NULL, "Couldn't open the OBJ", "Error", MB_ICONERROR );
		exit( -1 );
	}

	pModel = (lmf_file_t*)malloc( sizeof(lmf_file_t) );
	pModel->vertexes = NULL; pModel->normals = NULL; pModel->faces = NULL; pModel->textures = NULL;

	bByte = 0; bPosition = 0; placeholder = 0;
	pModel->numvertexes = 0; pModel->numnormals = 0; pModel->numfaces = 0; pModel->numtextures = 0;

	memset( pLine, 0, 128 );
	while( !feof(pLMFFile) ) {
		if( !fread( &bByte, 1, 1, pLMFFile ) ) break;

		if( bByte == '\r' ) fread( &bByte, 1, 1, pLMFFile );
		if( bByte == '\n' ) {
			switch( pLine[0] ) {
				case 'v': {
					pModel->vertexes = (lmf_vec_t*)realloc( pModel->vertexes, (pModel->numvertexes+1)*sizeof(lmf_vec_t) );
					sscanf( pLine, "v %f %f %f", &pModel->vertexes[pModel->numvertexes].x,
						&pModel->vertexes[pModel->numvertexes].y, &pModel->vertexes[pModel->numvertexes].z );

					printf( "Reading vertexes: %f %f %f\n", pModel->vertexes[pModel->numvertexes].x,
						pModel->vertexes[pModel->numvertexes].y, pModel->vertexes[pModel->numvertexes].z );

					pModel->numvertexes++;
				} break;
				case 'n': { //UNUSED
					pModel->normals = (lmf_vec_t*)realloc( pModel->normals, (pModel->numnormals+1)*sizeof(lmf_vec_t) );
					sscanf( pLine, "n %f %f %f", &pModel->normals[pModel->numnormals].x,
						&pModel->normals[pModel->numnormals].y, &pModel->normals[pModel->numnormals].z );

					printf( "Reading normals: %f %f %f\n", pModel->normals[pModel->numnormals].x,
						pModel->normals[pModel->numnormals].y, pModel->normals[pModel->numnormals].z );

					pModel->numnormals++;
				} break;
				case 'f': {
					pModel->faces = (lmf_face_t*)realloc( pModel->faces, (pModel->numfaces+1)*sizeof(lmf_face_t) );
					sscanf( pLine, "f %i %i %i",
						&pModel->faces[pModel->numfaces].vertexes[0],
						&pModel->faces[pModel->numfaces].vertexes[1],
						&pModel->faces[pModel->numfaces].vertexes[2] );

					pModel->numfaces++;
				} break;
			}
			memset( pLine, 0, 128 );
			bPosition = 0;
			continue;
		}

		pLine[bPosition] = bByte;
		bPosition++;
	}

	fclose( pLMFFile );

	pModel->colors = (BYTE*)malloc( pModel->numfaces*3 );
	for( INT i = 0; i < pModel->numfaces; i+=3 ) {
		srand( time(NULL) );
		pModel->colors[i] = rand()%255;
		pModel->colors[i+1] = rand()%255;
		pModel->colors[i+2] = rand()%255;
	}

	pModel->type = EFFORT_OBJ;

	printf( "\n--------------------\n" );
	printf( "Model finished with %i vertexes\n\n", pModel->numvertexes );

	pModelFile = pModel;
}

/***************************************************/
/*					  RENDERER					   */
/***************************************************/

void R_BringAboutGL( HDC hdc ) {
	PIXELFORMATDESCRIPTOR pPixelFmt;
	INT iPixelFmt;

	memset( &pPixelFmt, 0, sizeof(pPixelFmt) );

	pPixelFmt.nSize = sizeof( PIXELFORMATDESCRIPTOR );
	pPixelFmt.nVersion = 1;
	pPixelFmt.dwFlags = ( PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER );
	pPixelFmt.iPixelType = PFD_TYPE_RGBA;
	pPixelFmt.cColorBits = 16;
	pPixelFmt.cDepthBits = 32;
	pPixelFmt.iLayerType = PFD_MAIN_PLANE;

	____hdc = hdc;
	iPixelFmt = ChoosePixelFormat( hdc, &pPixelFmt );
	SetPixelFormat( hdc, iPixelFmt, &pPixelFmt );
	hGLRenderContext = wglCreateContext( hdc );
	if( !hGLRenderContext ) {
		MessageBoxA( NULL, "wglCreateContext failed", "Error", MB_ICONERROR );
		exit( -1 );
	}
	if( !wglMakeCurrent(hdc, hGLRenderContext) ) {
		MessageBoxA( NULL, "wglMakeCurrent failed", "Error", MB_ICONERROR );
		exit( -1 );
	}
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();
	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	glOrtho( 0, 640, 480, 0, -1000, 1000 );
	glViewport( 0, 0, 640, 480 );

	glClearColor( 1, 1, 1, 1 );

	R_DrawModel( pModelFile );
}

void R_DrawModel( lmf_file_t* mdl ) {
	INT iDrawMode, bFlipVertical;
	lmf_vec_t min, max;

	iDrawMode = GL_LINE;
	bFlipVertical = FALSE;
	
	/*************************
		Center the model
	*************************/
	memset( &min, 0, sizeof(lmf_vec_t) ); memset( &max, 0, sizeof(lmf_vec_t) );
	for( INT i = 0; i < mdl->numvertexes; i++ ) {
		if( mdl->vertexes[i].x < min.x ) min.x = mdl->vertexes[i].x;
		else if( mdl->vertexes[i].x > max.x ) max.x = mdl->vertexes[i].x;

		if( mdl->vertexes[i].y < min.y ) min.y = mdl->vertexes[i].y;
		else if( mdl->vertexes[i].y > max.y ) max.y = mdl->vertexes[i].y;

		if( mdl->vertexes[i].z < min.z ) min.z = mdl->vertexes[i].z;
		else if( mdl->vertexes[i].z > max.z ) max.z = mdl->vertexes[i].z;
	}
	glTranslatef( (-(max.x-min.x)/2.0f)+(640/2), (-(max.y-min.y)/2.0f)+(480/2), 2 );

	while( TRUE ) {
		SleepEx( 200, FALSE );
		// Controls
		if( GetAsyncKeyState(65) ) {
			glRotatef( 10, 0, 1, 0 );
		}
		else if( GetAsyncKeyState(68) ) {
			glRotatef( -10, 0, 1, 0 );
		}
		else if( GetAsyncKeyState(87) ) {
			glScalef( 1.5, 1.5, 0 );
		}
		else if( GetAsyncKeyState(83) ) {
			glScalef( 0.5, 0.5, 0 );
		}
		else if( GetAsyncKeyState(VK_LEFT) ) {
			glTranslatef( -8, 0, 0 );
		}
		else if( GetAsyncKeyState(VK_RIGHT) ) {
			glTranslatef( 8, 0, 0 );
		}
		else if( GetAsyncKeyState(VK_UP) ) {
			glTranslatef( 0, 8, 0 );
		}
		else if( GetAsyncKeyState(VK_DOWN) ) {
			glTranslatef( 0, -8, 0 );
		}
		else if( GetAsyncKeyState('R') ) {
			if( iDrawMode == GL_LINE ) iDrawMode = GL_FILL;
			else iDrawMode = GL_LINE;
		}
		else if( GetAsyncKeyState(VK_BACK) ) {
			bFlipVertical = !bFlipVertical;
		}
		else if( GetAsyncKeyState('Q') ) {
			exit( 1 );
		}

		glClear( GL_COLOR_BUFFER_BIT );
		if( iDrawMode == GL_LINE ) glColor3f( 0, 0, 1 );
		glPolygonMode( GL_FRONT_AND_BACK, iDrawMode );
		glBegin( GL_TRIANGLE_STRIP );

		if( mdl->type == EFFORT_OBJ ) {
			for( INT f = 0; f < mdl->numfaces; f++ ) {
				if( iDrawMode != GL_LINE ) {
					glColor3b( mdl->colors[f], mdl->colors[f+1], mdl->colors[f+2] );
				} // this actually messes up the color array alignment because of f++ but final result is the same
				// and whether the colors are TRULY the ones we intended don't really matter, they're still random and don't change upon redraw anyway
				for( INT i = 0; i < 3; i++ ) {
					glVertex3f( mdl->vertexes[ mdl->faces[f].vertexes[i]-1 ].x,
						bFlipVertical ?
						-mdl->vertexes[ mdl->faces[f].vertexes[i]-1 ].y :
						mdl->vertexes[ mdl->faces[f].vertexes[i]-1 ].y,
						mdl->vertexes[ mdl->faces[f].vertexes[i]-1 ].z );
				}
			}
		}
		else {
			for( INT f = 0; f < mdl->numvertexes; f++ ) {
				if( iDrawMode != GL_LINE ) {
					glColor3b( mdl->colors[f], mdl->colors[f+1], mdl->colors[f+2] );
				}

				glVertex3f( mdl->vertexes[f].x,
					bFlipVertical ?
					-mdl->vertexes[f].y :
					mdl->vertexes[f].y,
					mdl->vertexes[f].z );
			}
		}
		glEnd();

		SwapBuffers( ____hdc );
	}
}