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

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

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

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

#include "Base64.hpp"
#include "Module.h"

#define COMMANDS "KHOFMANN_PDF_READER_COMMANDS"
#define DATA "KHOFMANN_PDF_READER_DATA"

FILE* logFile;

static void CommandWasmCallback(const char* args, unsigned int size, void* ctx);
void log(FILE *file, const char *format, void* optionalElement = NULL);
std::string readFile(const char* path);

extern "C" MSFS_CALLBACK void module_init(void)
{
	log(stdout, "[PDF-Reader Module] Starting init\n");

	logFile = fopen("\\work\\log.txt", "w");
	if (logFile == NULL)
	{
		log(stderr, "[PDF-Reader Module] Error creating logfile\n");
	}

	if (!fsCommBusRegister(COMMANDS, CommandWasmCallback))
	{
		log(stderr, "[PDF-Reader Module] Error registering command CommBus\n");
	}

	log(stdout, "[PDF-Reader Module] Inited\n");
}

extern "C" MSFS_CALLBACK void module_deinit(void)
{
	log(stdout, "[PDF-Reader Module] Starting deinit\n");

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

	log(stdout, "[PDF-Reader Module] Deinited\n");
}

static void CommandWasmCallback(const char* args, unsigned int size, void* ctx)
{
	if (strcmp(args, "LIST") == 0)
	{
		rapidjson::Document jsonDoc;
		rapidjson::Document::AllocatorType& allocator = jsonDoc.GetAllocator();

		jsonDoc.SetObject();
		jsonDoc.AddMember("id", "LIST", allocator);

		rapidjson::Value files;
		files.SetArray();

		DIR* d;
		struct dirent* dir;
		d = opendir("\\work\\Files");
		if (d)
		{
			log(stdout, "Found files:\n");
			while ((dir = readdir(d)) != NULL)
			{
				if (dir->d_type == DT_DIR && strcmp(dir->d_name, ".") && strcmp(dir->d_name, ".."))
				{
					log(stdout, "%s\n", dir->d_name);

					std::string path("\\work\\Files\\");
					path += dir->d_name;

					int count = 0;
					DIR* s;
					struct dirent* dir_s;
					s = opendir(path.c_str());
					if (s)
					{
						while ((dir_s = readdir(s)) != NULL)
						{
							if (dir_s->d_type == DT_REG)
							{
								count++;
							}
						}

					}
					closedir(s);
					
					std::string thumb(dir->d_name);
					thumb += "\\thumb.png";

					rapidjson::Value name(dir->d_name, allocator);
					rapidjson::Value _thumb(readFile(thumb.c_str()).c_str(), allocator);
					
					rapidjson::Value entry;
					entry.SetObject();
					entry.AddMember("name", name.Move(), allocator);
					entry.AddMember("thumb", _thumb.Move(), allocator);
					entry.AddMember("pages", count - 1, allocator);

					files.PushBack(entry.Move(), allocator);
				}
			}

			jsonDoc.AddMember("data", files, allocator);

			rapidjson::StringBuffer strbuf;
			rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
			jsonDoc.Accept(writer);

			fsCommBusCall(DATA, strbuf.GetString(), strbuf.GetSize(), FsCommBusBroadcast_JS);

			closedir(d);
		}
	}
	else
	{
		rapidjson::Document inDoc;
		inDoc.Parse(args);

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

		if (strcmp(cmd, "LOAD") == 0) {
			const char* data = inDoc["file"].GetString();

			log(stdout, "Loading file %s\n", (void*)data);

			rapidjson::Document outDoc;
			rapidjson::Document::AllocatorType& allocator = outDoc.GetAllocator();

			outDoc.SetObject();
			outDoc.AddMember("id", "LOAD", allocator);
			outDoc.AddMember("data", rapidjson::Value(readFile(data).c_str(), allocator).Move(), allocator);

			rapidjson::StringBuffer strbuf;
			rapidjson::Writer<rapidjson::StringBuffer> writer(strbuf);
			outDoc.Accept(writer);

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

std::string readFile(const char* path)
{
	std::string _path;
	_path += "\\work\\Files\\";
	_path += path;

	FILE* file = fopen(_path.c_str(), "r");
	if (file) {
		fseek(file, 0, SEEK_END);
		long fsize = ftell(file);
		fseek(file, 0, SEEK_SET);  /* same as rewind(f); */

		char* string = (char*)calloc(sizeof(char), fsize + 1);
		fread(string, fsize, 1, file);
		fclose(file);

		return macaron::Base64::Encode(string, fsize);
	}

	return "";
}

void log(FILE* file, const char* format, void* optionalElement)
{
	if (logFile != NULL)
	{
		fprintf(logFile, format, optionalElement);
		fflush(logFile);
	}
	fprintf(file, format, optionalElement);
}