/**
 *  one room arena
 *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, version 3 of the License.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */



#include <cmath>
#include <iostream>
#include "Player.hpp"
#include "Map.hpp"
#include "Bullet.hpp"
#include "Powerup.hpp"

ActualPlayer* Player::actualPlayer;
std::vector<AiPlayer*>* Player::aiPlayer;
unsigned Player::totalScore = 0;

Player::Player(Map* map): 
	ConvexShape(6), 
	_nukes(0), 
	_speed(400.f), 
	_strength(200), 
	_rateOfFire(1.f), 
	_map(map), 
	_dead(false)
{
	const int playerRadius = 30;
	const float angle = 40.f;

	this->setPoint(0, Vector2f(0, -playerRadius));
	this->setPoint(1, Vector2f(-std::sin(angle * 3.1415f / 180.f) * playerRadius / 3.25f, -std::cos(angle * 3.1415f / 180.f) * playerRadius / 3.f));
	this->setPoint(2, Vector2f(-std::sin(angle * 3.1415f / 180.f) * playerRadius, std::cos(angle * 3.1415f / 180.f) * playerRadius));
	this->setPoint(3, Vector2f(0, playerRadius / 4.f));
	this->setPoint(4, Vector2f(std::sin(angle * 3.1415f / 180.f) * playerRadius, std::cos(angle * 3.1415f / 180.f) * playerRadius));
	this->setPoint(5, Vector2f(std::sin(angle * 3.1415f / 180.f) * playerRadius / 3.25f, -std::cos(angle * 3.1415f / 180.f) * playerRadius / 3.f));
	this->setOutlineThickness(playerRadius * 6.f);
	
	_killKill.loadFromFile("data/explosion.ogg");
	_kill.setBuffer(_killKill);
	_laserLaser.loadFromFile("data/shoot.ogg");
	_laser.setBuffer(_laserLaser);
	_powerupPowerup.loadFromFile("data/powerup.ogg");
	_powerup.setBuffer(_powerupPowerup);
}

Player::~Player()
{
	
}

void Player::setColor(Color color)
{
	this->setFillColor(color);
	this->setOutlineColor(Color(color.r, color.g, color.b, 15));
	_color = color;
}

float mouseToRotation(Vector2f mouse)
{
	float val = std::atan(-mouse.y / mouse.x) * 180.f / 3.1415f;
	if(mouse.x < 0.f)
		val += 180.f;
	else if(mouse.x > 0.f && mouse.y > 0.f)
		val += 360.f;
	val += 270.f;
	val = std::fmod(val, 360.f);
	val = 360.f - val;
	return val;
}

void ActualPlayer::input(float delta, Map* map, const Vector2f mouse)
{
	static Clock clock;
	if(Mouse::isButtonPressed(Mouse::Left))
	{	
		if(clock.getElapsedTime().asSeconds() >= _rateOfFire)
		{
			spawnBullet(false);
			clock.restart();
		}
	}
	
	bool left = Keyboard::isKeyPressed(Keyboard::A);
	bool right = Keyboard::isKeyPressed(Keyboard::D);
	bool up = Keyboard::isKeyPressed(Keyboard::W);
	bool down = Keyboard::isKeyPressed(Keyboard::S);
	
	float targetRotation = mouseToRotation(mouse);
	Transform transform;
	transform.translate(getPosition());
	transform.rotate(targetRotation);
	for(unsigned i = 0; i < getPointCount(); ++i)
	{
		Vector2f pos = transform.transformPoint(getPoint(i));
		if(map->collision(pos))
			break;
		if(i == getPointCount() - 1)
			setRotation(targetRotation);
	}
	
	static float velocityF = 0.f;
	static float velocityS = 0.f;
	static Vector2f movement;
	
	movement = movementHelper(delta, right, left, velocityS, getRotation() + 90.f, _speed, 0.75f) * delta;
	movement += movementHelper(delta, up, down, velocityF, getRotation(), _speed, 0.5f) * delta;

	for(unsigned i = 0; i < getPointCount(); ++i)
	{
		Vector2f pos = movement + getTransform().transformPoint(getPoint(i));
		if(map->collision(pos))
		{
			velocityF *= -1.f;
			velocityS *= -1.f;
			movement *= -1.f;
			break;
		}	
	}

	move(movement);
}

void AiPlayer::updateAi(Map* map, ActualPlayer* player)
{ 
	if(std::pow(player->getPosition().x - getPosition().x, 2) + std::pow(player->getPosition().y - getPosition().y, 2) < 1000*1000)
	{
		_targetRotation = mouseToRotation(Vector2f(player->getPosition().x - getPosition().x, player->getPosition().y - getPosition().y)) + _targetRotationDeviation;
		if(_nukes-- > 0)
			spawnBullet(true);
		else
			_nukes = 0;
	}
	else if(_powerupManager != nullptr)
	{
		for(unsigned i = 0; i < _powerupManager->getSize(); ++i)
		{
			if(std::pow(_powerupManager->getPowerup(i).getPosition().x - getPosition().x, 2) + std::pow(_powerupManager->getPowerup(i).getPosition().y - getPosition().y, 2) < 1000*1000)
			{
				_targetRotation = mouseToRotation(Vector2f(_powerupManager->getPowerup(i).getPosition().x - getPosition().x, _powerupManager->getPowerup(i).getPosition().y - getPosition().y)) + _targetRotationDeviation;
			}
		}
	}
}

void AiPlayer::input(float delta, Map* map, ActualPlayer* player)
{
	if(_clock.getElapsedTime().asSeconds() >= _rateOfFire)
	{
		spawnBullet(false);
		_clock.restart();
	}
	if(_clock2.getElapsedTime().asSeconds() >= 0.5f)
	{
		updateAi(map, player);
		_clock2.restart();
	}
	
	Transform transform;
	transform.translate(getPosition());
	transform.rotate(_targetRotation);

	for(unsigned i = 0; i < getPointCount(); ++i)
	{
		Vector2f pos = transform.transformPoint(getPoint(i));
		if(map->collision(pos))
			break;
		if(i == getPointCount() - 1)
			setRotation(_targetRotation);
	}
	
	Vector2f movement;
	movement = movementHelper(delta, true, false, _velocity, getRotation(), _speed, 0.5f) * delta;

	for(unsigned i = 0; i < getPointCount(); ++i)
	{
		Vector2f pos = movement + getTransform().transformPoint(getPoint(i));
		if(map->collision(pos))
		{
			_velocity *= -1.f;
			movement *= -1.f;
			break;
		}	
	}

	for(unsigned i = 0; i < getPointCount(); ++i)
	{
		Vector2f pos = movement * 100.f + getTransform().transformPoint(getPoint(i));
		if(map->collision(pos))
		{
			_targetRotation += delta * 720.f;
			_targetRotationDeviation += delta * 720.f;
			_targetRotation = std::fmod(_targetRotation, 360.f);
			_targetRotationDeviation = std::fmod(_targetRotationDeviation, 360.f);
			break;
		}
		if(i == getPointCount() - 1)
			_targetRotationDeviation = 0;
	}

	move(movement);
	
	_laser.setPosition(Vector3f(getPosition().x - player->getPosition().x, getPosition().y - player->getPosition().y, 0) / 100.f);
	_powerup.setPosition(Vector3f(getPosition().x - player->getPosition().x, getPosition().y - player->getPosition().y, 0) / 100.f);
	if(_kill.getStatus() != Sound::Playing)
		_kill.setPosition(Vector3f(getPosition().x - player->getPosition().x, getPosition().y - player->getPosition().y, 0) / 200.f);
}

void AiPlayer::registerPowerups(PowerupManager* mgr)
{
	_powerupManager = mgr;
}

void Player::logic(float delta)
{
	for(unsigned i = 0; i < _bullets.size(); ++i)
	{
		_bullets[i]->update(delta);
		if(_bullets[i]->isDead())
		{
			_bullets.erase(_bullets.begin() + i);
			if(_bullets[i]->isExplosive())
			{
				_kill.setPosition(Vector3f(0.f, 0.f, 0.f));
				_kill.play();
			}
		}
	}
}

void Player::spawnBullet(bool altFire)
{
	if(!altFire)
	{
		_bullets.push_back(new RectBullet(getPosition(), getRotation(), _strength));
		_laser.play();
	}
	else
	{
		if(_nukes--)
			_bullets.push_back(new NukeBullet(getPosition(), getRotation(), _strength));
		else
		{
			_nukes = 0;
			return;
		}
	}
	if(this == actualPlayer)
		_bullets.back()->madeByPlayer();
	_bullets.back()->registerSelf(this);
	_bullets.back()->registerMap(_map);
	_bullets.back()->setColor(_color);
}

void Player::drawBullets(RenderTarget& target) const
{
	for(unsigned i = 0; i < _bullets.size(); ++i)
	{
		target.draw(*_bullets[i]);
	}
}

void Player::hasten()
{
	_powerup.play();
	_speed += 150.f;
}

void Player::addNuke()
{
	_powerup.play();
	++_nukes;
}

int Player::nukes() const
{
	return _nukes;
}

void Player::growStronger()
{
	if(_strength <= 600)
	{
		_powerup.play();
		_strength += 50;
	}
}

void Player::rateOfFire()
{
	if(_rateOfFire > 0.2f)
	{
		_powerup.play();
		_rateOfFire -= 0.1f;
	}
}

void Player::kill(bool increaseScore)
{
	totalScore += 1000 * increaseScore;
	_dead = true;
	_kill.play();
}
bool Player::isDead() const
{
	return _dead;
}

Vector2f Player::movementHelper(float delta, bool key1, bool key2, float& velocity, float angle, float speed, float damping)
{
	Vector2f rotated = Vector2f(std::sin(angle * 3.1415f / 180.f), -std::cos(angle * 3.1415f / 180.f));
	
	if(key1 && !key2)
	{
		if(velocity < speed)
			velocity += speed * delta;
	}
	else if(!key1 && key2)
	{
		if(velocity > -speed)
			velocity -= speed * delta;
	}
	else 
	{
		if(std::fabs(velocity) < 50.f)
			velocity = 0.f;
		else if(velocity > 0.f)
			velocity -= speed * delta * damping;
		else
			velocity += speed * delta * damping;
	}
	
	return rotated * velocity;
}
