
#include <raylib.h>
#include "MPFW_Console.h"
#include <string>
#include <fstream>
#include <sstream>



MPFW_ConsoleCommand(echo) {
	for (int i = 0; i < cmd.size(); i++) {
		*logStr += std::format("{} ", cmd[i]);
	}
	*logStr += "\n";
}

MPFW_ConsoleCommand(ui_create_window) {
	if(cmd.size() != 1){
		*logStr += "'ui_create_window' command usage:\n     ui_create_window <path to window file>\n";
		return;
	}
	try{
		MPFW::UI::Window wnd(cmd[0]);
		ctx->ui->windows.push_back(wnd);
	}
	catch (std::exception& e) {
		*logStr += "Error on window creation: ";
		*logStr += e.what();
		*logStr += "\n";

	}
	catch (...) {
		*logStr += "Unknown error on window creation\n";
	}
}

MPFW_ConsoleCommand(ui_show) {
	ctx->ui->rendererIsActive = true;
}
MPFW_ConsoleCommand(ui_hide) {
	ctx->ui->rendererIsActive = false;
}

MPFW_ConsoleCommand(ui_delete_window) {
	if (cmd.size() != 1) {
	}
}

MPFW_ConsoleCommand(quit) {
	exit(0); // some day there'll be proper unloading :3
}

MPFW_ConsoleCommand(mode) {
	if (cmd.size() != 1) {
		*logStr += "'mode' command usage:\n     mode <0/1>\n\nModes:\n 0 - MPFW (default)\n 1 - Quake\n";
	}
	else {
		try {
			int newMode = std::stoi(cmd[0]);
			if (newMode > 1 || newMode < 0) throw std::out_of_range("not a mode");
			ctx->mode = (MPFW::Console::OperationMode)newMode;
			*logStr += "Mode is set to '" + std::to_string(newMode) + "'\n";
		}
		catch(...){
			*logStr += "'mode' command usage:\n     mode <0/1>\n\nModes:\n 0 - MPFW (default)\n 1 - Quake\n";
		}
	}
}

MPFW_ConsoleCommand(map_quake) {

	if (cmd.size() != 1) {
		*logStr += "'map' command usage:\n     map <path>\n";
	}
	else {
		try {
			ctx->mapQuake->LoadBSPMap(cmd[0]);
			*logStr += std::format("{}", ctx->mapQuake->data);
		}
		catch (...) {
			*logStr += "Couldn't load map '" + cmd[0] + "'\n";
		}
	}
}

MPFW_ConsoleCommand(map_mpfw) {

	*logStr += "Mode MPFW has no \"map\" command because it has no available map format.\n\nUse one of these modes instead:\n  - 'mode 1' : QUAKE\n\n";
}

MPFW_ConsoleCommand(disable_game) {
	ctx->gameDisabled = true;
}
MPFW_ConsoleCommand(enable_game) {
	ctx->gameDisabled = false;
}
MPFW_ConsoleCommand(set) {
	if (cmd.size() == 2) {
		ctx->cvars[cmd[0]] = cmd[1];
	}
}

MPFW_ConsoleCommand(show_cursor) {
	ctx->ui->showCursorOnLaunch = true;
	EnableCursor();
}

MPFW_ConsoleCommand(hide_cursor) {
	ctx->ui->showCursorOnLaunch = false;
	DisableCursor();
}

MPFW::Console::CommandHandler::CommandHandler(CommandHandlerResources* c)
{

	chr = c;
	

	chr->cvars.clear();

	std::ifstream cvarFile("data/sys/cvardefs.cvars");
	std::string cvarLine;
	while (std::getline(cvarFile, cvarLine)) {
		std::vector<std::string> line = parseCommand(cvarLine);
		chr->cvars[line[0]] = line[1];
	}




#pragma region
	

	   // System commands are defined manually à la Jason Thor Hall™
	  // but that's to make absolutely f*cking sure that these
	 // elemental commands work. less important commands will be
	// defined with a definition instantiator or some random shit

	CommandID mpfwSet = { MPFW, "set" };
	CommandID quakeSet = { QUAKE, "set" };
	functionMap[mpfwSet] = set;
	functionMap[quakeSet] = set;


	CommandID mpfwEcho = { MPFW, "echo" };
	CommandID quakeEcho = { QUAKE, "echo" };
	functionMap[mpfwEcho] = echo;
	functionMap[quakeEcho] = echo;


	CommandID mpfwQuit = { MPFW, "quit" };
	CommandID quakeQuit = { QUAKE, "quit" };
	functionMap[mpfwQuit] = quit;
	functionMap[quakeQuit] = quit;


	CommandID mpfwMode = { MPFW, "mode" };
	CommandID quakeMode = { QUAKE, "mode" };
	functionMap[mpfwMode] = mode;
	functionMap[quakeMode] = mode;

	CommandID mpfwMap = { MPFW, "map" };
	functionMap[mpfwMap] = map_mpfw;

	CommandID quakeMap = { QUAKE, "map" };
	functionMap[quakeMap] = map_quake;

	CommandID uiCreateWindow = { MPFW, "ui_create_window" };
	functionMap[uiCreateWindow] = ui_create_window;

	CommandID uiShow = { MPFW, "ui_show" };
	functionMap[uiShow] = ui_show;
	CommandID uiHide = { MPFW, "ui_hide" };
	functionMap[uiHide] = ui_hide;


	CommandID mpfwShowCursor = { MPFW, "show_cursor" };
	functionMap[mpfwShowCursor] = show_cursor;

	CommandID mpfwHideCursor = { MPFW, "hide_cursor" };
	functionMap[mpfwHideCursor] = hide_cursor;


	CommandID mpfwEnableGame = { MPFW, "enable_game" };
	functionMap[mpfwEnableGame] = enable_game;

	CommandID mpfwDisableGame = { MPFW, "disable_game" };
	functionMap[mpfwDisableGame] = disable_game;
	


#pragma endregion System Commands
}

std::vector<std::string> MPFW::Console::CommandHandler::parseCommand(std::string s)
{


	bool inQString = false;


	std::vector<std::string> retval;

	std::string cstr; // current string
	for (int i = 0; i < s.size(); i++) {
		if (s[i] == '"') {
			inQString = !inQString;
			if (!inQString) {
				retval.push_back(cstr);
				cstr = "";
			}
		}
		else {
			if (!inQString && s[i] == ' ') {
				retval.push_back(cstr);
				cstr = "";
			}
			else cstr += s[i];
		}
	}

	if(cstr != "") retval.push_back(cstr);

	return retval;

}

void MPFW::Console::CommandHandler::Run(std::string command, bool verbose)
{
	if(verbose) logStr += "\n" + std::string(chr->mode == QUAKE ? "QUAKE" : "MPFW") + "] " + command + "\n";
	std::vector<std::string> cmdParseResult = parseCommand(command);
	CommandID id;
	if (cmdParseResult.size() > 0){
		id.n = cmdParseResult[0];
		cmdParseResult.erase(cmdParseResult.begin(), cmdParseResult.begin() + 1);

		id.m = chr->mode;

		if (functionMap.find(id) != functionMap.end()) {
			functionMap[id](cmdParseResult, chr, &logStr);
		}
		else {
			logStr += "Unknown command " + id.n + " in mode " + (chr->mode == QUAKE ? "QUAKE" : "MPFW") + "\n";
		}

	}
	
	
}

void MPFW::Console::CommandHandler::RunScript(std::string path)
{
	std::ifstream i(path);
	if (i.good() && i.is_open()) {
		std::string line;

		while (std::getline(i, line)) {
			Run(line, false);
		}
	}
	else {
		throw std::runtime_error("Can't run called script " + path);
	}
}

MPFW::Console::CommandHandler::~CommandHandler()
{
}
