#include "Player.h"
#include "Camera.h"
#include "Input.h"
#include "Ship.h"
#include "Material.h"
#include "math.h"
#include <iostream>
#include "AABoundingBox.h"
#include "TextureObject.h"
#include "Billboard.h"

#define PI_OVER_2 1.57079633
#define PI 3.14159265

Player::Player(Camera* cam, Input* keyboard)
{
	this->cam = cam;
	this->keyboard = keyboard;

	this->spawned = false;
	this->controled = false;

	ship = new Ship();

	texture = new TextureObject("Textures/fighter.png");
	flame = new Billboard("Textures/flame.png");
	flame_bottom = new Billboard("Textures/flameBottom.png");

	shipMaterial = new Material(Vec4f(0.2, 0.2, 0.2),  Vec4f(0.5, 0.5, 0.5), Vec4f(0.5, 0.5, 0.5), 128);

	velocity = Vec3f(0,0,MIN_SPEED);

	flipAngle = 0;
}
//-------------------------------------------------------------------------
Player::~Player()
{
	delete ship;
	delete shipMaterial;
}
//-------------------------------------------------------------------------
void Player::startControl()
{
	controled = true;
}
//-------------------------------------------------------------------------
void Player::stopControl()
{
	controled = false;
}
bool Player::isControled()
{
	return controled;
}
//-------------------------------------------------------------------------
AABoundBox* Player::getBoundingVolume()
{
	return ship->getBoundingBox();
}
//-------------------------------------------------------------------------
void Player::spawn(Vec3f position, Vec3f direction, Vec3f upDir)
{
	this->position = position;
	this->direction = direction;
	this->upDir = upDir;
	spawned = true;

	ship->getBoundingBox()->setCenter(position);
	ship->getBoundingBox()->scale(0.8, 0.5, 0.8);
}
//-------------------------------------------------------------------------
float Player::getSpeed()
{
	return velocity.z;
}//-------------------------------------------------------------------------
void Player::setSpeed(float newSpeed)
{
	this->velocity.z = newSpeed;
}
//-------------------------------------------------------------------------
Vec3f Player::getPosition()
{
	return position;
}
//-------------------------------------------------------------------------
void Player::update()
{
	if(!controled)
		return;

	#pragma region Movements
	// Move vertical
	if(keyboard->getKey('w'))
	{
		if( position.y >= MAX_MOVE_UP || position.y <= -MAX_MOVE_DOWN)
			velocity.y = 0;

		if( velocity.y > 0)
			velocity.y -= BREAK_FACTOR * ACCELERATION_VERTICAL;

		if(abs(velocity.y) < MAX_SPEED_VERTICAL)
			velocity.y -= ACCELERATION_VERTICAL;
	} 

	if(keyboard->getKey('s'))
	{
		if( position.y >= MAX_MOVE_UP || position.y <= -MAX_MOVE_DOWN)
			velocity.y = 0;

		if( velocity.y < 0)
			velocity.y += BREAK_FACTOR * ACCELERATION_VERTICAL;

		if(abs(velocity.y) < MAX_SPEED_VERTICAL)
			velocity.y += ACCELERATION_VERTICAL;
	}

	if(!keyboard->getKey('w') && !keyboard->getKey('s'))
	{
		if( abs(velocity.y) < 1.2 * BREAK_FACTOR * ACCELERATION_VERTICAL)
			velocity.y = 0;
		else if(velocity.y > 0)
			velocity.y -= BREAK_FACTOR * ACCELERATION_VERTICAL;
		else
			velocity.y += BREAK_FACTOR * ACCELERATION_VERTICAL;
	}

	// Move horizontal
	if(keyboard->getKey('a'))
	{
		if( abs(position.x) >= MAX_MOVE_HORIZONTAL)
			velocity.x = 0;

		if( velocity.x > 0)
			velocity.x -= BREAK_FACTOR * ACCELERATION_HORIZONTAL;

		if(abs(velocity.x) < MAX_SPEED_HORIZONTAL)
			velocity.x -= ACCELERATION_HORIZONTAL;
	} 

	if(keyboard->getKey('d'))
	{
		if( abs(position.x) >= MAX_MOVE_HORIZONTAL)
			velocity.x = 0;

		if( velocity.x < 0)
			velocity.x += BREAK_FACTOR * ACCELERATION_HORIZONTAL;

		if(abs(velocity.x) < MAX_SPEED_HORIZONTAL)
		velocity.x += ACCELERATION_HORIZONTAL;
	}

	if(!keyboard->getKey('a') && !keyboard->getKey('d'))
	{
		if( abs(velocity.x) < 1.2 * BREAK_FACTOR * ACCELERATION_HORIZONTAL)
			velocity.x = 0;
		else if(velocity.x > 0)
			velocity.x -= BREAK_FACTOR * ACCELERATION_HORIZONTAL;
		else
			velocity.x += BREAK_FACTOR * ACCELERATION_HORIZONTAL;
	}
	#pragma endregion

	
	// Faster and slower
	if(keyboard->getKey('f'))
	{
		if(velocity.z > MIN_SPEED)
			velocity.z -= 0.2;
		if(velocity.z < MIN_SPEED)
			velocity.z = MIN_SPEED;
	}
	if(keyboard->getKey('r'))
	{
		if(velocity.z < MAX_SPEED)
			velocity.z += 0.1;
	}
	if(!(keyboard->getKey('f')) && !(keyboard->getKey('r')))
	{
		if (velocity.z > MIN_SPEED)
			velocity.z -= 0.1;

		else if (velocity.z < MIN_SPEED)
			velocity.z += 0.01;
	}


	if(keyboard->getKey('-'))
	{
		if(flipAngle > -90)
			flipAngle -= 10;
	}
	if(keyboard->getKey('.'))
	{
		if(flipAngle < 90)
			flipAngle += 10;
	}
	if(!keyboard->getKey('-') && !keyboard->getKey('.'))
	{
		if(flipAngle < 0)
			flipAngle += 10;
		if(flipAngle > 0)
			flipAngle -= 10;
	}


	move();

	rotate();

	ship->getBoundingBox()->setCenter(position);

	Vec3f camPosition = position;
	camPosition.x *= 0.33;
	camPosition.y *= 0.33;
	camPosition.y += 5;

	float speedDisplacement = velocity.z - MIN_SPEED;
	if(speedDisplacement < 0)
		speedDisplacement = 0;
	camPosition.z += 30 + 5 * speedDisplacement;

	Vec3f camLookAt = camPosition;
	camLookAt.x += 0.2 * position.x;
	camLookAt.y += 0.2 * position.y;
	camLookAt.z = position.z - 10;
	cam->set(camPosition, camLookAt);
}
//-------------------------------------------------------------------------
void Player::move()
{
	if( velocity.x > 0)
	{
		position.x += velocity.x;
		if(position.x > MAX_MOVE_HORIZONTAL)
			position.x = MAX_MOVE_HORIZONTAL;
	}
	if( velocity.x < 0)
	{
		position.x += velocity.x;
		if(position.x < -MAX_MOVE_HORIZONTAL)
			position.x = -MAX_MOVE_HORIZONTAL;
	}

	if( velocity.y > 0)
	{
		position.y += velocity.y;
		if(position.y > MAX_MOVE_UP)
			position.y = MAX_MOVE_UP;
	}
	if( velocity.y < 0)
	{
		position.y += velocity.y;
		if(position.y < -MAX_MOVE_DOWN)
			position.y = -MAX_MOVE_DOWN;
	}
}

//-------------------------------------------------------------------------
void Player::rotate()
{
	if(!spawned)
		return;

	float rotationZ =  velocity.x * 20;
	float maxRotationZ = rotationZ;

	if(velocity.x != 0 && abs(position.x) > MAX_MOVE_HORIZONTAL * 0.5f)
	{
		float factor = ((1.0f - (abs(position.x)/MAX_MOVE_HORIZONTAL)))*2.0f;
		maxRotationZ = maxRotationZ * factor * factor * factor;
		if(maxRotationZ < rotationZ)
			rotationZ = maxRotationZ;
	}


	float rotationX =  velocity.y * 20;
	float maxRotationX = rotationX;

	if(velocity.y != 0)
	{
		if(position.y > MAX_MOVE_UP * 0.75f)
		{
			float factor = (1.0f - (abs(position.y)/MAX_MOVE_UP))*4.0f;
			maxRotationX = maxRotationX * factor * factor * factor;
		} 
		else if(position.y < MAX_MOVE_DOWN * -0.75f)
		{
			float factor = (1.0f - (abs(position.y)/MAX_MOVE_DOWN))*4.0f;
			maxRotationX = maxRotationX * factor * factor * factor;
		}

		if(maxRotationX < rotationX)
			rotationX = maxRotationX;
	}

	upDir = Vec3f(0,1,0);
	direction = Vec3f(0,0,-1);

	if(abs(flipAngle) > 60)
	{
		if(flipAngle > 0)
		{
			//upDir.rotateDegZ(-rotationX);
			//direction.rotateDegX(-rotationZ);
			upDir.rotateDegZ(-rotationX);
			direction.rotateDegX(-rotationZ);
		}
		else
		{
			//upDir.rotateDegZ(rotationX);
			//direction.rotateDegX(rotationZ);
			upDir.rotateDegZ(rotationX);
			direction.rotateDegX(rotationZ);
		}
	}
	else
	{
		upDir.rotateDegZ(-rotationZ);
		direction.rotateDegX(rotationX);
	}

	//direction.rotateDegY(flipAngle);


	upDir.normalize();
	direction.normalize();
}
//-------------------------------------------------------------------------
void Player::render()
{

		if(!spawned)
			return;

		shipMaterial->activate();
	

		// UP Vector und Ausrichtung anzeigen
		#ifdef _DEBUG
		glLineWidth(3.0f);
		glBegin(GL_LINES);

		glVertex3f(position.x, position.y, position.z);
		glVertex3f(	position.x + direction.x * 50,
			position.y + direction.y * 50,
			position.z + direction.z * 50);

		glVertex3f(position.x, position.y, position.z);
		glVertex3f(	position.x + upDir.x * 50,
			position.y + upDir.y * 50,
			position.z + upDir.z * 50);
		glEnd();
		glLineWidth(1.0f);
		#endif
	
		glPushMatrix();

			// Achsen berechnen

			// x
			Vec3f localXAxis = upDir;
			localXAxis.crossToSelf(&direction);
			localXAxis.normalize();

			// y
			Vec3f localYAxis = direction;
			localYAxis.crossToSelf(&localXAxis);
			localYAxis.normalize();

			// z = direction
			direction.normalize();

			//glLineWidth(3.0f);
			//glBegin(GL_LINES);

			//glVertex3f(position.x, position.y, position.z);
			//glVertex3f(	position.x + localYAxis.x * 50,
			//			position.y + localYAxis.y * 50,
			//			position.z + localYAxis.z * 50);

			//glVertex3f( position.x, position.y, position.z);
			//glVertex3f(	position.x + localXAxis.x * 50,
			//			position.y + localXAxis.y * 50,
			//			position.z + localXAxis.z * 50);
			//glEnd();
			//glLineWidth(1.0f);

			// Matrix aufbauen

			GLfloat transform[16] = {	localXAxis.x, localYAxis.x, direction.x, 0,
										localXAxis.y, localYAxis.y, direction.y, 0,
										localXAxis.z, localYAxis.z, direction.z, 0,
										0, 0, 0, 1 };
			//glLoadIdentity();
			glTranslatef(position.x, position.y, position.z);
			glRotatef(flipAngle,0,0,1);
			glMultMatrixf(transform);
	
			texture->activate();
			ship->render();
			texture->deactivate();

			glDisable (GL_CULL_FACE);
			//glBlendEquation(GL_MAX);

			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glDepthMask(false);


			// Horizontal
			flame->showForTicks(	Vec3f(0.825f, -0.3f, -3.5f), 
									Vec3f(0,1,0), Vec3f(0,0,-1),
									0.4f, 0.75f);
			flame->render();

			flame->showForTicks(	Vec3f(-0.825f, -0.3f, -3.5f), 
									Vec3f(0,1,0), Vec3f(0,0,-1),
									0.4f, 0.75f);
			flame->render();

			// Vertical
			flame->showForTicks(	Vec3f(0.825f, -0.3f, -3.5f), 
				Vec3f(0,1,0), Vec3f(-1,0,0),
				0.4f, 0.75f);
			flame->render();

			flame->showForTicks(	Vec3f(-0.825f, -0.3f, -3.5f), 
				Vec3f(0,1,0), Vec3f(-1,0,0),
				0.4f, 0.75f);
			flame->render();

			// Bottom
			flame_bottom->showForTicks(	Vec3f(0.825f, -0.3f, -2.8f), 
				Vec3f(0,0,-1), Vec3f(0,1,0),
				0.5f, 0.5f);
			flame_bottom->render();
			flame_bottom->showForTicks(	Vec3f(-0.825f, -0.3f, -2.8f), 
				Vec3f(0,0,-1), Vec3f(0,1,0),
				0.5f, 0.5f);
			flame_bottom->render();

			//glBlendEquation(GL_ADD);
			glEnable(GL_CULL_FACE);
			
			glDepthMask(true);
			//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

		glPopMatrix();
	
		#ifdef _DEBUG
			ship->getBoundingBox()->render();
		#endif
}