#include "options.h"
#include <SDL2/SDL_gamecontroller.h> // for SDL_GameControllerButton, SDL_C...
#include <algorithm>				 // for max, min
#include <fstream>					 // for basic_ostream, operator<<, basi...
#include <iostream>					 // for cout
#include <vector>					 // for vector
#include "input.h"					 // for inputs_e, INPUT_USE_ANY, INPUT_...
#include "lang.h"					 // for lang_e
#include "screen.h"					 // for ScreenVideoMode, ScreenFilter
#include "utils.h"					 // for OptionsController, Options, op_...

// Variables
Options options;

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

// Inicializa las opciones del programa
void initOptions()
{
	// Opciones de video
#ifdef ANBERNIC
	options.video.mode = ScreenVideoMode::WINDOW;
	options.video.window.size = 3;
#else
	options.video.mode = ScreenVideoMode::WINDOW;
	options.video.window.size = 2;
#endif
	options.video.filter = ScreenFilter::NEAREST;
	options.video.v_sync = true;
	options.video.shaders = true;

	// Opciones de las notificaciones
	options.notification.pos_v = NotifyPosition::TOP;
	options.notification.pos_h = NotifyPosition::LEFT;
	options.notification.sound = false;
	options.notification.color = (Color){48, 48, 48};

	// Opciones de audio
	options.audio.music.enabled = true;
	options.audio.music.volume = 128;
	options.audio.sound.enabled = true;
	options.audio.sound.volume = 64;

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

	// Opciones de control
	options.controller.clear();
	OptionsController c;

	constexpr int numPlayers = 2;
	for (int index = 0; index < numPlayers; ++index)
	{
		c.index = index;
		c.player_id = index + 1;
		c.device_type = INPUT_USE_GAMECONTROLLER;
		c.name = "NO NAME";
		c.plugged = false;

		// Inputs que se guardan en las opciones y, por tanto, a disco
		c.inputs.clear();
		c.inputs.push_back(input_fire_left);
		c.inputs.push_back(input_fire_center);
		c.inputs.push_back(input_fire_right);
		c.inputs.push_back(input_start);
		c.inputs.push_back(input_service);

		// Botones asociados a los inputs anteriores
		c.buttons.clear();
		c.buttons.push_back(SDL_CONTROLLER_BUTTON_X);
		c.buttons.push_back(SDL_CONTROLLER_BUTTON_Y);
		c.buttons.push_back(SDL_CONTROLLER_BUTTON_B);
		c.buttons.push_back(SDL_CONTROLLER_BUTTON_START);
		c.buttons.push_back(SDL_CONTROLLER_BUTTON_BACK);

		options.controller.push_back(c);
	}
	options.controller[0].device_type = INPUT_USE_ANY; // El primer jugador puede usar tanto el teclado como el primer mando
}

// 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
	const std::string fileName = file_path.substr(file_path.find_last_of("\\/") + 1);
	std::ifstream file(file_path);

	// Si el fichero se puede abrir
	if (file.good())
	{
		// Procesa el fichero linea a linea
#ifdef VERBOSE
		std::cout << "Reading file: " << fileName << std::endl;
#endif
		std::string line;
		while (std::getline(file, line))
		{
			// Comprueba que la linea no sea un comentario
			if (line.substr(0, 1) != "#")
			{
				// Encuentra la posición del caracter '='
				int pos = line.find("=");
				// Procesa las dos subcadenas
				if (!setOptions(line.substr(0, pos), line.substr(pos + 1, line.length())))
				{
#ifdef VERBOSE
					std::cout << "Warning: file " << fileName << std::endl;
					std::cout << "Unknown parameter " << line.substr(0, pos).c_str() << std::endl;
#endif
					success = false;
				}
			}
		}

		// Cierra el fichero
		file.close();
	}

	// El fichero no existe
	else
	{ // Crea el fichero con los valores por defecto
		saveOptionsFile(file_path);
	}

	// Normaliza los valores
	const bool a = options.video.mode == ScreenVideoMode::WINDOW;
	const bool b = options.video.mode == ScreenVideoMode::FULLSCREEN;
	if (!(a || b))
	{
		options.video.mode = ScreenVideoMode::WINDOW;
	}

	if (options.video.window.size < 1 || options.video.window.size > 4)
	{
		options.video.window.size = 3;
	}

	if (options.game.language < 0 || options.game.language > 2)
	{
		options.game.language = lang::en_UK;
	}

	return success;
}

// Guarda el fichero de configuración
bool saveOptionsFile(std::string file_path)
{
	const std::string fileName = file_path.substr(file_path.find_last_of("\\/") + 1);
	std::ofstream file(file_path);

	if (!file.good())
	{
#ifdef VERBOSE
		std::cout << fileName << " can't be opened" << std::endl;
#endif
		return false;
	}

#ifdef VERBOSE
	std::cout << "Writing file: " << fileName << std::endl;
#endif

	// Opciones de video
	const auto value_video_mode_winow = std::to_string(static_cast<int>(ScreenVideoMode::WINDOW));
	const auto value_video_mode_fullscreen = std::to_string(static_cast<int>(ScreenVideoMode::FULLSCREEN));
	const auto value_filter_nearest = std::to_string(static_cast<int>(ScreenFilter::NEAREST));
	const auto value_filter_lineal = std::to_string(static_cast<int>(ScreenFilter::LINEAL));

	file << "## VIDEO\n";
	file << "## video.mode [" << value_video_mode_winow << ": window, " << value_video_mode_fullscreen << ": fullscreen]\n";
	file << "## video.filter [" << value_filter_nearest << ": nearest, " << value_filter_lineal << ": lineal]\n";
	file << "\n";

	const auto valueVideoMode = std::to_string(static_cast<int>(options.video.mode));
	file << "video.mode=" << valueVideoMode << "\n";

	file << "video.window.size=" + std::to_string(options.video.window.size) + "\n";

	const auto valueFilter = std::to_string(static_cast<int>(options.video.filter));
	file << "video.filter=" << valueFilter << "\n";

	file << "video.shaders=" + boolToString(options.video.shaders) + "\n";
	file << "video.v_sync=" + boolToString(options.video.v_sync) + "\n";

	// Opciones de notificaciones
	file << "\n\n## NOTIFICATION\n";
	file << "## notification.pos_v [TOP | BOTTOM]\n";
	file << "## notification.pos_h [LEFT | MIDDLE | RIGHT]\n";
	file << "\n";

	if (options.notification.pos_v == NotifyPosition::TOP)
	{
		file << "notification.pos_v=TOP\n";
	}

	else
	{
		file << "notification.pos_v=BOTTOM\n";
	}

	if (options.notification.pos_h == NotifyPosition::LEFT)
	{
		file << "notification.pos_h=LEFT\n";
	}

	else if (options.notification.pos_h == NotifyPosition::MIDDLE)
	{
		file << "notification.posH=MIDDLE\n";
	}

	else
	{
		file << "notification.pos_h=RIGHT\n";
	}

	file << "notification.sound=" + boolToString(options.notification.sound) + "\n";

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

	file << "audio.music.enabled=" + boolToString(options.audio.music.enabled) + "\n";
	file << "audio.music.volume=" + std::to_string(options.audio.music.volume) + "\n";
	file << "audio.sound.enabled=" + boolToString(options.audio.sound.enabled) + "\n";
	file << "audio.sound.volume=" + std::to_string(options.audio.sound.volume) + "\n";

	// Opciones del juego
	const auto value_difficulty_easy = std::to_string(static_cast<int>(GameDifficulty::EASY));
	const auto value_difficulty_normal = std::to_string(static_cast<int>(GameDifficulty::NORMAL));
	const auto value_difficulty_hard = std::to_string(static_cast<int>(GameDifficulty::HARD));
	file << "\n\n## GAME\n";
	file << "## game.language [0: spanish, 1: valencian, 2: english]\n";
	file << "## game.difficulty [" << value_difficulty_easy << ": easy, " << value_difficulty_normal << ": normal, " << value_difficulty_hard << ": hard]\n";
	file << "\n";

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

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

	const int numPlayers = 2;
	for (int index = 0; index < numPlayers; ++index)
	{
		const std::string joyIndex = std::to_string(index + 1);
		file << "controller" + joyIndex + ".name=" + options.controller[index].name + "\n";
		file << "controller" + joyIndex + ".player=" + std::to_string(options.controller[index].player_id) + "\n";
		file << "controller" + joyIndex + ".button.fire_left=" + std::to_string((int)options.controller[index].buttons[0]) + "\n";
		file << "controller" + joyIndex + ".button.fire_center=" + std::to_string((int)options.controller[index].buttons[1]) + "\n";
		file << "controller" + joyIndex + ".button.fire_right=" + std::to_string((int)options.controller[index].buttons[2]) + "\n";
		file << "controller" + joyIndex + ".button.start=" + std::to_string((int)options.controller[index].buttons[3]) + "\n";
		file << "controller" + joyIndex + ".button.service=" + std::to_string((int)options.controller[index].buttons[4]) + "\n";

		if (index < numPlayers - 1)
		{
			file << "\n";
		}
	}

	// Cierra el fichero
	file.close();

	return true;
}

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

	// Opciones de video
	if (var == "video.mode")
	{
		// options.video.mode = value == std::to_string(static_cast<int>(ScreenVideoMode::WINDOW)) ? ScreenVideoMode::WINDOW : ScreenVideoMode::FULLSCREEN;
		options.video.mode = static_cast<ScreenVideoMode>(std::stoi(value));
	}

	else if (var == "video.window.size")
	{
		options.video.window.size = std::stoi(value);
		if ((options.video.window.size < 1) || (options.video.window.size > 4))
		{
			options.video.window.size = 3;
		}
	}

	else if (var == "video.filter")
	{
		// options.video.filter = value == std::to_string(static_cast<int>(ScreenFilter::NEAREST)) ? ScreenFilter::NEAREST : ScreenFilter::LINEAL;
		options.video.filter = static_cast<ScreenFilter>(std::stoi(value));
	}

	else if (var == "video.shaders")
	{
		options.video.shaders = stringToBool(value);
	}

	else if (var == "video.v_sync")
	{
		options.video.v_sync = stringToBool(value);
	}

	// Opciones de notificaciones
	else if (var == "notification.pos_h")
	{
		if (value == "LEFT")
		{
			options.notification.pos_h = NotifyPosition::LEFT;
		}
		else if (value == "MIDDLE")
		{
			options.notification.pos_h = NotifyPosition::MIDDLE;
		}
		else
		{
			options.notification.pos_h = NotifyPosition::RIGHT;
		}
	}

	else if (var == "notification.pos_v")
	{
		options.notification.pos_v = value == "TOP" ? NotifyPosition::TOP : NotifyPosition::BOTTOM;
	}

	else if (var == "notification.sound")
	{
		options.notification.sound = stringToBool(value);
	}

	// Opciones de audio
	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 = 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 == "controller1.name")
	{
		options.controller[0].name = value;
	}

	else if (var == "controller1.player")
	{
		options.controller[0].player_id = std::max(1, std::min(2, std::stoi(value)));
	}

	else if (var == "controller1.button.fire_left")
	{
		options.controller[0].buttons[0] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller1.button.fire_center")
	{
		options.controller[0].buttons[1] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller1.button.fire_right")
	{
		options.controller[0].buttons[2] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller1.button.start")
	{
		options.controller[0].buttons[3] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller1.button.service")
	{
		options.controller[0].buttons[4] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller2.name")
	{
		options.controller[1].name = value;
	}

	else if (var == "controller2.player")
	{
		options.controller[1].player_id = std::max(1, std::min(2, std::stoi(value)));
	}

	else if (var == "controller2.button.fire_left")
	{
		options.controller[1].buttons[0] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller2.button.fire_center")
	{
		options.controller[1].buttons[1] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller2.button.fire_right")
	{
		options.controller[1].buttons[2] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller2.button.start")
	{
		options.controller[1].buttons[3] = (SDL_GameControllerButton)std::stoi(value);
	}

	else if (var == "controller2.button.service")
	{
		options.controller[1].buttons[4] = (SDL_GameControllerButton)std::stoi(value);
	}

	// Lineas vacias o que empiezan por comentario
	else if (var == "" || var.substr(0, 1) == "#")
	{
	}

	else
	{
		success = false;
	}

	return success;
}