/**
 * @namespace MATTIE.eventAPI
 * @description The main API the modding engine uses to perform any actions regarding events
 * This api is mostly for runtime events, if you want to add new compile time events look at loading data assets.
 */
MATTIE.eventAPI = MATTIE.eventAPI || {};
MATTIE.eventAPI.dataEvents = MATTIE.eventAPI.dataEvents || {};
MATTIE.eventAPI.blankEvent = {};
/**
 *
 * @param {Game_Item} item
 */
MATTIE.eventAPI.addItemDropToCurrentMap = function (item, spawn = true) {
	const event = new MapEvent();
	const itemObj = item.object();
	event.addPage();
	event.data.pages[1].conditions.selfSwitchValid = true;
	event.setImage(0, MATTIE.static.events.images.shiny());
	event.addText(0, 'There is something shining here....');
	event.addText(0, `You find... A ${itemObj.name}`);
	if (item.isArmor()) event.addCommand(0, 128, [itemObj.id, 0, 0, 1]); // give armor
	else if (item.isWeapon()) event.addCommand(0, 127, [itemObj.id, 0, 0, 1]); // give weapon
	else event.addCommand(0, 126, [itemObj.id, 0, 0, 1]); // give item
	event.addCommand(0, 123, ['A', 0]);// set self switch
	event.setPersist(true);
	if (spawn) { event.spawn($gamePlayer.x, $gamePlayer.y); }
	return event;
};

/**
 * @description create a blank event
 * @returns the id of the event
 */
MATTIE.eventAPI.createBlankEvent = function () {
	const event = new MapEvent();
	event.spawn(1, 1);
	return event.data.id;
};

MATTIE.eventAPI.createDataEvent = function (id, name, note, pages, x, y) {
	const obj = {};
	obj.id = id;
	obj.name = name;
	obj.note = note;
	obj.pages = pages;
	obj.x = x;
	obj.y = y;
	return obj;
};

MATTIE.eventAPI.updatePosOfRunTimeEvents = function () {
	const keys = Object.keys(MATTIE.eventAPI.dataEvents);
	for (let index = 0; index < keys.length; index++) {
		/** @type {rm.types.Event} */
		const eventId = keys[index];
		const dataEvent = MATTIE.eventAPI.dataEvents[keys[index]].data;
		if (dataEvent) {
			if (dataEvent.mapId === $gameMap.mapId() && dataEvent.persist) {
				const savedMapId = dataEvent.mapId;
				// eslint-disable-next-line max-len
				Object.assign(MATTIE.eventAPI.dataEvents[eventId].data, $dataMap.events[eventId] ? $dataMap.events[eventId] : MATTIE.eventAPI.dataEvents[eventId].data);

				const event = $gameMap.event(eventId);
				if (event) {
					MATTIE.eventAPI.dataEvents[eventId].data.x = event.x;
					MATTIE.eventAPI.dataEvents[eventId].data.y = event.y;
				}
			}
		}
	}
};

MATTIE.eventAPI.setupRunTimeDataEvents = function () {
	const keys = Object.keys(MATTIE.eventAPI.dataEvents);
	for (let index = 0; index < keys.length; index++) {
		setTimeout(() => {
		/** @type {MapEvent} */
			const mapEvent = MATTIE.eventAPI.dataEvents[keys[index]];
			const dataEvent = mapEvent.data;
			console.log(`A runtime event tried to setup data:\nWhere event is on MapId:${dataEvent.mapId}\nAnd player is on MapId:${$gameMap.mapId()}`);
			if (dataEvent.mapId === $gameMap.mapId() && dataEvent.persist) {
				$dataMap.events[dataEvent.id] = dataEvent;
				mapEvent.refresh();
				if (!$dataMap.events[dataEvent.id]) $dataMap.events[dataEvent.id] = undefined;
			}
		}, 150);
	}
};

MATTIE.eventAPI.setupRunTimeGameEvents = function () {
	const keys = Object.keys(MATTIE.eventAPI.dataEvents);
	for (let index = 0; index < keys.length; index++) {
		/** @type {MapEvent} */
		const mapEvent = MATTIE.eventAPI.dataEvents[keys[index]];
		const dataEvent = mapEvent.data;
		setTimeout(() => {
			console.log(`A runtime event tried to setup:\nWhere event is on MapId:${dataEvent.mapId}\nAnd player is on MapId:${$gameMap.mapId()}`);
			if (dataEvent.mapId === $gameMap.mapId() && dataEvent.persist) {
				if (!$dataMap.events[dataEvent.id]) {
					mapEvent.refresh();
					if (!$dataMap.events[dataEvent.id]) $dataMap.events[dataEvent.id] = undefined;
				}
			}
		}, 150);
	}
	$gameMap.refreshTileEvents();
};

// deprecated old loading code
// MATTIE.eventAPI.ReserveTrasnferOrg = Game_Player.prototype.reserveTransfer;
// Game_Player.prototype.reserveTransfer = function (mapId, x, y, d, fadeType) {
// 	MATTIE.eventAPI.ReserveTrasnferOrg.call(this, mapId, x, y, d, fadeType);
// 	// MATTIE.eventAPI.updatePosOfRunTimeEvents();
// };

// MATTIE.eventAPI.DataManager_loadMapData = DataManager.loadMapData;
// DataManager.loadMapData = function (mapId) {
// 	MATTIE.eventAPI.DataManager_loadMapData.call(this, mapId);
// 	MATTIE.eventAPI.setupRunTimeDataEvents();
// 	MATTIE.eventAPI.setupRunTimeGameEvents();
// };

// MATTIE.eventAPI.orgSetEventup = Game_Map.prototype.setupEvents;
// Game_Map.prototype.setupEvents = function () {
// 	MATTIE.eventAPI.orgSetEventup.call(this);
// };

/**
 * @description get a Game event obj from id and map id
 * @param {int} id event id
 * @param {int} mapId mapid
 * @returns {rm.types.Event}
 */
MATTIE.eventAPI.getEventOnMap = function (id, mapId, maxTime = 1000) {
	return new Promise((res) => {
		setTimeout(() => {
			res();
		}, maxTime);
		const url = 'data/Map%1.json'.format(mapId.padZero(3));
		const xhr = new XMLHttpRequest();

		xhr.open('GET', url, false);
		xhr.overrideMimeType('application/json');

		xhr.onload = function () {
			if (xhr.status < 400) {
				const mapData = JSON.parse(xhr.responseText);

				if (!(id in mapData.events)) {
					console.error('Error getting event data. Check to make sure an event with that specific id exists on the map.');
					res(null);
				} else {
					res(mapData.events[id]);
				}
			}
		};

		xhr.onerror = function () {
			console.error('Error getting map data. Check to make sure a map with that specific id exists.');
			res(null);
		};

		xhr.send();
	});
};

/**
 * @description create an enemy from an existing enemy handling death with a self switch
 * the self switch "A" will be used for the death page
 * the self switch "B" is used to signal if the player is in combat with it or not
 * @param {*} mapId the map of the original enemy
 * @param {*} eventId the id of the original enemy
 * @param {*} alivePageId the alive page of the original enemy
 * @param {*} deadPageId the dead page of the original enemy
 */
MATTIE.eventAPI.createEnemyFromExisting = function (mapId, eventId, alivePageId, deadPageId) {
	const enemy = new MapEvent();
	const baseEnemy = new MapEvent();
	baseEnemy.copyActionsFromEventOnMap(eventId, mapId); // create copy of crow mauler obj
	const alivePage = baseEnemy.data.pages[alivePageId];
	const deadPage = baseEnemy.data.pages[deadPageId];

	enemy.data.pages[0] = alivePage;
	enemy.data.pages[0].conditions = enemy.setDefaultConditions();
	// set up changing self switch on victory
	const indexOfIfWinCmd = enemy.indexOfCommandOnPage(0, MATTIE.static.commands.ifWin);
	const indent = alivePage.list[alivePage.list.length - 1].indent + 1;
	enemy.addCommandAfterIndex(0, indexOfIfWinCmd, enemy.createCommand(MATTIE.static.commands.selfSwitch, ['A', 0], indent));

	enemy.addPage();
	// set up conditions for death page
	enemy.data.pages[1] = deadPage;
	enemy.data.pages[1].conditions = enemy.setDefaultConditions();
	enemy.data.pages[1].conditions.selfSwitchValid = true;
	enemy.setPersist(true);
	return enemy;
};

MATTIE.eventAPI.removePersistingEvent = function (eventId) {
	delete MATTIE.eventAPI.dataEvents[eventId];
};

MATTIE.eventAPI.marriageAPI = {};
/**
 * @description display sex between the two actors specified (will default to Cahara+Enki if a solution cannot be found)
 * @param {*} actorId1 the id of the first actor
 * @param {*} actorId2 the id of the second actor
 * @param {*} x the x pos on the map to display the sex
 * @param {*} y the y pos on the map to display the sex
 * @returns {MapEvent} the map event that was created
 */
MATTIE.eventAPI.marriageAPI.displaySex = function (actorId1, actorId2, x, y, spawn = true) {
	const marriage = new MapEvent();
	// marriage.setPersist(false);
	marriage.copyActionsFromEventOnMap(84, 1); // copy event from the first room ritual circle
	/** @description a dict mapping the actor index to the page index that the game uses for this marriage */

	// map all ghouls to be id 16
	const ghoulIds = [16, 17, 18];
	if (ghoulIds.includes(actorId1)) actorId1 = 16;
	if (ghoulIds.includes(actorId2)) actorId2 = 16;

	const mappings = [
		{
			id1: 1, id2: 16, pageIndex: 5, characterImage: '$love_mercenary_ghoul',
		}, // merc ghoul
		{
			id1: 3, id2: 16, pageIndex: 6, characterImage: '$love_knight_ghoul',
		}, // knight ghoul
		{
			id1: 4, id2: 16, pageIndex: 7, characterImage: '$love_darkpriest_ghoul',
		}, // priest ghoul
		{
			id1: 5, id2: 16, pageIndex: 8, characterImage: '$love_outlander_ghoul',
		}, // outlander ghoul
		{
			id1: 9, id2: 16, pageIndex: 10, characterImage: '$love_marriage_ghoul',
		}, // marriage ghoul

		{
			id1: 3, id2: 6, pageIndex: 11, characterImage: '$love_captain_knight',
		}, // knight legarde
		{
			id1: 1, id2: 3, pageIndex: 12, characterImage: MATTIE.util.randChance(0.5) ? '$love_knight_mercenary' : '$love_knight_mercenary_bottom',
		}, // knight merc
		{
			id1: 5, id2: 3, pageIndex: 13, characterImage: '$love_outlander_knight',
		}, // outlander knight
		{
			id1: 1, id2: 5, pageIndex: 14, characterImage: '$love_outlander_mercenary',
		}, // outlander merc
		{
			id1: 1, id2: 6, pageIndex: 15, characterImage: '$love_captain_mercenary',
		}, // legard merc

		{
			id1: 9, id2: 1, pageIndex: 16, characterImage: '$love_marriage_mercenary',
		}, // marriage merc
		{
			id1: 1, id2: 4, pageIndex: 17, characterImage: '$love_darkpriest_mercenary',
		}, // dark merc
		{
			id1: 1, id2: 1, pageIndex: 17, characterImage: '$love_mercenary_mercenary',
		}, // merc merc
		{
			id1: 4, id2: 4, pageIndex: 17, characterImage: '$love_darkpriest_darkpriest',
		}, // dark dark
		{
			id1: 3, id2: 3, pageIndex: 17, characterImage: '$love_knight_knight',
		}, // knight knight
		{
			id1: 5, id2: 5, pageIndex: 17, characterImage: '$love_outlander_outlander',
		}, // outlander outlander
		{
			id1: 4, id2: 3, pageIndex: 17, characterImage: '$love_knight_darkpriest',
		}, // knight darkpriest
	];

	const findMapping = function (id1, id2) {
		const defaultMapping = mappings[11];
		for (let index = 0; index < mappings.length; index++) {
			const element = mappings[index];
			if ((element.id1 == id1 && element.id2 == id2) || (element.id2 == id1 && element.id1 == id2)) {
				return element;
			}
		}
		return defaultMapping;
	};

	const mapping = findMapping(actorId1, actorId2);

	// set to only have the one page that we want
	marriage.data.pages = [marriage.data.pages[mapping.pageIndex]];
	marriage.setChar(0, mapping.characterImage);

	marriage.data.pages[0].conditions = marriage.setDefaultConditions();
	marriage.data.pages[0].trigger = 0;
	marriage.data.pages[0].list = [];
	marriage.data.pages[0].directionFix = true;
	marriage.addSpokenText(0, '[Intense Moaning]', `${$gameActors.actor(actorId1).name()} & ${$gameActors.actor(actorId2).name()}`);
	marriage.addText(0, 'Best not to interrupt them.');

	if (spawn) { marriage.spawn(x, y); }

	return marriage;
};

/**
 * @description display a marriage (will default to Cahara+Enki if a solution cannot be found)
 * @param {int} actorId1 the id of the first actor
 * @param {int} actorId2 the id of the second actor
 * @param {bool} success whether the marriage is successful or failed
 * @param {int} x the x pos on the map to display the marriage
 * @param {int} y the y pos on the map to display the marriage
 * @returns {MapEvent} the map event that was created
 */
MATTIE.eventAPI.marriageAPI.displayMarriage = function (actorId1, actorId2, success, x, y, spawn = true) {
	// up is failed marriage
	// left starts to merge
	// right is sex
	// down is mostly merged
	const sexEvent = this.displaySex(actorId1, actorId2, x, y, false);

	sexEvent.data.pages[0].list = [];
	sexEvent.data.pages[0].trigger = 4;
	sexEvent.data.pages[0].directionFix = false;

	// failed marriage commands
	if (!success) {
		sexEvent.data.pages[1] = JsonEx.makeDeepCopy(sexEvent.data.pages[0]);
		sexEvent.addSpokenText(1, 'Whheeezee....', 'Amalgam of Flesh');
	} else {
		sexEvent.addPage();
	}

	sexEvent.data.pages[1].conditions = sexEvent.setDefaultConditions();
	sexEvent.data.pages[1].conditions.selfSwitchValid = true;
	sexEvent.data.pages[1].directionFix = true;
	sexEvent.data.pages[1].trigger = 0;

	// marriage commands
	sexEvent.addMoveRoute(0, Game_Character.ROUTE_TURN_RIGHT, true);
	sexEvent.addWait(0, 90);
	sexEvent.addTintScreen(0, MATTIE.fxAPI.formatTint(-155, -155, -155, 0), 40, true);
	sexEvent.addMoveRoute(0, Game_Character.ROUTE_TURN_LEFT, true);
	sexEvent.addWait(0, 90);
	sexEvent.addTintScreen(0, MATTIE.fxAPI.formatTint(-155, -155, -155, 0), 40, true);
	sexEvent.addMoveRoute(0, Game_Character.ROUTE_TURN_DOWN, true);
	sexEvent.addWait(0, 90);
	sexEvent.addTintScreen(0, MATTIE.fxAPI.formatTint(-255, -255, -255, 0), 70, true);
	if (!success) {
		sexEvent.addMoveRoute(0, Game_Character.ROUTE_TURN_UP, true);
		sexEvent.data.pages[1].moveRoute = {
			list: [
				{
					code: 19,
					indent: null,
				},
				{
					code: 0,
					parameters: [],
				},
			],
			repeat: true,
			skippable: false,
			wait: false,
		};
	}
	sexEvent.addWait(0, 90);
	sexEvent.addTintScreen(0, MATTIE.fxAPI.formatTint(-255, -255, -255, 0), 70, false);
	sexEvent.addWait(0, 35);
	sexEvent.addCommand(0, 123, ['A', 0]); // set self switch

	sexEvent.setPersist(!success);
	if (spawn) { sexEvent.spawn(x, y); }
	return sexEvent;
};