#pragma once
#include "mapping/ModuleID.h"
#include <rack.hpp>

struct ModuleDirectory {

	// "Regular" module means it's a virtual module that
	// we should include in the patch file.
	// We exclude modules which users would not expect to have
	// running on hardware, like modules that do MIDI Mappings,
	// notes, blanks, scope modules, etc.
	static bool isRegularModule(rack::Module *module, bool use_builtin_midi) {
		if (!isValid(module))
			return false;

		if (isHubOrExpander(module))
			return false;

		// When using RackCore MIDI, treat Core::MIDI* modules as normal regular modules
		// (Split and Merge are always regular modules; poly-split Splits are filtered later
		// using MIDI::Modules::isPolySplitModule in built-in MIDI mode.)
		// MIDI-Map is the exception: its CC mappings are always converted into the patch's
		// MIDI map and the module itself is never added (in either MIDI mode).
		if (!use_builtin_midi) {
			if (isCoreMIDI(module) && !isMidiMap(module))
				return true;
		}

		std::array blacklist = {"AudioInterface2",
								"AudioInterface",
								"AudioInterface16",
								"Bogaudio-Analyzer",
								"Bogaudio-AnalyzerXL",
								"Bogaudio-Ranalyzer",
								"Notes",
								"Blank",
								"MIDIToCVInterface",
								"MIDI-Map",
								"MIDITriggerToCVInterface",
								"MIDICCToCVInterface"};

		for (auto slug : blacklist) {
			if (module->model->slug == slug)
				return false;
		}
		return true;
	}

	static std::string convertSlugs(rack::Module *module) {
		if (!isValid(module))
			return "";

		auto brand = module->getModel()->plugin->slug;
		auto module_slug = module->getModel()->slug;

		// Convert Airwin2Rack FX selection to a MetaModule module
		if (brand == "Airwin2Rack") {
			brand = "Airwindows";
			auto json = module->dataToJson();
			auto val = json_object_get(json, "airwindowSelectedFX");

			if (val && json_typeof(val) == JSON_STRING)
				module_slug = json_string_value(val);
			else
				module_slug = "Galactic";

			json_decref(json);
		}

		return brand + ":" + module_slug;
	}

	// Hub
	static bool isHub(std::string_view slug) {
		if (slug == "PanelMedium")
			return true;
		if (slug == "HubMedium")
			return true;
		if (slug == "4msCompany:PanelMedium")
			return true;
		if (slug == "4msCompany:HubMedium")
			return true;

		return false;
	}

	static bool isHub(rack::Module *module) {
		if (!isValid(module))
			return false;

		if (module->model->plugin->slug != "4msCompany")
			return false;

		return isHub(module->model->slug);
	}

	// Expanders
	static bool isAudioExpander(std::string_view slug) {
		return slug == "MMAudioExpander";
	}

	static bool isAudioExpander(rack::Module *module) {
		if (!module || !module->model || !module->model->plugin)
			return false;

		if (module->model->plugin->slug != "4msCompany")
			return false;

		return isAudioExpander(module->model->slug);
	}

	static bool isButtonExpander(std::string_view slug) {
		return slug == "MMButtonExpander";
	}

	static bool isButtonExpander(rack::Module *module) {
		if (!module || !module->model || !module->model->plugin)
			return false;

		if (module->model->plugin->slug != "4msCompany")
			return false;

		return isButtonExpander(module->model->slug);
	}

	static bool isExpander(std::string_view slug) {
		return isAudioExpander(slug) || isButtonExpander(slug);
	}

	static bool isExpander(rack::Module *module) {
		return isAudioExpander(module) || isButtonExpander(module);
	}

	static bool isHubOrExpander(rack::Module *module) {
		if (!isValid(module))
			return false;

		if (module->model->plugin->slug != "4msCompany")
			return false;

		return isHub(module->model->slug) || isExpander(module->model->slug);
	}

	// MIDI

	static bool isCoreSplitMerge(rack::Module *module) {
		if (!isValid(module))
			return false;

		if (module->model->plugin->slug == "Core") {
			if (module->model->slug == "Split")
				return true;
			if (module->model->slug == "Merge")
				return true;
		}

		return false;
	}

	static bool isAudioInterface(rack::Module *module) {
		if (!isValid(module))
			return false;

		if (module->model->plugin->slug != "Core")
			return false;

		auto const &slug = module->model->slug;
		return slug == "AudioInterface" || slug == "AudioInterface2" || slug == "AudioInterface16";
	}

	static bool isCoreMIDI(rack::Module *module) {
		if (!isValid(module))
			return false;

		if (module->model->plugin->slug == "Core") {
			if (module->model->slug == "MIDIToCVInterface")
				return true;
			if (module->model->slug == "MIDICCToCVInterface")
				return true;
			if (module->model->slug == "MIDI-Map")
				return true;
			if (module->model->slug == "MIDITriggerToCVInterface")
				return true;
		}

		return false;
	}

	static bool isMidiMap(rack::Module *module) {
		if (!isValid(module))
			return false;

		return module->model->plugin->slug == "Core" && module->model->slug == "MIDI-Map";
	}

	static bool isValid(rack::Module *module) {
		return module && module->model && module->model->plugin;
	}
};
