#include "Util.h"
#include "EventListener.h"
#include "Papyrus.h"
#include "Patches/MainMenuPatch.h"
#include "Patches/FlatMapMarkers.h"
#include "Patches/StayAtSystemPage.h"
#include "Patches/MapMarkerPlacement.h"
#include "Patches/AchievementFix.h"
#include "Patches/BinkInterruptPatch.h"
#include "Patches/DialogueMenuPatch.h"
#include "Patches/TweenMenuPatch.h"
#include "Patches/HeroMenuPatch.h"
#include "Patches/HUDMenuPatch.h"
#include "Patches/ForceBorderless.h"
#include "Patches/AttachLightHitEffectCrash.h"

using namespace SKSE;

static std::map<std::string, bool> g_settings{
	{ "FlatMapMarkers", true },
	{ "StayAtSystemPage", true },
	{ "MapMarkerPlacementFixes", true },
	{ "AchievementFix", true },
	{ "VideoInterruptPatch", true },
	{ "ForceBorderless", true },
	{ "AttachLightHitEffectCrashFix", true }
};

namespace {

	void InitializeLogging() {
		wchar_t* buffer{ nullptr };
		const auto result = ::SHGetKnownFolderPath(::FOLDERID_Documents, ::KNOWN_FOLDER_FLAG::KF_FLAG_DEFAULT, nullptr, std::addressof(buffer));
		std::unique_ptr<wchar_t[], decltype(&::CoTaskMemFree)> knownPath(buffer, ::CoTaskMemFree);

		if (!knownPath || result != S_OK) {
			logger::error("failed to get known folder path"sv);
			return;
		}

		std::filesystem::path path = knownPath.get();
		path /= "My Games"sv;
		if (GetModuleHandle(L"Galaxy64.dll") != NULL) {
			path /= "Skyrim Special Edition GOG"sv;
		} else {
			path /= "Skyrim Special Edition"sv;
		}
		path /= "SKSE"sv;
		path /= "EnderalSE.log"sv;

		auto sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.string(), true);
		auto log = std::make_shared<spdlog::logger>("global log"s, std::move(sink));

		log->set_level(spdlog::level::info);
		log->flush_on(spdlog::level::info);

		spdlog::set_default_logger(std::move(log));
		spdlog::set_pattern("[%l] %v"s);
    }

    void InitializeMessaging()
	{
		GetMessagingInterface()->RegisterListener([](MessagingInterface::Message* message) {
			
			if (message->type == MessagingInterface::kPostLoad) {
				if (!REL::Module::IsVR()) {
					if (g_settings.at("AttachLightHitEffectCrashFix")) {
						logger::info("Installing light attach crash fix...");
						AttachLightHitEffectCrash::Install();
					}
					if (g_settings.at("StayAtSystemPage")) {
						if (const auto pluginInfo = GetLoadInterface()->GetPluginInfo("StayAtSystemPage"); pluginInfo) {
							MessageBoxW(NULL, L"Stay At The System Page is already included in Enderal, please, disable it.", L"Enderal SE Error", MB_OK | MB_ICONERROR);
							exit(EXIT_FAILURE);
						}
					}
					if (g_settings.at("FlatMapMarkers")) {
						if (const auto pluginInfo = GetLoadInterface()->GetPluginInfo("FlatMapMarkersSSE"); pluginInfo) {
							MessageBoxW(NULL, L"Flat Map Markers is already included in Enderal, please, disable it (remove file Data\\SKSE\\Plugins\\FlatMapMarkersSSE.dll).", L"Enderal SE Error", MB_OK | MB_ICONERROR);
							exit(EXIT_FAILURE);
						}
					}
					if (g_settings.at("MapMarkerPlacementFixes")) {
						logger::info("Initializing map market placement fixes...");
						MapMarkerPlacement::Install();
					}
				}
			} else if (message->type == MessagingInterface::kDataLoaded) {
				if (REL::Module::get().version() > REL::Version(1, 5, 97, 0)) {
					RE::INIPrefSettingCollection::GetSingleton()->GetSetting("bFreebiesSeen:General")->data.b = true;
					if (REL::Module::get().version() >= REL::Version(1, 6, 1130, 0)) {
						RE::INIPrefSettingCollection::GetSingleton()->GetSetting("bUpsellOwned:General")->data.b = true;
					}
				}

				SKSE::GetTaskInterface()->AddTask([]() {
					const auto dataHandler = RE::TESDataHandler::GetSingleton();
					if (dataHandler) {
						if (!dataHandler->LookupLoadedModByName("Update.esm")) {
							MessageBoxW(NULL, L"Update.esm is not loaded!", L"Enderal SE Error", MB_OK | MB_ICONERROR);
							exit(EXIT_FAILURE);
						}
						if (!dataHandler->LookupLoadedModByName("Enderal - Forgotten Stories.esm")) {
							MessageBoxW(NULL, L"Enderal - Forgotten Stories.esm is not loaded!", L"Enderal SE Error", MB_OK | MB_ICONERROR);
							exit(EXIT_FAILURE);
						}
					}

					bool bScripts = RE::BSScript::Internal::VirtualMachine::GetSingleton()->TypeIsValid("PRKF__00E_CraftQuestPotionPe_0300146D");
					bool bInterface = RE::BSResourceNiBinaryStream("interface/00e_heromenu.swf").good();

					if (!bScripts || !bInterface) {
						RE::DebugMessageBox("E - Misc.bsa is not loaded or is outdated. Make sure Enderal - Forgotten Stories.esm is enabled and revalidate your files.");
					}

					bool bSounds = RE::BSResourceNiBinaryStream("sound/fx/enderal/laughingchild.wav").good();

					if (!bSounds) {
						RE::DebugMessageBox("E - Sounds.bsa is not loaded. Make sure Enderal - Forgotten Stories.esm is enabled and revalidate your files.");
					}

					bool bVoices = RE::BSResourceNiBinaryStream("sound/voice/enderal - forgotten stories.esm/vt_calia/fs_mq18c__0002a4ed_1.fuz").good();

					if (!bVoices) {
						RE::DebugMessageBox("L - Voices.bsa is not loaded. Make sure Enderal - Forgotten Stories.esm is enabled and revalidate your files.");
					}

					bool bMeshes = RE::BSResourceNiBinaryStream("meshes/enderal/loadscreenart/loadscreenundercity.nif").good();

					if (!bMeshes) {
						RE::DebugMessageBox("E - Meshes.bsa is not loaded. Make sure Enderal - Forgotten Stories.esm is enabled and revalidate your files.");
					}

					CheckIncompatibleMods();
				});
			} else if ((message->type == MessagingInterface::kPostLoadGame && message->data) || message->type == MessagingInterface::kNewGame) {
				NewGameCount(true);
				if (NewGameCount() == 1) {
					CheckScriptVersions();
				}
			}
		});
	}
}

SKSEPluginLoad(const LoadInterface* skse) {
	std::string filenames[4] = {
		"fs.dll",
		"fs_se.dll",
		"fs_skse_functions.dll",
		"FlatMapMarkersEnderal.dll"
	};
	for (int i = 0; i < 4; i++) {
		if (std::filesystem::exists(std::format("Data\\SKSE\\Plugins\\{}", filenames[i]))) {
			MessageBoxA(NULL, std::format("Found a leftover file from a previous version of Enderal SE. Please, delete Data\\SKSE\\Plugins\\{}.", filenames[i]).c_str(), "Enderal SE Error", MB_OK | MB_ICONERROR);
			exit(EXIT_FAILURE);
		}
	}

	if (DataFileExists("Dawnguard.esm") || DataFileExists("Dragonborn.esm") || DataFileExists("HearthFires.esm") || DataFileExists("Update.esm")) {
		MessageBoxW(NULL, L"Skyrim DLCs are incompatible with Enderal.", L"Enderal SE Error", MB_OK | MB_ICONERROR);
		exit(EXIT_FAILURE);
	}

	if (DataFileExists("Unofficial Skyrim Special Edition Patch.esp")) {
		MessageBoxW(NULL, L"Unofficial Skyrim Special Edition Patch is incompatible with Enderal.", L"Enderal SE Error", MB_OK | MB_ICONERROR);
		exit(EXIT_FAILURE);
	}

	if (!DataFileExists("Enderal - Forgotten Stories.esm")) {
		MessageBoxW(NULL, L"Enderal - Forgotten Stories.esm is not loaded!", L"Enderal SE Error", MB_OK | MB_ICONERROR);
		exit(EXIT_FAILURE);
	}

	CheckCCMods();

	GetLoadInterface(skse);

    InitializeLogging();

    auto* plugin = PluginDeclaration::GetSingleton();
    auto version = plugin->GetVersion();
	logger::info("{} {} is loading...", plugin->GetName(), version);

    Init(skse);
	InitializeMessaging();

	RE::INISettingCollection::GetSingleton()->GetSetting("sIntroSequence:General")->data.s = nullptr;

	EventListener::Install();

	GetPapyrusInterface()->Register(Papyrus::Bind);

	MainMenuPatch::Install();
	DialogueMenuPatch::Install();
	HeroMenuPatch::Install();
	TweenMenuPatch::Install();
	HUDMenuPatch::Install();

	LoadINI(&g_settings, "Data/SKSE/Plugins/EnderalSE.ini");

	if (g_settings.at("AchievementFix")) {
		logger::info("Patching achievements...");
		AchievementFix::Install();
	}

	if (!REL::Module::IsVR()) {
		RE::INISettingCollection::GetSingleton()->GetSetting("bModManagerMenuEnabled:General")->data.b = false;

		if (g_settings.at("VideoInterruptPatch")) {
			logger::info("Making videos interruptible...");
			BinkInterruptPatch::Install();
		}
		if (g_settings.at("ForceBorderless")) {
			ForceBorderless::Install();
		}
		if (g_settings.at("FlatMapMarkers")) {
			logger::info("Initializing Flat Map Markers...");
			FlatMapMarkers::Install();
		}
		if (g_settings.at("StayAtSystemPage")) {
			logger::info("Initializing Stay At The System Page...");
			JournalMenuEx::InstallHooks();
		}
	}

    logger::info("{} has finished loading.", plugin->GetName());
    return true;
}