#include "TankEntity.h"

#include "Engine/EntitySystem/EntityFactory.h"
#include "Engine/EntitySystem/Components/SpriteComponent.h"
#include "Engine/EntitySystem/Components/RigidbodyComponent.h"
#include "Engine/EntitySystem/Components/CircleCollisionComponent.h"
#include "Engine/EntitySystem/Components/ViewComponent.h"
#include "Engine/Engine.h"

#include "Game/Entities/BulletEntity.h"
#include "Game/Entities/HealthbarEntity.h"
#include "Game/Entities/GameManager.h"
#include "Game/GameStates.h"

#include "Engine/SFMLMath/SFMLMath.hpp"

void TankEntity::Init()
{
	m_pTankBaseSpriteComponent = AddComponent<SpriteComponent>();
	m_pTankBaseSpriteComponent->SetSpriteByPath("../Assets/Tanks/Tanks_base/tank2_greyscale.png", true);

	m_pTankTurretSpriteComponent = AddComponent<SpriteComponent>();
	m_pTankTurretSpriteComponent->SetSpriteByPath("../Assets/Tanks/Cannons_color1/cannon3_greyscale.png", false);
	m_pTankTurretSpriteComponent->GetSprite().setOrigin(sf::Vector2f(63.f, 40.f));

	m_pEnemyIndicatorSpriteComponent = AddComponent<SpriteComponent>();
	m_pEnemyIndicatorSpriteComponent->SetSpriteByPath("../Assets/dir_indicator.png", false);
	m_pEnemyIndicatorSpriteComponent->GetSprite().setOrigin(8.f, 63.f);

	m_pCollisionComponent = AddComponent<CircleCollisionComponent>();
	m_pCollisionComponent->SetProperties(40.f, false, false, nullptr);

	m_pHealthbarEntity = Engine::GetInstance()->GetEntitySystem().SpawnEntity<HealthbarEntity>();
	m_pHealthbarEntity->SetOwnerEntity(GetId());
	m_pHealthbarEntity->SetOffset(sf::Vector2f(0.f, -75.f));

	m_currentHealth = m_maxHealth;

	std::shared_ptr<MovementSpeedBuff> pBaseMoveSpeedBuff = std::make_shared<MovementSpeedBuff>();
	pBaseMoveSpeedBuff->flatMoveSpeedIncrease = 200.f;
	AddBuff(pBaseMoveSpeedBuff);

	std::shared_ptr<TurnSpeedBuff> pBaseTurnSpeedBuff = std::make_shared<TurnSpeedBuff>();
	pBaseTurnSpeedBuff->flatTurnSpeedIncrease = 200.f;
	AddBuff(pBaseTurnSpeedBuff);

	std::shared_ptr<BulletCountBuff> pBaseBulletCountBuff = std::make_shared<BulletCountBuff>();
	pBaseBulletCountBuff->additionalBulletsPerFire = 1;
	AddBuff(pBaseBulletCountBuff);

	ReevaluateStats();
}

void TankEntity::Shutdown()
{
	if (m_inputCallbackId != 0)
	{
		Engine::GetInstance()->GetInputManager().Unregister(m_inputCallbackId);
		m_inputCallbackId = 0;
	}

	if (m_pHealthbarEntity)
	{
		m_pHealthbarEntity->Destroy();
		m_pHealthbarEntity = nullptr;
	}
}

void TankEntity::Update(float deltaSeconds)
{
	if (m_handleInput)
	{
		UpdateMovement(GetMovementVector(), deltaSeconds);
	}
	UpdateTurretRotation(deltaSeconds);
	UpdateEnemyIndicatorRotation(deltaSeconds);
	UpdateBuffs(deltaSeconds);
}

void TankEntity::ProcessDamage(float damageAmount)
{
	for (const std::shared_ptr<Buff>& pBuff : m_buffs)
	{
		pBuff->EvaluateStat(EBuffStat::Armor, damageAmount);
	}

	if (damageAmount <= 0.f)
		return;

	m_currentHealth = std::max(0.f, m_currentHealth - damageAmount);

	if (m_currentHealth <= 0.f)
	{
		m_handleInput = false;
		m_pTankBaseSpriteComponent->SetSpriteByPath("../Assets/Tanks/Broken_assets/tank2_broken_greyscale.png", true);
		m_pTankTurretSpriteComponent->SetVisibility(false);

		std::shared_ptr<GameOverGameState> pGameOverState = std::make_shared<GameOverGameState>();
		pGameOverState->loserEntityId = GetId();
		GameManager::Get()->SetGameState(pGameOverState);
	}
}

void TankEntity::SetInputKeys(sf::Keyboard::Key fireButton, const TMovementKeys& movementKeys)
{
	if (m_inputCallbackId != 0)
	{
		Engine::GetInstance()->GetInputManager().Unregister(m_inputCallbackId);
		m_inputCallbackId = 0;
	}

	m_inputCallbackId = Engine::GetInstance()->GetInputManager().Register(fireButton, EInputEvent::Pressed, std::bind(&TankEntity::OnInputPressed, this));
	m_movementKeys = movementKeys;
}

void TankEntity::SetColor(sf::Color color)
{
	m_factionColor = color;

	m_pTankBaseSpriteComponent->GetSprite().setColor(m_factionColor);
	m_pTankTurretSpriteComponent->GetSprite().setColor(m_factionColor);
}

void TankEntity::SetTargetId(int targetEntityId, sf::Color color)
{
	m_targetEntityId = targetEntityId;
	m_pEnemyIndicatorSpriteComponent->GetSprite().setColor(color);
}

void TankEntity::AddBuff(std::shared_ptr<Buff> pBuff)
{
	if (pBuff)
	{
		m_buffs.push_back(pBuff);
		ReevaluateStats();
	}
}

void TankEntity::UpdateMovement(sf::Vector2f movementVector, float deltaSeconds)
{
	const sf::Vector2f oldPosition = GetPosition();
	const sf::Vector2f newPosition = GetPosition() + (movementVector * deltaSeconds * m_movementSpeed);
	SetPosition(newPosition);
	UpdateBaseRotation(movementVector, oldPosition, newPosition);
}

void TankEntity::UpdateBuffs(float deltaSeconds)
{
	for (int i = static_cast<int>(m_buffs.size()) - 1; i >= 0; i--)
	{
		std::shared_ptr<Buff> pBuff = m_buffs[i];

		if (pBuff->lifetime < 0.f)
		{
			continue;
		}

		pBuff->lifetime -= deltaSeconds;
		if (pBuff->lifetime <= 0.f)
		{
			m_buffs.erase(m_buffs.begin() + i);
		}
	}

	ReevaluateStats();
}

void TankEntity::ReevaluateStats()
{
	m_movementSpeed = std::min(ReevaluateStat(EBuffStat::MoveSpeed), 500.f);
	m_bulletsPerShot = std::min(static_cast<int>(ReevaluateStat(EBuffStat::BulletCount)), 5);
	m_turretTurnSpeed = std::min(ReevaluateStat(EBuffStat::TurnSpeed), 1080.f);
}

float TankEntity::ReevaluateStat(EBuffStat stat)
{
	float value = 0.f;
	for (std::shared_ptr<Buff> pBuff : m_buffs)
	{
		pBuff->EvaluateStat(stat, value);
	}

	return value;
}

sf::Vector2f TankEntity::GetMovementVector() const
{
	InputManager& manager = Engine::GetInstance()->GetInputManager();

	float movementSpeed = 200.f;
	sf::Vector2f movementVector;

	// vertical
	if (manager.IsKeyPressed(m_movementKeys[0]))
	{
		movementVector.y -= 1.f;
	}

	if (manager.IsKeyPressed(m_movementKeys[2]))
	{
		movementVector.y += 1.f;
	}

	// horizontal
	if (manager.IsKeyPressed(m_movementKeys[1]))
	{
		movementVector.x -= 1.f;
	}

	if (manager.IsKeyPressed(m_movementKeys[3]))
	{
		movementVector.x += 1.f;
	}

	return movementVector;
}

void TankEntity::UpdateBaseRotation(sf::Vector2f movementVector, sf::Vector2f oldPosition, sf::Vector2f newPosition)
{
	if (movementVector.x != 0.f || movementVector.y != 0.f)
	{
		const float degree = sf::GetRotationFromDirection(newPosition - oldPosition);
		m_pTankBaseSpriteComponent->GetSprite().setRotation(degree);
	}
}

void TankEntity::UpdateTurretRotation(float deltaSeconds)
{
	const float currentRotation = m_pTankTurretSpriteComponent->GetSprite().getRotation();
	m_pTankTurretSpriteComponent->GetSprite().setRotation(currentRotation + m_turretTurnSpeed * deltaSeconds);
}

void TankEntity::UpdateEnemyIndicatorRotation(float deltaSeconds)
{
	if (Entity* pTargetEntity = Engine::GetInstance()->GetEntitySystem().FindEntityById(m_targetEntityId))
	{
		m_pEnemyIndicatorSpriteComponent->GetSprite().setRotation(sf::GetRotationFromDirection(GetPosition() - pTargetEntity->GetPosition()));
	}
}

void TankEntity::OnInputPressed()
{
	if (!m_handleInput)
		return;

	constexpr float bulletOffset = 20.f;
	const float positionOffset = (m_bulletsPerShot - 1) * bulletOffset * 0.5f;

	const float turretRotationInDegrees = m_pTankTurretSpriteComponent->GetSprite().getRotation();
	sf::Vector2f fireVector = sf::GetDirectionFromRotation(turretRotationInDegrees);
	sf::Vector2f rightVector = sf::GetDirectionFromRotation(turretRotationInDegrees + 90);

	for (int i = 0; i < m_bulletsPerShot; i++)
	{
		BulletEntity* pBulletEntity = Engine::GetInstance()->GetEntitySystem().SpawnEntity<BulletEntity>();

		const sf::Vector2f newPosition = GetPosition() + fireVector * 61.f + (-rightVector * positionOffset) + (rightVector * bulletOffset * static_cast<float>(i));
		pBulletEntity->SetPosition(newPosition);
		pBulletEntity->SetRotation(sf::GetRotationFromDirection(newPosition - GetPosition()));

		pBulletEntity->Launch(500.f, 1.f, 20.f, 5, m_factionColor, GetId());
	}
}