#include <Game.h>
#include <vector>

#include <Util.h>

#include <SFML/Graphics.hpp>
#include <imgui-SFML.h>
#include <imgui.h>

#include <fstream>
#include <iostream>


Game::Game(bool useImgui_in)
	: config()
	, window({sf::VideoMode({ 1920u, 1080u }), "project-snake"})
	, font("assets/fonts/ChakraPetch-Regular.ttf")
	, score(font)
	, useImgui(useImgui_in)
{ 
	if (!ImGui::SFML::Init(window)) 
	{
		std::cout << "ERROR: Could not open window\n";
		return;
	}

	if (!parseConfigFile()) 
	{
		std::cout << "ERROR: Could not parse config file\n";
		window.close();
		return;
	}

	gridCount.x = window.getSize().x / config.gridSize;
	gridCount.y = window.getSize().y / config.gridSize;

	player.body.reserve(100);
	player.body.emplace_back(config.headGridStartPos);

	fruit.gridPos = {5,5};
	fruit.color = sf::Color::Red;
	
	//uncomment to quickly test player movement
	/* 
	player.body.emplace_back(sf::Vector2i{config.headGridStartPos.x - 1, config.headGridStartPos.y});
	player.body.emplace_back(sf::Vector2i{config.headGridStartPos.x - 2, config.headGridStartPos.y});
	player.body.emplace_back(sf::Vector2i{config.headGridStartPos.x - 3, config.headGridStartPos.y});
	player.body.emplace_back(sf::Vector2i{config.headGridStartPos.x - 4, config.headGridStartPos.y});
 	 */
	
	tempRect.setSize(sf::Vector2f{(float)config.gridSize, (float)config.gridSize});
	tempRect.setOutlineThickness(-5);
	tempRect.setOutlineColor(sf::Color::Black);

	score.setPosition({10, static_cast<float>(window.getSize().y - 40)});

	window.setFramerateLimit(config.framerate);

}

bool Game::parseConfigFile()
{
	std::ifstream configFile{"config/game-config.txt"};

	if(!configFile)
	{
		std::cerr << "ERROR: CANNOT OPEN 'game-config.txt'\n";
		return false;
	}

	std::string inputBuff;
	while(configFile >> inputBuff)
	{
		if (inputBuff == "framerate")
		{
			configFile >> config.framerate;
		}

		if (inputBuff == "tickRate")
		{
			configFile >> config.tickRate;
		}

		if (inputBuff == "playerHeadColor")
		{
			configFile >> config.playerHeadColor.r >> config.playerHeadColor.g >> config.playerHeadColor.b;
		}

		if (inputBuff == "playerBodyColor")
		{
			configFile >> config.playerBodyColor.r >> config.playerBodyColor.g >> config.playerBodyColor.b;
		}
		
		if (inputBuff == "headGridStartPos")
		{
			configFile >> config.headGridStartPos.x >> config.headGridStartPos.y;
		}

		if (inputBuff == "gridSize")
		{
			configFile >> config.gridSize;
		}
	}

	return true;
}

void Game::input()
{
	while (const std::optional event = window.pollEvent())
	{
		ImGui::SFML::ProcessEvent(window, *event);

		if (event->is<sf::Event::Closed>())
		{
			window.close();
		}

		if (const auto* keyPressed = event->getIf<sf::Event::KeyPressed>())
		{
			switch (keyPressed->scancode)
			{
				case sf::Keyboard::Scan::Escape:
					window.close();
					break;

				case sf::Keyboard::Scan::Left:
				case sf::Keyboard::Scan::A:
					player.left = true;
					break;

				case sf::Keyboard::Scan::Right:
				case sf::Keyboard::Scan::D:
					player.right = true;
					break;

				case sf::Keyboard::Scan::Up:
				case sf::Keyboard::Scan::W:
					player.up = true;
					break;

				case sf::Keyboard::Scan::Down:
				case sf::Keyboard::Scan::S:
					player.down = true;
					break;

				case sf::Keyboard::Scan::R:
					resetGame();
					break;

				default:
					break;
			}
		}

		if (const auto* keyReleased = event->getIf<sf::Event::KeyReleased>())
		{
			switch (keyReleased->scancode)
			{
				case sf::Keyboard::Scan::Left:
				case sf::Keyboard::Scan::A:
					player.left = false;
					break;

				case sf::Keyboard::Scan::Right:
				case sf::Keyboard::Scan::D:
					player.right = false;
					break;

				case sf::Keyboard::Scan::Up:
				case sf::Keyboard::Scan::W:
					player.up = false;
					break;

				case sf::Keyboard::Scan::Down:
				case sf::Keyboard::Scan::S:
					player.down = false;
					break;

				default:
					break;
			}
		}
	}
}

void Game::collision()
{
	const size_t limit = player.body.size();
	for (size_t i = 1; i < limit; i++)
	{
		if (player.head().gridPos == player.body.at(i).gridPos)
		{
			fail = true;
			return;
		}
	}

	if (player.head().gridPos == fruit.gridPos)
	{
		player.body.emplace_back(player.body.back().previousGridPos);
		if (player.body.size() != (gridCount.x * gridCount.y))
		{
			//play sound
			fruit.respawn(player.body, gridCount);
		} 
		else 
		{
			//play win sound

		}
	}

}

void Game::movement()
{
	if (player.left and !player.right and (player.facing == Direction::up or player.facing == Direction::down))
	{
		player.inputBuffer = Direction::left;
	}

	if (player.right and !player.left and (player.facing == Direction::up or player.facing == Direction::down))
	{
		player.inputBuffer = Direction::right;
	}

	if (player.up and !player.down and (player.facing == Direction::left or player.facing == Direction::right))
	{
		player.inputBuffer = Direction::up;
	}

	if (player.down and !player.up and (player.facing == Direction::left or player.facing == Direction::right))
	{
		player.inputBuffer = Direction::down;
	}

	if (frameCount % config.tickRate != 0) return;

	SnakeNode& head = player.head();
	switch (player.inputBuffer)
	{
        case Direction::up:
			if (head.gridPos.y == 0) 
			{
				fail = true; 
				return;
			}

			head.previousGridPos = head.gridPos;
			head.gridPos.y -= 1;
			player.facing = Direction::up;
			// std::cout << "up\n";
			break;

        case Direction::down:
			if (head.gridPos.y == gridCount.y - 1) 
			{
				fail = true; 
				return;
			}

			head.previousGridPos = head.gridPos;
			head.gridPos.y += 1;
			player.facing = Direction::down;
			// std::cout << "down\n";
			break;

        case Direction::left:
			if (head.gridPos.x == 0) 
			{
				fail = true; 
				return;
			}

			head.previousGridPos = head.gridPos;
			head.gridPos.x -= 1;
			player.facing = Direction::left;
			// std::cout << "left\n";
			break;

        case Direction::right:
			if (head.gridPos.x == gridCount.x -1) 
			{
				fail = true; 
				return;
			}

			head.previousGridPos = head.gridPos;
			head.gridPos.x += 1;
			player.facing = Direction::right;
			// std::cout << "right\n";
			break;
	}

	const size_t limit = player.body.size();
	for (size_t i = 1; i < limit; i++)
	{
		player.body[i].previousGridPos = player.body[i].gridPos;
		player.body[i].gridPos = player.body[i-1].previousGridPos;
	}

}

void Game::imgui()
{
	ImGui::SetNextWindowCollapsed(true, ImGuiCond_Once);
	ImGui::SetNextWindowSize({490,375}, ImGuiCond_Once);
	ImGui::SetNextWindowPos({26,29}, ImGuiCond_Once);
	if(!ImGui::Begin("menu"))
	{
		ImGui::End();
		return;
	}
	ImGui::Text("Controls:");
	ImGui::Indent();
	ImGui::Text("A/D or arrow keys: move left and right");
	ImGui::Text("R: reset game");
	ImGui::Unindent();

	if (ImGui::SliderFloat("Game Volume", &config.volume, 0, 100, "%.1f"))
	{

	}


	ImGui::End();
}

void Game::render()
{
		window.clear();

		for (auto& node : player.body)
		{
			tempRect.setFillColor(node.color.sfml());
			tempRect.setPosition(node.windowPos((float)config.gridSize));
			window.draw(tempRect);
		}

		tempRect.setFillColor(fruit.color.sfml());
		tempRect.setPosition(fruit.windowPos((float)config.gridSize));
		window.draw(tempRect);

		score.setString("Score: " + std::to_string(player.score));

		window.draw(score);

		ImGui::SFML::Render(window);

		window.display();
}

void Game::soundSystem()
{

}

void Game::scoreSystem()
{

}

void Game::resetGame()
{
	player.body.clear();
	player.body.emplace_back(config.headGridStartPos);
	fail = false;
	player.facing = Direction::up;
}

void Game::failState()
{
	// if (failSound.getStatus() == sf::Sound::Status::Stopped)
	// {
	// 	resetGame();
	// 	return;
	// }
}

void Game::winState()
{

}

void Game::run()
{
	while(window.isOpen())
	{		
		ImGui::SFML::Update(window, clock.restart());

		frameCount++;

		input();

		if (!fail and !win)
		{
			movement();

			collision();

			soundSystem();

			scoreSystem();
		}
		else if (fail)
		{
			failState();
		}
		else if (win)
		{
			winState();
		}

		imgui();

		render();

	}

	ImGui::SFML::Shutdown();
}