#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

// Variables
Options options;

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

// Inicializa las opciones del programa
void initOptions()
{
	options.window.caption = "Coffee Crisis Arcade Edition";
	options.window.zoom = 2;

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

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

	// Opciones de juego
	options.game.difficulty = GameDifficulty::NORMAL;
	options.game.language = lang::Code::ba_BA;
	options.game.autofire = true;
	options.game.clear_last_hi_score_entries();

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

// Carga el fichero de configuración
bool loadOptionsFile(std::string file_path)
{
	// Inicializa las opciones del programa
	initOptions();

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

	// Variables para manejar el fichero
	std::ifstream file(file_path);

	// 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(file_path).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 (!setOptions(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
	{
		// Crea el fichero con los valores por defecto
		saveOptionsFile(file_path);
	}

	// Normaliza los valores
	if (options.game.language != lang::Code::en_UK &&
		options.game.language != lang::Code::ba_BA &&
		options.game.language != lang::Code::es_ES)
	{
		options.game.language = lang::Code::en_UK;
	}

	return success;
}

// Guarda el fichero de configuración
bool saveOptionsFile(std::string file_path)
{
	std::ofstream file(file_path);

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

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

	// 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=" << options.window.zoom << "\n";
	file << "video.fullscreen=" << boolToString(options.video.fullscreen) << "\n";
	file << "video.scale_mode=" << static_cast<int>(options.video.scale_mode) << "\n";
	file << "video.v_sync=" << boolToString(options.video.v_sync) << "\n";
	file << "video.integer_scale=" << boolToString(options.video.integer_scale) << "\n";
	file << "video.shaders=" << boolToString(options.video.shaders) << "\n";

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

	file << "audio.enabled=" << boolToString(options.audio.enabled) << "\n";
	file << "audio.volume=" << options.audio.volume << "\n";
	file << "audio.music.enabled=" << boolToString(options.audio.music.enabled) << "\n";
	file << "audio.music.volume=" << options.audio.music.volume << "\n";
	file << "audio.sound.enabled=" << boolToString(options.audio.sound.enabled) << "\n";
	file << "audio.sound.volume=" << options.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>(GameDifficulty::EASY) << ": easy, " << static_cast<int>(GameDifficulty::NORMAL) << ": normal, " << static_cast<int>(GameDifficulty::HARD) << ": hard]\n";
	file << "\n";

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

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

	int controller_index = 0;
	for (const auto &controller : options.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 setOptions(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")
	{
		options.video.fullscreen = stringToBool(value);
	}
	else if (var == "window.zoom")
	{
		options.window.zoom = std::stoi(value);
	}
	else if (var == "video.scale_mode")
	{
		options.video.scale_mode = static_cast<SDL_ScaleMode>(std::stoi(value));
	}
	else if (var == "video.shaders")
	{
		options.video.shaders = stringToBool(value);
	}
	else if (var == "video.integer_scale")
	{
		options.video.integer_scale = stringToBool(value);
	}
	else if (var == "video.v_sync")
	{
		options.video.v_sync = stringToBool(value);
	}

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

	// Opciones de juego
	else if (var == "game.language")
	{
		options.game.language = static_cast<lang::Code>(std::stoi(value));
	}
	else if (var == "game.difficulty")
	{
		options.game.difficulty = static_cast<GameDifficulty>(std::stoi(value));
	}
	else if (var == "game.autofire")
	{
		options.game.autofire = stringToBool(value);
	}

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

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

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

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