#include <MSFS/MSFS.h>
#include <MSFS/MSFS_CommBus.h>
#include <MSFS/Legacy/gauges.h>

#include <algorithm>
#include <iterator>
#include <string>
#include <vector>

#include <dirent.h> 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"

#include "FileSystem.h"
#include "Utils.h"

#include "Constants.h"

static void CommandWasmCallback(const char* args, unsigned int size, void* ctx);
static FILE* logFile;

extern "C"
{
	MSFS_CALLBACK void module_init(void)
	{
		khofmann::log(stdout, "Starting init\n");

		khofmann::logFile = fopen("/work/log.txt", "w");
		if (khofmann::logFile == NULL)
		{
			khofmann::log(stderr, "Error creating logfile\n");
		}

		if (!fsCommBusRegister(COMMANDS, CommandWasmCallback))
		{
			khofmann::log(stderr, "Error registering command CommBus\n");
		}

		khofmann::log(stdout, "Inited\n");
	}

	extern "C" MSFS_CALLBACK void module_deinit(void)
	{
		khofmann::log(stdout, "Starting deinit\n");

		int c = fsCommBusUnregister(COMMANDS, CommandWasmCallback);
		khofmann::log(stdout, "%i unregistered", &c);

		khofmann::log(stdout, "Deinited\n");

		fclose(khofmann::logFile);
	}

	static void CommandWasmCallback(const char* args, unsigned int size, void* ctx)
	{
		rapidjson::Document inDoc;
		inDoc.Parse(args);

		const char* cmd = inDoc["cmd"].GetString();

		rapidjson::Document outDoc;
		rapidjson::Document::AllocatorType& allocator = outDoc.GetAllocator();
		outDoc.SetObject();
		outDoc.AddMember("id", rapidjson::Value(cmd, allocator).Move(), allocator);

		rapidjson::StringBuffer strbuf;
		rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);

		if (strcmp(cmd, LIST) == 0)
		{
			std::string path;
			path += PACKAGE_DIR;
			path += inDoc["path"].GetString();

			rapidjson::Value files;
			files.SetArray();
			khofmann::enumerateDir(path.c_str(), files, allocator);
			outDoc.AddMember("data", files.Move(), allocator);

			outDoc.Accept(writer);

			fsCommBusCall(DATA, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS);
		}
		else if (strcmp(cmd, LOAD) == 0)
		{
			std::string filePath;
			filePath += PACKAGE_DIR;
			filePath += inDoc["file"].GetString();

			std::string marksPath;
			marksPath += inDoc["file"].GetString();
			size_t lastSlash = marksPath.find_last_of("/");
			if (lastSlash != std::string::npos)
			{
				marksPath.erase(lastSlash);
			}
			marksPath.erase(std::remove(marksPath.begin(), marksPath.end(), '/'), marksPath.end());
			marksPath = WORK_DIR + marksPath + "_bookmarks.json";

			rapidjson::Value data;
			data.SetObject();

			khofmann::log(stdout, "Loading file %s\n", filePath.c_str());
			data.AddMember("file", khofmann::readFile(filePath.c_str(), allocator).Move(), allocator);

			if (filePath.find("/1.bjpg") != std::string::npos)
			{
				khofmann::log(stdout, "Loading book marks %s\n", marksPath.c_str());
				rapidjson::Document marks;
				marks.Parse(khofmann::readFile(marksPath.c_str(), allocator).GetString());
				if (marks.IsArray())
				{
					data.AddMember("bookMarks", marks.Move(), allocator);
				}
				else
				{
					rapidjson::Value t;
					t.SetArray();
					data.AddMember("bookMarks", t.Move(), allocator);
				}
			}

			outDoc.AddMember("data", data.Move(), allocator);

			outDoc.Accept(writer);

			fsCommBusCall(DATA, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS);
		}
		else if (strcmp(cmd, SAVE) == 0)
		{
			std::string marksPath;
			marksPath += inDoc["path"].GetString();
			marksPath.erase(std::remove(marksPath.begin(), marksPath.end(), '/'), marksPath.end());
			marksPath = WORK_DIR + marksPath + "_bookmarks.json";

			khofmann::log(stdout, "Saving book marks %s\n", marksPath.c_str());
			khofmann::writeFile(marksPath.c_str(), inDoc["bookMarks"]);

			outDoc.Accept(writer);
			
			fsCommBusCall(DATA, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS);
		}
	}
}
