#include "options.h"
#include <SDL3/SDL_log.h> // Para SDL_LogCategory, SDL_LogInfo, SDL_LogError
#include <algorithm>	  // Para clamp
#include <fstream>		  // Para basic_ostream, operator<<, basic_ostream::...
#include <utility>		  // Para swap
#include <vector>		  // Para vector
#include "input.h"		  // Para InputDeviceToUse
#include "lang.h"		  // Para Code
#include "utils.h"		  // Para boolToString, stringToBool, getFileName
#include "asset.h"

namespace Options
{
	// --- Variables globales ---
	WindowOptions window;					 // Opciones de la ventana
	SettingsOptions settings;				 // Opciones del juego
	VideoOptions video;						 // Opciones de vídeo
	AudioOptions audio;						 // Opciones de audio
	std::vector<GamepadOptions> controllers; // Opciones de mando para cada jugador
	PendingChanges pending_changes;			 // Opciones que se aplican al cerrar

	// Vector con las dificultades
	std::vector<Difficulty> difficulties = {
		{DifficultyCode::EASY, "Easy"},
		{DifficultyCode::NORMAL, "Normal"},
		{DifficultyCode::HARD, "Hard"}};

	// Declaraciones
	bool set(const std::string &var, const std::string &value);

	// Inicializa las opciones del programa
	void init()
	{
		// Opciones de ventana
		window.caption = "Coffee Crisis Arcade Edition";
		window.size = 2;

		// Opciones de video
		video.fullscreen = false;
		video.scale_mode = SDL_ScaleMode::SDL_SCALEMODE_NEAREST;
		video.v_sync = true;
		video.integer_scale = true;
		video.shaders = false;

		// Opciones de audio
		audio.enabled = true;
		audio.volume = 100;
		audio.music.enabled = true;
		audio.music.volume = 100;
		audio.sound.enabled = true;
		audio.sound.volume = 50;

		// Opciones de configuracion
		settings.difficulty = DifficultyCode::NORMAL;
		settings.language = Lang::Code::VALENCIAN;
		settings.autofire = true;
		settings.shutdown_enabled = false;
		settings.clearLastHiScoreEntries();
		settings.config_file = Asset::get()->get("config.txt");

		// Opciones de control
		controllers.clear();
		controllers.resize(2);
		controllers.at(0).player_id = 1;
		controllers.at(1).player_id = 2;
		setKeyboardToPlayer(1);

		// Opciones pendientes
		pending_changes.new_language = settings.language;
		pending_changes.new_difficulty = settings.difficulty;
		pending_changes.has_pending_changes = false;
	}

	// Carga el fichero de configuración
	bool loadFromFile()
	{
		// Inicializa las opciones del programa
		init();

		// Indicador de éxito en la carga
		bool success = true;

		// Variables para manejar el fichero
		std::ifstream file(settings.config_file);

		// Si el fichero se puede abrir
		if (file.good())
		{
			// Procesa el fichero línea a línea
			SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "\nReading file: %s", getFileName(settings.config_file).c_str());
			std::string line;
			while (std::getline(file, line))
			{
				// Comprueba que la línea no sea un comentario
				if (line.substr(0, 1) != "#")
				{
					// Encuentra la posición del carácter '='
					int pos = line.find("=");
					// Procesa las dos subcadenas
					if (!set(line.substr(0, pos), line.substr(pos + 1, line.length())))
					{
						SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Unknown parameter: %s", line.substr(0, pos).c_str());
						success = false;
					}
				}
			}
			file.close();
		}
		// El fichero no existe
		else
		{
			saveToFile(); // Crea el fichero con los valores por defecto
		}

		// Normaliza los valores
		if (settings.language != Lang::Code::ENGLISH &&
			settings.language != Lang::Code::VALENCIAN &&
			settings.language != Lang::Code::SPANISH)
		{
			settings.language = Lang::Code::ENGLISH;
		}

		return success;
	}

	// Guarda el fichero de configuración
	bool saveToFile()
	{
		std::ofstream file(settings.config_file);

		if (!file.good())
		{
			SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Error: %s can't be opened", getFileName(settings.config_file).c_str());
			return false;
		}

		SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Writing file: %s", getFileName(settings.config_file).c_str());

		applyPendingChanges();

		// Opciones de video
		file << "## VIDEO\n";
		file << "## video.scale_mode [" << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_NEAREST) << ": nearest, " << static_cast<int>(SDL_ScaleMode::SDL_SCALEMODE_LINEAR) << ": lineal]\n";
		file << "\n";

		file << "window.zoom=" << window.size << "\n";
		file << "video.fullscreen=" << boolToString(video.fullscreen) << "\n";
		file << "video.scale_mode=" << static_cast<int>(video.scale_mode) << "\n";
		file << "video.v_sync=" << boolToString(video.v_sync) << "\n";
		file << "video.integer_scale=" << boolToString(video.integer_scale) << "\n";
		file << "video.shaders=" << boolToString(video.shaders) << "\n";

		// Opciones de audio
		file << "\n\n## AUDIO\n";
		file << "## volume [0 .. 100]\n";
		file << "\n";

		file << "audio.enabled=" << boolToString(audio.enabled) << "\n";
		file << "audio.volume=" << audio.volume << "\n";
		file << "audio.music.enabled=" << boolToString(audio.music.enabled) << "\n";
		file << "audio.music.volume=" << audio.music.volume << "\n";
		file << "audio.sound.enabled=" << boolToString(audio.sound.enabled) << "\n";
		file << "audio.sound.volume=" << audio.sound.volume << "\n";

		// Opciones del juego
		file << "\n\n## GAME\n";
		file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
		file << "## game.difficulty [" << static_cast<int>(DifficultyCode::EASY) << ": easy, " << static_cast<int>(DifficultyCode::NORMAL) << ": normal, " << static_cast<int>(DifficultyCode::HARD) << ": hard]\n";
		file << "\n";

		file << "game.language=" << static_cast<int>(settings.language) << "\n";
		file << "game.difficulty=" << static_cast<int>(settings.difficulty) << "\n";
		file << "game.autofire=" << boolToString(settings.autofire) << "\n";
		file << "game.shutdown_enabled=" << boolToString(settings.shutdown_enabled) << "\n";

		// Opciones de mandos
		file << "\n\n## CONTROLLERS\n";

		int controller_index = 0;
		for (const auto &controller : controllers)
		{
			file << "\n";
			file << "controller." << controller_index << ".name=" << controller.name << "\n";
			file << "controller." << controller_index << ".player=" << controller.player_id << "\n";
			file << "controller." << controller_index << ".type=" << static_cast<int>(controller.type) << "\n";
			file << "controller." << controller_index << ".button.fire_left=" << controller.buttons.at(0) << "\n";
			file << "controller." << controller_index << ".button.fire_center=" << controller.buttons.at(1) << "\n";
			file << "controller." << controller_index << ".button.fire_right=" << controller.buttons.at(2) << "\n";
			file << "controller." << controller_index << ".button.start=" << controller.buttons.at(3) << "\n";
			file << "controller." << controller_index << ".button.service=" << controller.buttons.at(4) << "\n";

			// Incrementa el índice
			++controller_index;
		}

		// Cierra el fichero
		file.close();

		return true;
	}

	// Asigna variables a partir de dos cadenas
	bool set(const std::string &var, const std::string &value)
	{
		// Indicador de éxito en la asignación
		auto success = true;

		// Opciones de video
		if (var == "video.fullscreen")
		{
			video.fullscreen = stringToBool(value);
		}
		else if (var == "window.zoom")
		{
			window.size = std::stoi(value);
		}
		else if (var == "video.scale_mode")
		{
			video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value));
		}
		else if (var == "video.shaders")
		{
			video.shaders = stringToBool(value);
		}
		else if (var == "video.integer_scale")
		{
			video.integer_scale = stringToBool(value);
		}
		else if (var == "video.v_sync")
		{
			video.v_sync = stringToBool(value);
		}

		// Opciones de audio
		else if (var == "audio.enabled")
		{
			audio.enabled = stringToBool(value);
		}
		else if (var == "audio.volume")
		{
			audio.volume = std::clamp(std::stoi(value), 0, 100);
		}
		else if (var == "audio.music.enabled")
		{
			audio.music.enabled = stringToBool(value);
		}
		else if (var == "audio.music.volume")
		{
			audio.music.volume = std::clamp(std::stoi(value), 0, 100);
		}
		else if (var == "audio.sound.enabled")
		{
			audio.sound.enabled = stringToBool(value);
		}
		else if (var == "audio.sound.volume")
		{
			audio.sound.volume = std::clamp(std::stoi(value), 0, 100);
		}

		// Opciones de juego
		else if (var == "game.language")
		{
			settings.language = static_cast<Lang::Code>(std::stoi(value));
			pending_changes.new_language = settings.language;
		}
		else if (var == "game.difficulty")
		{
			settings.difficulty = static_cast<DifficultyCode>(std::stoi(value));
			pending_changes.new_difficulty = settings.difficulty;
		}
		else if (var == "game.autofire")
		{
			settings.autofire = stringToBool(value);
		}
		else if (var == "game.shutdown_enabled")
		{
			settings.shutdown_enabled = stringToBool(value);
		}

		// Opciones de mandos
		else if (var == "controller.0.name")
		{
			controllers.at(0).name = value;
		}
		else if (var == "controller.0.player")
		{
			controllers.at(0).player_id = std::clamp(std::stoi(value), 1, 2);
		}
		else if (var == "controller.0.type")
		{
			controllers.at(0).type = static_cast<InputDeviceToUse>(std::stoi(value));
		}
		else if (var == "controller.0.button.fire_left")
		{
			controllers.at(0).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.0.button.fire_center")
		{
			controllers.at(0).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.0.button.fire_right")
		{
			controllers.at(0).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.0.button.start")
		{
			controllers.at(0).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.0.button.service")
		{
			controllers.at(0).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.1.name")
		{
			controllers.at(1).name = value;
		}
		else if (var == "controller.1.player")
		{
			controllers.at(1).player_id = std::clamp(std::stoi(value), 1, 2);
		}
		else if (var == "controller.1.type")
		{
			controllers.at(1).type = static_cast<InputDeviceToUse>(std::stoi(value));
		}
		else if (var == "controller.1.button.fire_left")
		{
			controllers.at(1).buttons.at(0) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.1.button.fire_center")
		{
			controllers.at(1).buttons.at(1) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.1.button.fire_right")
		{
			controllers.at(1).buttons.at(2) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.1.button.start")
		{
			controllers.at(1).buttons.at(3) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}
		else if (var == "controller.1.button.service")
		{
			controllers.at(1).buttons.at(4) = static_cast<SDL_GamepadButton>(std::stoi(value));
		}

		// Lineas vacias o que empiezan por comentario
		else if (var.empty() || var.starts_with("#"))
		{
		}
		else
		{
			success = false;
		}

		return success;
	}

	// Asigna el teclado al jugador
	void setKeyboardToPlayer(int player_id)
	{
		for (auto &controller : controllers)
		{
			if (controller.player_id == player_id)
			{
				controller.type = InputDeviceToUse::ANY;
			}
			else
			{
				controller.type = InputDeviceToUse::CONTROLLER;
			}
		}
	}

	// Intercambia el teclado de jugador
	void swapKeyboard()
	{
		std::swap(controllers.at(0).type, controllers.at(1).type);
	}

	// Intercambia los jugadores asignados a los dos primeros mandos
	void swapControllers()
	{
		std::swap(controllers.at(0).player_id, controllers.at(1).player_id);
		std::swap(controllers.at(0).type, controllers.at(1).type);
	}

	// Averigua quien está usando el teclado
	int getPlayerWhoUsesKeyboard()
	{
		for (const auto &controller : controllers)
		{
			if (controller.type == InputDeviceToUse::ANY)
			{
				return controller.player_id;
			}
		}
		return 0;
	}

	// Aplica los cambios pendientes copiando los valores a sus variables
	void applyPendingChanges()
	{
		if (pending_changes.has_pending_changes)
		{
			settings.language = pending_changes.new_language;
			settings.difficulty = pending_changes.new_difficulty;
			pending_changes.has_pending_changes = false;
		}
	}

	void checkPendingChanges()
	{
		if (settings.language != pending_changes.new_language ||
			settings.difficulty != pending_changes.new_difficulty)
		{
			pending_changes.has_pending_changes = true;
		}
		else
		{
			pending_changes.has_pending_changes = false;
		}
	}

	DifficultyCode getDifficultyCodeFromName(const std::string &name)
	{
		for (const auto &difficulty : difficulties)
		{
			if (difficulty.name == name)
				return difficulty.code;
		}
		// Si no se encuentra, devuelve el primero por defecto
		return difficulties[0].code;
	}

	std::string getDifficultyNameFromCode(DifficultyCode code)
	{
		for (const auto &difficulty : difficulties)
		{
			if (difficulty.code == code)
				return difficulty.name;
		}
		// Si no se encuentra, devuelve el nombre del primero por defecto
		return difficulties[0].name;
	}
} // namespace Options