#include "GameStates.h"

#include "Engine/Engine.h"
#include "Engine/EntitySystem/EntitySystem.h"
#include "Engine/EntitySystem/Components/BoxCollisionComponent.h"
#include "Engine/EntitySystem/Components/SpriteComponent.h"
#include "Engine/EntitySystem/Components/TextRenderComponent.h"
#include "Engine/EntitySystem/Components/ViewComponent.h"
#include "Engine/SFMLMath/SFMLMath.hpp"

#include "Game/Entities/TankEntity.h"
#include "Game/Entities/GameManager.h"
#include "Game/Entities/PickupEntity.h"
#include "Game/Entities/BulletEntity.h"


//////////////////////////////////////////////////////////////////////////
void GameState::OnExit(GameState* pNextState)
{
	EntitySystem& entitySystem = Engine::GetInstance()->GetEntitySystem();
	for (int id : m_ownedEntityIds)
	{
		if (Entity* pEntity = entitySystem.FindEntityById(id))
		{
			pEntity->Destroy();
		}
	}

	m_ownedEntityIds.clear();
}

//////////////////////////////////////////////////////////////////////////
void TitleScreenGameState::OnEnter(GameState* pPrevState)
{
	{
		sf::RenderWindow& renderWindow = Engine::GetInstance()->GetRenderWindow();

		Entity* pPlayfield = Engine::GetInstance()->GetEntitySystem().SpawnEntity<Entity>();
		SpriteComponent* pSpriteComp = pPlayfield->AddComponent<SpriteComponent>();
		pSpriteComp->SetSpriteByPath("../Assets/bg.png", true);

		if (ViewComponent* pViewComponent = pPlayfield->AddComponent<ViewComponent>())
		{
			sf::View view;
			view.setSize(sf::Vector2f(static_cast<float>(renderWindow.getSize().x), static_cast<float>(renderWindow.getSize().y)));
			view.setViewport(sf::FloatRect(0.f, 0.f, 1.f, 1.f));
			view.setCenter(pPlayfield->GetPosition());
			pViewComponent->SetView(view);
		}
	}

	{
		Entity* pTitle = Engine::GetInstance()->GetEntitySystem().SpawnEntity<Entity>();
		SpriteComponent* pSpriteComp = pTitle->AddComponent<SpriteComponent>();
		pSpriteComp->SetSpriteByPath("../Assets/logo.png", true);

		pTitle->SetPosition(pTitle->GetPosition() - sf::Vector2f(0, 100.f));

		m_ownedEntityIds.push_back(pTitle->GetId());
	}

	{
		Entity* pInputPrompt = Engine::GetInstance()->GetEntitySystem().SpawnEntity<Entity>();
		m_pPromptComponent = pInputPrompt->AddComponent<TextRenderComponent>();
		m_pPromptComponent->SetFontByPath("../Assets/Fonts/Daydream.ttf");
		m_pPromptComponent->GetText().setString("Press Enter to continue");
		m_pPromptComponent->CenterText();

		pInputPrompt->SetPosition(pInputPrompt->GetPosition() + sf::Vector2f(0, 200.f));

		m_ownedEntityIds.push_back(pInputPrompt->GetId());
	}

	{
		//Input
		m_inputCallbackId = Engine::GetInstance()->GetInputManager().Register(
			sf::Keyboard::Enter,
			EInputEvent::Released,
			[&]() {
				GameManager::Get()->SetGameState(std::make_shared<IngameGameState>());
			});
	}
}

void TitleScreenGameState::Update(float deltaSeconds)
{
	sf::Color color(255, 255, 255, 100 + static_cast<int>(((std::sin(Engine::GetInstance()->GetElapsedTime().asSeconds() * 3.f) + 1.f) * 0.5f) * 155));
	m_pPromptComponent->GetText().setFillColor(color);
}

void TitleScreenGameState::OnExit(GameState* pNextState)
{
	GameState::OnExit(pNextState);

	Engine::GetInstance()->GetInputManager().Unregister(m_inputCallbackId);
}

//////////////////////////////////////////////////////////////////////////
void IngameGameState::OnEnter(GameState* pPrevState)
{
	{
		CreatePlayfieldBounds();
	}

	{
		// Player entities
		const sf::Color tankOneColor = sf::Color(102, 153, 255);
		const sf::Color tankTwoColor = sf::Color(255, 102, 102);

		TankEntity* pTankOne = Engine::GetInstance()->GetEntitySystem().SpawnEntity<TankEntity>();
		pTankOne->SetInputKeys(sf::Keyboard::Space, { sf::Keyboard::W, sf::Keyboard::A, sf::Keyboard::S, sf::Keyboard::D });
		pTankOne->SetColor(tankOneColor);
		pTankOne->SetPosition(sf::Vector2f(300.f, 0.f));

		m_tankOneId = pTankOne->GetId();

		TankEntity* pTankTwo = Engine::GetInstance()->GetEntitySystem().SpawnEntity<TankEntity>();
		pTankTwo->SetInputKeys(sf::Keyboard::Enter, { sf::Keyboard::Up, sf::Keyboard::Left, sf::Keyboard::Down, sf::Keyboard::Right });
		pTankTwo->SetColor(tankTwoColor);
		pTankTwo->SetPosition(sf::Vector2f(-300.f, 0.f));

		m_tankTwoId = pTankTwo->GetId();

		pTankOne->SetTargetId(m_tankTwoId, tankTwoColor);
		pTankTwo->SetTargetId(m_tankOneId, tankOneColor);
	}

	m_secondsUntilBuffSpawn = m_secondsBetweenBuffSpawn;
	std::srand(static_cast<int>(std::time(nullptr)));
}

void IngameGameState::Update(float deltaSeconds)
{
	m_secondsUntilBuffSpawn -= deltaSeconds;
	if (m_secondsUntilBuffSpawn <= 0.f)
	{
		m_secondsUntilBuffSpawn = m_secondsBetweenBuffSpawn;

		if (Engine::GetInstance()->GetEntitySystem().CountAllEntitiesOfType<PickUpEntity>() < 5)
			SpawnRandomBuff();
	}
}

void IngameGameState::CreatePlayfieldBounds()
{
	constexpr float colliderThickness = 100.f;

	sf::Vector2u size = Engine::GetInstance()->GetRenderWindow().getSize();

	EntitySystem& entitySystem = Engine::GetInstance()->GetEntitySystem();

	{
		//Upper bound
		Entity* pBound = entitySystem.SpawnEntity<Entity>();
		pBound->SetPosition(sf::Vector2f(0.f, (size.y + colliderThickness) * -0.5f));

		BoxCollisionComponent* pBoxCollider = pBound->AddComponent<BoxCollisionComponent>();
		pBoxCollider->SetProperties(static_cast<float>(size.x), colliderThickness, true, false, nullptr);

		m_boundIds.push_back(pBound->GetId());
	}

	{
		//Lower bound
		Entity* pBound = entitySystem.SpawnEntity<Entity>();
		pBound->SetPosition(sf::Vector2f(0.f, (size.y + colliderThickness) * 0.5f));

		BoxCollisionComponent* pBoxCollider = pBound->AddComponent<BoxCollisionComponent>();
		pBoxCollider->SetProperties(static_cast<float>(size.x), colliderThickness, true, false, nullptr);

		m_boundIds.push_back(pBound->GetId());
	}

	{
		//Right bound
		Entity* pBound = entitySystem.SpawnEntity<Entity>();
		pBound->SetPosition(sf::Vector2f((size.x + colliderThickness) * 0.5f, 0.f));

		BoxCollisionComponent* pBoxCollider = pBound->AddComponent<BoxCollisionComponent>();
		pBoxCollider->SetProperties(colliderThickness, static_cast<float>(size.y), true, false, nullptr);

		m_boundIds.push_back(pBound->GetId());
	}

	{
		//Left bound
		Entity* pBound = entitySystem.SpawnEntity<Entity>();
		pBound->SetPosition(sf::Vector2f((size.x + colliderThickness) * -0.5f, 0.f));

		BoxCollisionComponent* pBoxCollider = pBound->AddComponent<BoxCollisionComponent>();
		pBoxCollider->SetProperties(colliderThickness, static_cast<float>(size.y), true, false, nullptr);

		m_boundIds.push_back(pBound->GetId());
	}
}

void IngameGameState::SpawnRandomBuff()
{
	EntitySystem& entitySystem = Engine::GetInstance()->GetEntitySystem();

	sf::Vector2u sizeInt = Engine::GetInstance()->GetRenderWindow().getSize();
	sf::Vector2f size(static_cast<float>(sizeInt.x), static_cast<float>(sizeInt.y));
	size.x *= 0.9f;
	size.y *= 0.9f;

	const float randX = sf::GetRandomInRange(0.f, size.x) - size.x * 0.5f;
	const float randY = sf::GetRandomInRange(0.f, size.y) - size.y * 0.5f;

	PickUpEntity* pSpawnedPickUp = nullptr;

	const int type = std::rand() % 3;
	switch (type)
	{
	case 0:
		pSpawnedPickUp = entitySystem.SpawnEntity<TurnSpeedPickUp>();
		break;
	case 1:
		pSpawnedPickUp = entitySystem.SpawnEntity<MoveSpeedPickUp>();
		break;
	case 2:
		pSpawnedPickUp = entitySystem.SpawnEntity<TripleShotPickUp>();
		break;
	}

	m_ownedEntityIds.push_back(pSpawnedPickUp->GetId());
	pSpawnedPickUp->SetPosition(sf::Vector2f(randX, randY));
}

//////////////////////////////////////////////////////////////////////////
void GameOverGameState::OnEnter(GameState* pPrevState)
{
	EntitySystem& entitySystem = Engine::GetInstance()->GetEntitySystem();

	std::string displayText;
	sf::Color displayColor;

	if (IngameGameState* pIngameState = static_cast<IngameGameState*>(pPrevState))
	{
		m_ownedEntityIds.push_back(pIngameState->m_tankOneId);
		m_ownedEntityIds.push_back(pIngameState->m_tankTwoId);
		m_ownedEntityIds.insert(m_ownedEntityIds.begin(), pIngameState->m_boundIds.begin(), pIngameState->m_boundIds.end());

		TankEntity* pTankEntityOne = static_cast<TankEntity*>(entitySystem.FindEntityById(pIngameState->m_tankOneId));
		TankEntity* pTankEntityTwo = static_cast<TankEntity*>(entitySystem.FindEntityById(pIngameState->m_tankTwoId));

		//Make tanks invulnerable
		pTankEntityOne->AddBuff(std::make_shared<ArmorBuff>(0.f));
		pTankEntityTwo->AddBuff(std::make_shared<ArmorBuff>(0.f));

		displayText = loserEntityId == pIngameState->m_tankTwoId ? "Blue won!" : "Red won!";
		displayColor = loserEntityId == pIngameState->m_tankTwoId ? sf::Color(102, 153, 255) : sf::Color(255, 102, 102);
	}

	//Input
	m_inputCallbackId = Engine::GetInstance()->GetInputManager().Register(
		sf::Keyboard::R,
		EInputEvent::Released,
		[]() {
			GameManager::Get()->SetGameState(std::make_shared<IngameGameState>());
		});

	Entity* pEntity = entitySystem.SpawnEntity<Entity>();
	TextRenderComponent* pWinnerText = pEntity->AddComponent<TextRenderComponent>();
	pWinnerText->SetFontByPath("../Assets/Fonts/Daydream.ttf");
	pWinnerText->GetText().setString(displayText);
	pWinnerText->GetText().setCharacterSize(60);
	pWinnerText->GetText().setFillColor(displayColor);
	pWinnerText->GetText().setPosition(sf::Vector2f(0, -50.f));
	pWinnerText->CenterText();

	TextRenderComponent* pTextPrompt = pEntity->AddComponent<TextRenderComponent>();
	pTextPrompt->SetFontByPath("../Assets/Fonts/Daydream.ttf");
	pTextPrompt->GetText().setString("Press R for a rematch");

	pTextPrompt->GetText().setPosition(sf::Vector2f(0, 50.f));
	pTextPrompt->CenterText();

	m_ownedEntityIds.push_back(pEntity->GetId());
}

void GameOverGameState::OnExit(GameState* pNextState)
{
	GameState::OnExit(pNextState);

	Engine::GetInstance()->GetInputManager().Unregister(m_inputCallbackId);
	Engine::GetInstance()->GetEntitySystem().DestroyAllEntitiesOfType<BulletEntity>();
	Engine::GetInstance()->GetEntitySystem().DestroyAllEntitiesOfType<PickUpEntity>();
}
