/**
 *  Triangles
 *  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 "Player.hpp"
#include "Assets.hpp"

Player::Player(): 
	Triangle(), 
	InputTarget(_inputMap), 
	Collidable(),
	Lantern(),
	_movement(0.f), 
	_movementSpeed(0.6f), 
	_movementMomentum(0.f),
	_movementAcceleration(7.f),
	_movementSuppresion(10.f),
	_rotation(0.f), 
	_rotationSpeed(0.6f), 
	_rotationMomentum(0.f),
	_rotationAcceleration(38.f),
	_rotationSuppresion(20.f),
	_hit(false),
	_dead(false),
	_won(false),
	//_particleSystem(512, 512),
	_ammo(4),
	_life(sf::Color::Red, 1.f),
	_mana(sf::Color::Blue, 0.942f),
	_type(Type::Tropical)
{
	/**
	 *  Default binding
	 */
	 if(sf::Joystick::isConnected(0))
	 {
		_inputMap.map(1, Input(sf::Joystick::Axis::Y, false));
		_inputMap.map(2, Input(sf::Joystick::Axis::Y, true));
		_inputMap.map(3, Input(sf::Joystick::Axis::Z, false));
		_inputMap.map(4, Input(sf::Joystick::Axis::Z, true));
		_inputMap.map(5, Input(13));
		_inputMap.map(6, Input(15, Input::Type::Pressed));
	 }
	 else
	 {
		_inputMap.map(1, Input(sf::Keyboard::W));
		_inputMap.map(2, Input(sf::Keyboard::S));
		_inputMap.map(3, Input(sf::Keyboard::A));
		_inputMap.map(4, Input(sf::Keyboard::D));
		_inputMap.map(5, Input(sf::Keyboard::Space));
		_inputMap.map(6, Input(sf::Keyboard::LControl, Input::Type::Pressed));
	 }
	
	/**
	 *  Input lambdas
	 */
	bind(1, [this](const sf::Event&, float power)
	{
		_movement += power;
	}); 
	bind(2, [this](const sf::Event&, float power)
	{
		_movement -= power * 0.8f;
	});
	bind(3, [this](const sf::Event&, float power)
	{
		_rotation -= power;
	});
	bind(4, [this](const sf::Event&, float power)
	{
		_rotation += power;
	});
	bind(5, [this](const sf::Event&, float)
	{
		if(_bulletTimer.getElapsedTime().asMilliseconds() > 750 and _ammo > 0)
		{
			// Does not check if _context != nullptr
			// so please do remember to call setContext().
			--_ammo;
			_bullets.emplace_back(getRotation(), getPosition(), 
								_context->assets->loadSound("data/audio/Shotgun.ogg"), 
								_context->assets->loadSound("data/audio/Bomb.ogg"),
								_context->foreground);
			_bullets.back().setTexture(_innerTexture);
			_bulletTimer.restart();
			_movementMomentum -= (rand() % 3000 + 8000) / 1000.f;
			_life.show();
			_mana.show();
		}
	});
	
	bind(6, [this](const sf::Event&, float)
	{
		_lightType++;
		_lightType %= 2;
		switch(_lightType)
		{
			case 0: 
				light()->_emissionSprite.setTexture(_context->assets->loadTexture("data/background/Light_Alt.png"));
				light()->_emissionSprite.setOrigin(sf::Vector2f(_context->assets->loadTexture("data/background/Light_Alt.png").getSize().x / 2u, _context->assets->loadTexture("data/background/Light_Alt.png").getSize().y - (_type == Illuminati ? 71 : 0)));
				break;
			case 1: 
				light()->_emissionSprite.setTexture(_context->assets->loadTexture("data/background/Light.png"));
				light()->_emissionSprite.setOrigin(sf::Vector2f(_context->assets->loadTexture("data/background/Light.png").getSize().x / 2u, _context->assets->loadTexture("data/background/Light.png").getSize().y / 2u - (_type == Illuminati ? 30 : 0)));
				break;
			default: 
				break;
		}
	});
	
	/**
	*  Particles
	*/

   // _particleSystem.setDissolutionRate(255.f);

	/**
	 *  Technical
	 */
	
	genCollisionBox("data/hitbox/Player.png", sf::Vector2f(71.f, 100.f));
	
	//_particleSystem.setOrigin(256.f, 256.f);
	
	_bullets.reserve(10);
	_bulletTimer.restart();
	 
	_durability = 0.4f;

	light()->_emissionSprite.setColor(sf::Color::White);
}

void Player::setContext(Context* context)
{
	_context = context;
	_glass.setBuffer(_context->assets->loadSound("data/audio/GlassHit.ogg"));
	_deathSound.setBuffer(_context->assets->loadSound("data/audio/GlassRekt.ogg"));
	_context->assets->loadTexture("data/triangle/Arc.png").setSmooth(true);
	_life.setTexture(_context->assets->loadTexture("data/triangle/Arc.png"));
	_mana.setTexture(_context->assets->loadTexture("data/triangle/Arc.png"));
	_lightType = 0;
	light()->_emissionSprite.setTexture(_context->assets->loadTexture("data/background/Light_Alt.png"));
	light()->_emissionSprite.setOrigin(sf::Vector2f(_context->assets->loadTexture("data/background/Light_Alt.png").getSize().x / 2u, _context->assets->loadTexture("data/background/Light_Alt.png").getSize().y - (_type == Illuminati ? 71 : 0)));
}

const bool Player::isDead() const
{
	return _dead;
}

const bool Player::hasWon() const
{
	return _won;
}

void Player::setTexture(sf::Texture& texture)
{
	_innerTexture = texture;
	_image = texture.copyToImage();
	//_particleSystem.loadColors(_image);
	_texture = &_innerTexture;
	vertexFit();
	
	unsigned pointCount = 0;
	for(unsigned i = 0; i < _image.getSize().x; ++i)
		for(unsigned j = 0; j < _image.getSize().y; ++j)
			if(_image.getPixel(i, j).a > 0)
				++pointCount;
	setPointCount(pointCount);
}

void Player::setType(Type type)
{
	_type = type;
}

bool Player::checkCollision(Foreground& foreground, bool pixel)
{
	Collidable::updateTransform(getTransform());
	int k = pixelPerfect(foreground);
	if(k == -1)
	{
		if(_glass.getStatus() != sf::Sound::Status::Playing)
			_glass.setVolume(0.f);
		_hit = false;
		return false;
	}
	float i = 10.f;
	while(k != -1)
	{
		sf::Vector2f pos = _cmap[k];
		sf::Vector2f center = sf::Vector2f(_image.getSize().x / 2.f, 100.f);
		if(center.x + pos.x >= 0 && center.x + pos.x < _image.getSize().x && center.y + pos.y >= 0 && center.y + pos.y < _image.getSize().y)
			_image.setPixel(center.x + pos.x, center.y + pos.y, sf::Color(0, 0, 0, 0));
		for(int i = -1; i <= 1; i++)
			for(int j = -1; j <= 1; j++)
			{
				if(i == 0 && j == 0) 
					continue;
				if(pos.x + center.x + i < 0 || pos.y + center.y + j < 0 || pos.x + center.x + i > _image.getSize().x || pos.y + center.y + j > _image.getSize().y)
					continue;
				if(_image.getPixel(pos.x + center.x + i, pos.y + center.y + j).a > 0)
				{
					sf::Vector2f vec;
					vec.x = pos.x + i;
					vec.y = pos.y + j;
					if(std::find(_cmap.begin(), _cmap.end(), vec) == _cmap.end())
						if(std::find(_cbanned.begin(), _cbanned.end(), vec) == _cbanned.end())
							_cmap.push_back(vec);
				}
			}
		_cmap.erase(_cmap.begin() + k);
		_cbanned.push_back(pos);
		k = pixelPerfect(foreground);
		if(i < 100.f) 
			i += 0.1f;
	}
	if(i > _glass.getVolume())
		_glass.setVolume(i);
	if(_glass.getStatus() != sf::Sound::Status::Playing) _glass.play();
	
	_innerTexture.loadFromImage(_image);
	_hit = true;
	_life.show();
	_mana.show();

	if(_pointCount * _durability < _cbanned.size())
	{
		_dead = true;
		_deathSound.play();
		return true;
	}
	else
		return false;
}

void Player::keyboardControls(sf::Time delta)
{
	float seconds = delta.asSeconds();
	const double degree = 3.14159265358 / 180.;
	
	if(_hit)
	{
		if(_movement > 0 || _movementMomentum > 0)
			_movementMomentum -= seconds * 15.f;
		if(_movement < 0 || _movementMomentum < 0)
			_movementMomentum += seconds * 15.f;
		_movement *= 0.6f;
	}

	_movementMomentum += _movementAcceleration * seconds * _movement;

	if(_movement == 0.f)
	{
		if(_movementMomentum > _movementSuppresion * seconds)
			_movementMomentum -= _movementSuppresion * seconds;
		else if(_movementMomentum < -_movementSuppresion * seconds)
			_movementMomentum += _movementSuppresion * seconds;
		else
			_movementMomentum = 0.f;
	}

	sf::Vector2f movement;
	movement.x = std::sin(getRotation() * degree) * 60.f * _movementSpeed * _movementMomentum * seconds;
	movement.y = -std::cos(getRotation() * degree) * 60.f * _movementSpeed * _movementMomentum * seconds;
	
	move(movement);

	//_particleSystem.fuel(movement.x * 20.f, getRotation());
	//_particleSystem.fuel(movement.y * 20.f, getRotation());

	_movement = 0.f;

	_rotationMomentum += _rotationAcceleration * seconds * _rotation;

	if(_rotationMomentum > _rotationSuppresion * seconds)
		_rotationMomentum -= _rotationSuppresion * seconds;
	else if(_rotationMomentum < -_rotationSuppresion * seconds)
		_rotationMomentum += _rotationSuppresion * seconds;
	else
		_rotationMomentum = 0.f;

	float rotation = 90.f * _rotationSpeed * _rotationMomentum * seconds;
	rotate(rotation);

	_rotation = 0.f;
}

void Player::update(sf::Time delta)
{	
	keyboardControls(delta);
		
	sf::Vector2f position = getPosition();
	
	bool all = false;
	for(unsigned i = 0; i < _bullets.size(); ++i)
	{
		_bullets[i].update(delta, position);

		if(!_bullets[i].isDead())
			all = true;
	}
	if(!all)
		_bullets.clear();

	//_particleSystem.setPosition(position);
	
	sf::Listener::setPosition(position.x, position.y, 0);
	
	_life.setPosition(position);
	_mana.setPosition(position);
	
	_life.setSize((unsigned)getHullLeft());
	_mana.setSize(_ammo * 10);
	
	_life.update(delta);
	_mana.update(delta);
	
	if(getScale().x == 1.f)
	{
		//_particleSystem.clear();
		//_particleSystem.update(delta);
		//_particleSystem.render();
	}
	if(getScale().x < 0.1f)
	{
		_dead = true;
		_won = true;
	}
	
	light()->_emissionSprite.setPosition(getPosition());
	light()->_emissionSprite.setRotation(getRotation());
}

const float Player::getHullLeft() const
{
	return ((_pointCount * _durability - _cbanned.size()) / (_pointCount *  _durability)) * 100.f;
}

void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
	//if(_particleSystem.isEnabled())
		//target.draw(_particleSystem);
	
	for(unsigned i = 0; i < _bullets.size(); ++i)
		target.draw(_bullets[i]);
		
	states.transform *= getTransform();

	if(_texture != nullptr)
		states.texture = (_texture);
    
	target.draw(_vertices, states);
	
	target.draw(_life);
	target.draw(_mana);
}