// BaseGL.cpp: implementation of the BaseGL class.

#include "BaseGL.h"
#include "Tetobject.h"
#include "Water.h"
#include "Environement.h"

Tetobject *model;								//our tetobj
															//needed graphical representation

Water *water;
Environement *environement;


	bool		BaseGL::m_keys[256];
	int		BaseGL::m_lastX;
	int		BaseGL::m_lastY;
	float		BaseGL::m_farPlane;
	float		BaseGL::m_nearPlane;
	float		BaseGL::m_fov;
	bool		BaseGL::m_fullScrn;
	int		BaseGL::m_height;
	int		BaseGL::m_width;
	int		BaseGL::m_bits;

	
//////////////////
// WndProc
/////////////////

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

		case WM_SYSCOMMAND:			
		{
			switch (wParam)				
			{
				case SC_SCREENSAVE:	
				case SC_MONITORPOWER:
				return 0;				
			}
			break;						
		}

		case WM_CLOSE:			
		{
			PostQuitMessage(0);
			return 0;	
		}

		case WM_SIZE:
		{
			return 0;	
		}
	}

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

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

BaseGL::BaseGL(int w, int h, int b)
{
	m_hDC=NULL;			// Private GDI Device Context
	m_hRC=NULL;			// Permanent Rendering Context
	m_hWnd=NULL;		// Holds Our Window Handle

	m_lastX = 0;
	m_lastY = 0;
	m_fov = 60.0;
	m_nearPlane	= 1.0f;
	m_farPlane	= 2000.0f;
	m_width = w;
	m_height = h;
	m_bits = b;
	m_fullScrn = false;
	m_finished = false;
	m_argc = 0;
	rendermesh = true;
}

BaseGL::BaseGL()
{
	m_hDC=NULL;
	m_hRC=NULL;
	m_hWnd=NULL;

	m_lastX = 0;
	m_lastY = 0;
	m_fov = 60.0;
	m_nearPlane	=  0.1f;
	m_farPlane	=  4000;
	m_width = 800;
	m_height = 600;
	m_bits = 16;
	m_fullScrn = false;
	m_finished = false;
	m_argc = 0;
	rendermesh = true;
}

BaseGL::~BaseGL()
{
	KillWindow();
}

bool BaseGL::Init(int argc, char **argv)
{
	const GLubyte *vendor,*renderer,*version,*extensions;

	m_argc = argc;
	m_argv = argv;

	//Create the window
	BuildWindow();	
	InitGL();
	CreateMouseInterface();
	m_camera.Init();
	Loading();
	
	vendor = glGetString(GL_VENDOR);
	renderer = glGetString(GL_RENDERER);
	version = glGetString(GL_VERSION);
	extensions = glGetString(GL_EXTENSIONS);

	return true;
}

void BaseGL::Reshape(int w, int h)
{
	fprintf(stderr, "new size = %d x %d\n", w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective( m_fov, (double)w/(double)h, m_nearPlane, m_farPlane);
	glMatrixMode(GL_MODELVIEW);
	glViewport(0,0,w,h);
}

void BaseGL::MainLoop()
{
	while(!m_finished)
	{
		if (PeekMessage(&m_msg,NULL,0,0,PM_NOREMOVE))
		{
			GetMessage(&m_msg,NULL,0,0);
			
			if (m_msg.message==WM_QUIT)	
			{
				m_finished=true;					
			}
			if (m_msg.message==WM_KEYDOWN)	
			{
				m_keys[m_msg.wParam] = true;	
			}
			if (m_msg.message==WM_KEYUP)		
			{
				m_keys[m_msg.wParam] = false;	
			}
			else								
			{
				TranslateMessage(&m_msg);			
				DispatchMessage(&m_msg);			
			}
		}
		else									
		{
			ProceedKeys();
			ProceedMouse();
			Drawing();
			SwapBuffers(m_hDC);					
		}
	}
	fflush(stdout);
	KillWindow();
	exit(0);
}

void BaseGL::InitGL()
{
	glEnable(GL_TEXTURE_2D);						
	glClearColor(0.3f, 0.3f, 1.0f, 0.5f);
	glClearDepth(1.0f);									
	glEnable(GL_DEPTH_TEST);							
	glDepthFunc(GL_LEQUAL);								
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);	

	glEnable(GL_LIGHTING);  
	glEnable(GL_NORMALIZE);  
	glShadeModel(GL_SMOOTH);							

	float amb[] = { 0.6,0.6f,0.6f,1};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb);

	glViewport(0,0,m_width,m_height);						
	glMatrixMode(GL_PROJECTION);						
	glLoadIdentity();									
	gluPerspective(m_fov,(GLfloat)m_width/(GLfloat)m_height,m_nearPlane,m_farPlane);
	glMatrixMode(GL_MODELVIEW);							
	glLoadIdentity();									
}


void BaseGL::BuildWindow()
{
	GLuint		PixelFormat;
	WNDCLASS	wc;
	DWORD		dwExStyle;
	DWORD		dwStyle;
	m_hInstance			= GetModuleHandle(NULL);
	wc.style			= CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
	wc.lpfnWndProc		= (WNDPROC) WndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= m_hInstance;
	wc.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	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);
	
	if (m_fullScrn)
	{
		DEVMODE dmScreenSettings;
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);	
		dmScreenSettings.dmPelsWidth	= m_width;	
		dmScreenSettings.dmPelsHeight	= m_height;
		dmScreenSettings.dmBitsPerPel	= m_bits;	
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

		if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
		{
			if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
			{
				m_fullScrn=false;
			}
			else
			{
				MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
			}
		}
	}

	if (m_fullScrn)
	{
		dwExStyle=WS_EX_APPWINDOW;
		dwStyle=WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
		ShowCursor(false);
	}
	else
	{
		dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		dwStyle=WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
	}

	if (!(m_hWnd=CreateWindowEx(dwExStyle,
								wc.lpszClassName,
								"OpenGL",		
								dwStyle,		
								0, 0,				
								m_width, m_height,
								NULL,				
								NULL,				
								m_hInstance,
								NULL)))
	{
		KillWindow();
		MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	static	PIXELFORMATDESCRIPTOR pfd=
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,														// Version Number
		PFD_DRAW_TO_WINDOW |					// Format Must Support Window
		PFD_SUPPORT_OPENGL |					// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		m_bits,												// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,														// No Alpha Buffer
		0,														// Shift Bit Ignored
		0,														// No Accumulation Buffer
		0, 0, 0, 0,										// Accumulation Bits Ignored
		16,														// 16Bit Z-Buffer (Depth Buffer)  
		0,														// No Stencil Buffer
		0,														// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,														// Reserved
		0, 0, 0												// Layer Masks Ignored
	};
	
	if (!(m_hDC=GetDC(m_hWnd)))
	{
		KillWindow();
		MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	if (!(PixelFormat=ChoosePixelFormat(m_hDC,&pfd)))
	{
		KillWindow();
		MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	if(!SetPixelFormat(m_hDC,PixelFormat,&pfd))
	{
		KillWindow();
		MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	if (!(m_hRC=wglCreateContext(m_hDC)))
	{
		KillWindow();
		MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	if(!wglMakeCurrent(m_hDC,m_hRC))
	{
		KillWindow();
		MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
	}

	ShowWindow(m_hWnd, m_argc);
	SetForegroundWindow(m_hWnd);
	SetFocus(m_hWnd);
}

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

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

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

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

	if (m_fullScrn)
	{
		ChangeDisplaySettings(NULL,0);
		ShowCursor(true);
	}

}

void BaseGL::CreateMouseInterface()
{
	m_Mouse.SetUp(m_hInstance, m_hWnd);
	m_Mouse.Init();
}

bool LoadConfigFile(char* filename, Tetobject* model) {
	// Read the file and store data in local variables
	FILE *f = fopen(filename, "r");
	if (!f) return false;
	const int STRING_LEN(256);
	char s[STRING_LEN];
	char tetfilename[STRING_LEN];
	char objfilename[STRING_LEN];
	int points[8];		// 8 points for steering
	float stiffness;
	float mass;

	// Check version
	fgets(s, STRING_LEN, f);		// get STRING_LEN chars, max. one line
	if (strncmp(s, "version 0.1", 11)) {
		fclose(f);
		return false;
	}
	fgets(s, STRING_LEN, f);		// get tet-filename
	if (sscanf(s, "tetfile %s", &tetfilename) != 1) {
		fclose(f);
		return false;
	}
	fgets(s, STRING_LEN, f);		// get obj-filename
	if (sscanf(s, "objfile %s", &objfilename) != 1) {
		fclose(f);
		return false;
	}
	fgets(s, STRING_LEN, f);		// get indices of the 8 points used for steering
	if (sscanf(s, "points %i %i %i %i %i %i %i %i",
		&points[0], &points[1], &points[2], &points[3],
		&points[4], &points[5], &points[6], &points[7]) != 8) {
		fclose(f);
		return false;
	}
	fgets(s, STRING_LEN, f);		// get standard stiffness for springs
	if (sscanf(s, "stiffness %f", &stiffness) != 1) {
		fclose(f);
		return false;
	}
	fgets(s, STRING_LEN, f);		// get standard mass for masspoints
	if (sscanf(s, "mass %f", &mass) != 1) {
		fclose(f);
		return false;
	}

	// Load TET-file and OBJ-file
	printf("Loading TET-File: %d \n\n", model->LoadTetraFile(tetfilename, stiffness, mass));
	printf("Loading OBJ-File: %d\n\n", model->mesh.LoadMesh(objfilename));

	// Get the quader vertices and store them in the Driver
	Vertex* quader[8];
	for (int i=0; i<8; i++) {
		quader[i] = model->vertexArray[points[i]];
	}
	model->driver->Setquader(quader);		// set the quader points (copy them)

	fclose(f);
	return true;
}

void BaseGL::Loading()
{
	//LIGHT
	light_pos[0]=-10;light_pos[1]=5;light_pos[2]=2;light_pos[3]=0;
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);  
	
	//MODEL
	model = new Tetobject();
	LoadConfigFile("config.txt", model);	// read config file and store the data in driver and model
	model->Join();
	m_camera.Follow(model);				// follow the model

	water = new Water();
	float len = 0.5f * SbVec3f((model->driver->quader[2]->pos - model->driver->quader[3]->pos)).length();

	water->Init(100,100,4.0f,4.0f,len);

}

void BaseGL::Drawing()
{
 	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
	m_camera.Set();
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);  

	model->UpdatePhysics(0.05f);
	rendermesh ? model->UpdateGraphics() : model->RenderTetras();
	water->Update(0.03f);
	water->Render();
	//environement->Render();
}

void BaseGL::ProceedMouse()
{
	m_Mouse.Refresh();
	m_camera.TurnHorizontal(m_Mouse.m_X);
	m_camera.TurnVertical(m_Mouse.m_Y);	
}

void BaseGL::ProceedKeys()
{
	static DEVMODE	dmScreenSettings;
	static bool		done = false;
	static DWORD	dwExStyle;
	static DWORD	dwStyle;
	if (!done)
	{
		memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
		dmScreenSettings.dmSize=sizeof(dmScreenSettings);
		dmScreenSettings.dmPelsWidth	= m_width;
		dmScreenSettings.dmPelsHeight	= m_height;
		dmScreenSettings.dmBitsPerPel	= m_bits;
		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
		done = true;
	}
	
	if (m_keys['t'] || m_keys['T'])
	{
		m_fullScrn = !m_fullScrn;
		if(m_fullScrn)
		{
			dwExStyle=WS_EX_APPWINDOW | WS_EX_TOPMOST;
			dwStyle=WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
			ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN);
			SetWindowLong(m_hWnd,GWL_EXSTYLE,dwExStyle);
			SetWindowLong(m_hWnd,GWL_STYLE,dwStyle);
			ShowWindow(m_hWnd,m_argc);
			ShowCursor(false);
		}
		else
		{
			dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
			dwStyle=WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE;
			SetWindowLong(m_hWnd,GWL_EXSTYLE,dwExStyle);
			SetWindowLong(m_hWnd,GWL_STYLE,dwStyle);
			ChangeDisplaySettings(NULL,0);
			ShowWindow(m_hWnd,m_argc);
			ShowCursor(true);
		}
		m_keys['t'] = false;
		m_keys['T'] = false;
	}
	if (m_keys[27])
	{
		m_finished = true;
	}
	
	if (m_keys[VK_UP])
	{
		model->driver->Accelerate();
	}
	if (m_keys[VK_DOWN])
	{
		model->driver->Brake();
	}
	if (m_keys[VK_LEFT])
	{
		model->driver->Left();
	}
	if (m_keys[VK_RIGHT])
	{
		model->driver->Right();
	}
	if (!m_keys[VK_UP] && !m_keys[VK_DOWN])	// Damp when not accelerating
	{
		model->driver->DampForward();
	}
	if (!m_keys[VK_LEFT] && !m_keys[VK_RIGHT])	// Damp when not steering left or right
	{
		model->driver->DampSideward();
	}
	if (m_keys[87])  //w
	{
		m_camera.MoveForward();
	}
	if (m_keys[83]) //s
	{
		m_camera.MoveBackward();
	}
	if (m_keys[65]) //a
	{
		m_camera.MoveLeft();
	}
	if (m_keys[68]) //d
	{
		m_camera.MoveRight();
	}
	if (m_keys[77]) //m
	{
		m_keys[77] = false;
		rendermesh = !rendermesh;
		rendermesh ? glEnable(GL_LIGHTING) : glDisable(GL_LIGHTING);
	}
	if (m_keys[67]) //c cameramode
	{
		m_camera.NextCameraMode();
	}
	if (m_keys['z'] || m_keys['Z'])
	{
		float h[] = {30,2,30};
		water->EmbossSphere(h);
	}
	if (m_keys['b'] || m_keys['B'])
	{
		model->BlowUp();
	}
}
