#include "../headers/Settings.h"
#include "../headers/Input.h"
#include "../headers/main.h"
#include "../headers/PhysXWrapper.h"
#include "../headers/MInterpolate.h"
#include "../headers/MVector3.h"
#include <math.h>

bool Input::mouseactive = false;
GeCamera* Input::cam = NULL;

void Input::init(GeCamera* cam) {
	Input::cam = cam;
	
	glfwSetKeyCallback(callbackKey); 
	glfwDisable(GLFW_MOUSE_CURSOR);
	glfwSetMousePosCallback(callbackMousePos);
	glfwSetMouseButtonCallback(callbackMouseButton);
}

void Input::resetMousePos() {
	Input::mouseactive = false;
	glfwSetMousePos(settings.halfWidth,settings.halfHeight);	
}

void Input::release(void) {
	glfwEnable(GLFW_MOUSE_CURSOR);
	glfwSetKeyCallback(NULL); 
	glfwSetMousePosCallback(NULL);
	glfwSetMouseButtonCallback(NULL);
}

void Input::callbackMousePos(int x, int y) {
	
	static int mox=x, moy=y;

	if(Input::mouseactive) {
		cam->rotateStep((mox-x)/400.0f, (moy-y)/400.0f);
		
		mox = x;
		moy = y;

		Input::mouseactive = false;
		if(abs((int)(x - settings.halfWidth)) > 40 || abs((int)(y - settings.halfHeight)) > 40) {
			mox = settings.halfWidth;
			moy = settings.halfHeight;
			glfwSetMousePos(mox,moy);
		}
		Input::mouseactive = true;
	} else {
		mox = x;
		moy = y;
		Input::mouseactive = true;
	}
	
}

void Input::callbackMouseButton(int button, int action) {
	switch (button) {
		 case GLFW_MOUSE_BUTTON_LEFT:
			character->autoCamera = !character->autoCamera;
			//if (action == GLFW_PRESS) {
			//}
			break;


		 
		 default:
			break;
	}
	
}


void Input::callbackKey(int key, int action) {
	
	switch (key) {
		case GLFW_KEY_F1: // View Help
			if (action == GLFW_PRESS) {
				//update_main(0.020);
			}
		break;
		
		case GLFW_KEY_F2: // FPS Display
			if (action == GLFW_PRESS) {
				hudFps->toggleVisibility();
				if(hudFps->isVisible()) {				
					hudInfoText->setFadOutText("Show frames per second");
				} else {
					hudInfoText->setFadOutText("Hide frames per second");
				}
			}
		break;
		
		case GLFW_KEY_F3: //Wire Frame on/off
			if (action == GLFW_PRESS) {
				if (settings.flags & FLAG_WIREFRAME) {
					settings.flags &= ~FLAG_WIREFRAME;
					glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
					hudInfoText->setFadOutText("Wire Frame off");
				}
				else {
					settings.flags |= FLAG_WIREFRAME;
					glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
					hudInfoText->setFadOutText("Wire Frame on");
				}
			}
		break;
		
		case GLFW_KEY_F4: // Texture Quality: Nearest Neighbour(false)/Bilinear(true)
			if (action == GLFW_PRESS) {
				if (settings.flags & FLAG_TEXTUREQUALITY_HIGH) {
					settings.flags &= ~FLAG_TEXTUREQUALITY_HIGH;
					hudInfoText->setFadOutText("Texture Quality: Nearest Neighbour");
				} else {
					settings.flags |= FLAG_TEXTUREQUALITY_HIGH;
					hudInfoText->setFadOutText("Texture Quality: Bilinear");
				}
			}
		break;
		
		case GLFW_KEY_F5: // Mipmapping Quality: Off(0)/Nearest Neighbour(1)/Linear(2)
			if (action == GLFW_PRESS) {
				settings.mipmapMode = (settings.mipmapMode+1)%3;
				if(settings.mipmapMode == 0) {
					hudInfoText->setFadOutText("Mipmapping Quality: Off");
				} else if(settings.mipmapMode == 1) {
					hudInfoText->setFadOutText("Mipmapping Quality: Nearest Neighbour");
				} else {
					hudInfoText->setFadOutText("Mipmapping Quality: Linear");
				}
			}
		break;
		
		case GLFW_KEY_F6: // Mode: Immediate(0)/Vertex Arrays(1)/VBO(2)
			if (action == GLFW_PRESS) {
				settings.renderMode = (settings.renderMode+1)%3;
				if(settings.renderMode == 0) {
					hudInfoText->setFadOutText("Mode: Immediate");
				} else if(settings.renderMode == 1) {
					hudInfoText->setFadOutText("Mode: Vertex Arrays");
				} else {
					hudInfoText->setFadOutText("Mode: VBO");
				}
			}
		break;
		
		case GLFW_KEY_F7: // Display Lists On/Off			
			if (action == GLFW_PRESS) {
				if (settings.flags & FLAG_USE_DISPLAY_LISTS) {
					settings.flags &= ~FLAG_USE_DISPLAY_LISTS;
					hudInfoText->setFadOutText("Display Lists Off");
				} else {
					settings.flags |= FLAG_USE_DISPLAY_LISTS; 
					hudInfoText->setFadOutText("Display Lists On");
				}
			}
		break;
		
		case GLFW_KEY_F8: // View Frustum Culling On/Off
			if(action == GLFW_PRESS) {
				if (settings.flags & FLAG_VIEWFRUSTUM_CULLING) {
					settings.flags &= ~FLAG_VIEWFRUSTUM_CULLING;
					hudInfoText->setFadOutText("View Frustum Culling Off");
				} else {
					settings.flags |= FLAG_VIEWFRUSTUM_CULLING; 
					hudInfoText->setFadOutText("View Frustum Culling On");
				}
			}
		break;

		case GLFW_KEY_F9: // Transparency On/Off
			if(action == GLFW_PRESS) {
				if (settings.flags & FLAG_TRANSPARENCY) {
					settings.flags &= ~FLAG_TRANSPARENCY;
					glDisable(GL_BLEND);
					hudInfoText->setFadOutText("Alpha Blending Off");
				} else {
					settings.flags |= FLAG_TRANSPARENCY; 
					glEnable(GL_BLEND);
					hudInfoText->setFadOutText("Alpha Blending On");
				}
			}
		break;

		case GLFW_KEY_F10: // Debug Render On/Off
			if(action == GLFW_PRESS) {
				if(gDebugRenderer.visible) {
					gDebugRenderer.visible = false;
					hudInfoText->setFadOutText("Debug Renderer Off");
				} else {
					gDebugRenderer.visible = true;
					hudInfoText->setFadOutText("Debug Renderer On");
				}
			}
		break;

		case GLFW_KEY_F11: // Shader Render On/Off
			if(action == GLFW_PRESS) {
				if(settings.flags & FLAG_USE_CUSTOM_SHADER) {	
					settings.flags &= ~FLAG_USE_CUSTOM_SHADER;
					hudInfoText->setFadOutText("Use Custom Shader Off");
				} else {
					settings.flags |= FLAG_USE_CUSTOM_SHADER; 
					hudInfoText->setFadOutText("Use Custom Shader On");
				}
			}
		break;

		case GLFW_KEY_F12: // Volume Shadows On/Off
			if(action == GLFW_PRESS) {
				if(settings.flags & FLAG_VOLUME_SHADOWS) {
					settings.flags &= ~FLAG_VOLUME_SHADOWS;
					hudInfoText->setFadOutText("Volume Shadows Off");
					
					GeLight::setGlobalAmbient(0.05f,0.05f,0.05f);
				} else {
					settings.flags |= FLAG_VOLUME_SHADOWS;
					hudInfoText->setFadOutText("Volume Shadows On");
					// Disable all lights
					std::list<GeLight*>::iterator iter = lights.begin();
					for(;iter != lights.end(); iter++)
					{	
						if((*iter)->isActive()) {
							(*iter)->hide();
						}
					}
					std::list<GeNode*>::iterator iterNodes = objects.begin();
					for(;iterNodes != objects.end(); iterNodes++)
					{
						// TODO: Branches alle extra durch iterieren??
						//((GeMesh*)*iterNodes)->setOutOfDate();
					}
				}
			}
		break;

		case GLFW_KEY_SPACE: // jump
			if (action == GLFW_PRESS) {
				character->jump();
			}
		break;
		
		case GLFW_KEY_ESC: // Quit
			if(action == GLFW_PRESS) {
				closeGame();
			}
		break;

		case GLFW_KEY_LSHIFT:
			if(action == GLFW_PRESS) {
				character->autoCamera = !character->autoCamera;
			}
		break; 

		default:
		break;
	}	
}

/*
#define ROTATIONSPEED 0.05f
#define MOVESPEED 30.0f
#define ZOOMSPEED 4.0f

extern Character* tire;

int Input::width, Input::height, Input::hwidth, Input::hheight, Input::crotx, Input::croty, Input::crotxunit, Input::crotyunit;
GLfloat Input::mov, Input::trot;
bool Input::mouseactive, Input::firstMove, Input::zoom;
GeCamera* Input::cam;
bool Input::showfps, Input::wireframe, Input::textureQuality, Input::displayLists, Input::vfCulling, Input::transparency;
unsigned char Input::mipmapQuality, Input::mode;

void Input :: setup (GeCamera* cam) {
	Input::cam = cam;
	mouseactive = false;
	firstMove = false;
	zoom = false;
	
	showfps = false;
	textureQuality = true; // Linear
	displayLists = true;
	vfCulling = true;
	transparency = true;
	mipmapQuality = 1; // Nearest Neighbour
	mode = 0; // VBO
	
	crotx = croty = 0;
	crotxunit = crotyunit = 20;
	mov=0.0f;
	glutIgnoreKeyRepeat(1);
	glutSpecialFunc(specialDown);
	glutKeyboardFunc(keyboardDown);
	glutKeyboardUpFunc(keyboardUp);
	glutReshapeFunc(reshape);
	glutMouseFunc(mouseButton);
	glutMotionFunc(mouseMotion);
	glutPassiveMotionFunc(pMouseMotion);
}

void Input :: reshape (int w, int h) {
	width = w;
	height = h;
	hwidth = width >> 1;
	hheight = height >> 1;
	
	glViewport(0, 0, w, h);
	
	mouseactive=false;
	glutWarpPointer(hwidth,hheight);
	mouseactive=true;
	
	cam->setupProjection(35.0f, ((float)w)/h);
}

void Input :: mouseButton(int button, int state, int x, int y) {
	switch (button) {
		 case GLUT_RIGHT_BUTTON:
			if (state ==  GLUT_DOWN) {
				zoom=true;
			}
			else {
				zoom=false;
			}
		 break;
		 
		 default:
		 break;
	}
}

void Input :: mouseMotion (int x, int y) {
	static int mox=x, moy=y;

	if (mouseactive)
	{
		if (zoom) {
			cam->zoom((y-moy)*ZOOMSPEED);
		}
		
		mox = x;
		moy = y;
		
		mouseactive = false;
		if(mabs((x-hwidth)) > 40 || mabs((y-hheight)) > 40) {
			mox = hwidth;
			moy = hheight;
			glutWarpPointer(hwidth,hheight);
		}
		mouseactive = true;
	}
}

void Input :: pMouseMotion (int x, int y) {
	static int mox=x, moy=y;

	if (mouseactive)
	{
		cam->rotateX((mox-x)/360.0f);
		cam->rotateY((y-moy)/360.0f);
		
		mox = x;
		moy = y;

		mouseactive = false;
		if(mabs((x-hwidth)) > 40 || mabs((y-hheight)) > 40) {
			mox = hwidth;
			moy = hheight;
			glutWarpPointer(hwidth,hheight);
		}
		mouseactive = true;
	}
	
}

void Input :: specialDown (int key, int x, int y) {
	switch (key) {
		case GLUT_KEY_F10: // Bounding Box/Sphere Display
			GeObject::drawBounding = !GeObject::drawBounding;
		break;
		
		case GLUT_KEY_F2: // FPS Display
			showfps = !showfps;
		break;
		
		case GLUT_KEY_F3: // Wire Frame on/off
			wireframe = !wireframe;
			if (wireframe)
				glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
			else
				glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
		break;
		
		case GLUT_KEY_F4: // Texture Quality: Nearest Neighbour(false)/Bilinear(true)
			textureQuality = !textureQuality;
		break;
		
		case GLUT_KEY_F5: // Mipmapping Quality: Off(0)/Nearest Neighbour(1)/Linear(2)
			mipmapQuality = (mipmapQuality+1)%3;
		break;
		
		case GLUT_KEY_F6: // Mode: VBO(0)/Vertex Arrays(1)/Immediate(2) ?
			mode = (mode+1)%3;
		break;
		
		case GLUT_KEY_F7: // Display Lists On/Off
			displayLists = !displayLists;
		break;
		
		case GLUT_KEY_F8: // View Frustum Culling On/Off
			vfCulling = !vfCulling;
		break;
		
		case GLUT_KEY_F9: // Transparency On/Off
			transparency = !transparency;
			if (transparency)
				glEnable(GL_BLEND);
			else
				glDisable(GL_BLEND);
		break;
		
		default:
		break;
	}
}
void Input :: keyboardDown (unsigned char key, int x, int y) {
	switch (key) {
		case 'w':
		case 'W':
			firstMove = true;
			mov += MOVESPEED;
			cam->setForwardVector(tire->vMotion);
		break;
		
		case 'a':
		case 'A':
			firstMove = true;
			trot += ROTATIONSPEED;
		break;
		
		case 's':
		case 'S':
			firstMove = true;
			mov -= MOVESPEED;
			cam->setForwardVector(tire->vMotion);
		break;
		
		case 'd':
		case 'D':
			firstMove = true;
			trot -= ROTATIONSPEED;
		break;
		
		case 27:  /*  Escape Key */ /*
			glutLeaveGameMode();
			exit(0);
		break;
		
		default: break;
	}
}

void Input :: keyboardUp (unsigned char key, int x, int y) {
	switch (key) {
		case 'w':
		case 'W':
			mov -= MOVESPEED;
		break;
		
		case 'a':
		case 'A':
			trot -= ROTATIONSPEED;
		break;
		
		case 's':
		case 'S':
			mov += MOVESPEED;
		break;
		
		case 'd':
		case 'D':
			trot += ROTATIONSPEED;
		break;
		
		case 27:  /*  Escape Key */ /*
			delete(tire);
			glutLeaveGameMode();
			exit(0);
		break;
		
		default: break;
    }

}

void Input::move() {
	if (trot != 0.0f) {
		tire->rotate(trot);
		if (mov != 0.0f) {
			cam->setForwardVector(tire->vMotion);
		}
	}
	
	if (mov != 0.0f) {
		tire->fSpeed = mov;
	}
	else tire->fSpeed = 0;
}
*/
