//-----------------------------------------------------------------------------------
// a class to override the base Game_Player to emit their inputs for multiplayer
//-----------------------------------------------------------------------------------

var MATTIE = MATTIE || {};
MATTIE.multiplayer = MATTIE.multiplayer || {};
MATTIE.menus.multiplayer = MATTIE.menus.multiplayer || {};
MATTIE.scenes.multiplayer = MATTIE.scenes.multiplayer || {};
MATTIE.windows.multiplayer = MATTIE.windows.multiplayer || {};
MATTIE.multiplayer.gamePlayer = MATTIE.multiplayer.gamePlayer || {};
MATTIE.multiplayer.movementEmitter = MATTIE.multiplayer.movementEmitter || {};
MATTIE.multiplayer.playerEmitter = MATTIE.multiplayer.playerEmitter || {};
MATTIE.RPG = MATTIE.RPG || {};

/**
 * override all necessary methods making them emit events for multiplayer support
 */

// every n moves send player x and y to move
MATTIE.multiplayer.selfMoveCount = 0;
MATTIE.multiplayer.selfMax = 5;
// every n moves send player x and y to transfer
MATTIE.multiplayer.selfTransMoveCount = 0;
MATTIE.multiplayer.selfTransMax = 100;

// every n seconds send force a movement position or transfer emission.
MATTIE.multiplayer.movementEmitter.secondsTillPosSend = 10 * 1000;
MATTIE.multiplayer.movementEmitter.secondsTillTrans = 100 * 1000;

// the interval to handle position emission every n seconds
setInterval(() => {
	if (MATTIE.multiplayer.isActive) {
		var netController = MATTIE.multiplayer.getCurrentNetController();
		if (netController) {
			if (netController.started) netController.emitMoveEvent(0, $gamePlayer.x, $gamePlayer.y);
		}
	}
}, MATTIE.multiplayer.movementEmitter.secondsTillPosSend);

// the interval to handle transfer emission every n seconds
setInterval(() => {
	if (MATTIE.multiplayer.isActive) {
		var netController = MATTIE.multiplayer.getCurrentNetController();
		if (netController) {
			if (netController.started) netController.emitMoveEvent(0, $gamePlayer.x, $gamePlayer.y, true);
		}
	}
}, MATTIE.multiplayer.movementEmitter.secondsTillTrans);

// A bit of an old function from the earlier stages of the mod, used to setup some but not all player emissions
// mostly for movement emissions of the player.
MATTIE.multiplayer.gamePlayer.override = function () {
	console.info('--emitter overrides initialized--');

	// override the execute move command
	MATTIE.multiplayer.gamePlayer.executeMove = Game_Player.prototype.executeMove;
	Game_Player.prototype.executeMove = function (direction) {
		// TODO: args is not setup well here. fix that

		MATTIE.multiplayer.gamePlayer.executeMove.call(this, direction);
		var netController = MATTIE.multiplayer.getCurrentNetController();
		const args = [direction];

		// if we should not be transferring
		if (MATTIE.multiplayer.selfMoveCount >= MATTIE.multiplayer.selfMax) {
			args.push(this.x);
			args.push(this.y);
			MATTIE.multiplayer.selfMoveCount = 0;
		}

		// if we should be transferring.
		if (MATTIE.multiplayer.selfTransMoveCount >= MATTIE.multiplayer.selfTransMax) {
			args.push(this.x);
			args.push(this.y);
			args.push(1);
			MATTIE.multiplayer.selfTransMoveCount = 0;
		}

		// emit the move event.
		netController.emitMoveEvent(...args);
		MATTIE.multiplayer.selfMoveCount++;
		MATTIE.multiplayer.selfTransMoveCount++;
	};

	// override the update main command to make all netPlayers update at the same time as though they were Game_Player
	MATTIE.RPG.SceneMap_MainUpdate = Scene_Map.prototype.updateMain;
	Scene_Map.prototype.updateMain = function () {
		// TODO: see if this fps function actually works for anything. It was intended to help the game when fps drops, but not sure if it
		// does its job or not.
		if (Graphics._fpsMeter.fps < 10) {
			SceneManager._deltaTime = 0.9 / Graphics._fpsMeter.fps;
		}

		// for each net player, update their local object on the main update
		const netController = MATTIE.multiplayer.getCurrentNetController();
		for (key in netController.netPlayers) {
			if (key) {
				const netPlayer = netController.netPlayers[key];
				const localPlayer = netPlayer.$gamePlayer;
				if (localPlayer) {
					localPlayer.update(SceneManager._scene.isActive());
				}
			}
		}

		// update everything else
		MATTIE.RPG.SceneMap_MainUpdate.call(this);

		// MATTIE.multiplayer._interpreter.update();
	};

	// create an objet to sure transfer data
	MATTIE.multiplayer.renderer.currentTransferObj = {};

	// override reserve transfer to also store its data in our obj
	MATTIE.RPG.reserveTransfer = Game_Player.prototype.reserveTransfer;
	Game_Player.prototype.reserveTransfer = function (mapId, x, y, d, fadeType) {
		MATTIE.RPG.reserveTransfer.call(this, mapId, x, y, d, fadeType);
		if (MATTIE.multiplayer.isActive) {
			MATTIE.multiplayer.renderer.currentTransferObj = {};
			MATTIE.multiplayer.renderer.currentTransferObj.transfer = {};
			MATTIE.multiplayer.renderer.currentTransferObj.transfer.x = x;
			MATTIE.multiplayer.renderer.currentTransferObj.transfer.y = y;
			MATTIE.multiplayer.renderer.currentTransferObj.transfer.map = mapId;
		}
	};

	// override performTransfer to emit events with the data we stored
	MATTIE.RPG.performTransfer = Game_Player.prototype.performTransfer;
	Game_Player.prototype.performTransfer = function (shouldSync = true) {
		MATTIE.RPG.performTransfer.call(this);
		if (MATTIE.multiplayer.isActive && shouldSync) {
			const netController = MATTIE.multiplayer.getCurrentNetController();
			netController.emitTransferEvent(MATTIE.multiplayer.renderer.currentTransferObj);
		}
	};

	// override the function that triggers when the scene map is fully loaded
	MATTIE.RPG.sceneMapOnLoaded = Scene_Map.prototype.onMapLoaded;
	Scene_Map.prototype.onMapLoaded = function () {
		MATTIE.RPG.sceneMapOnLoaded.call(this);
		if (MATTIE.multiplayer.isActive) {
			MATTIE.emitTransfer();
			MATTIE.multiplayer.setEnemyHost();
			MATTIE.multiplayer.getCurrentNetController().updatePlayersOnCurrentMap();

			// TODO: verify that this is not causing any issues for players being visible when they should not be
			// SEE: https://discord.com/channels/1148766509406093342/1157000978885791765/1230770842179207209
			$gamePlayer.setTransparent(false); // make sure the player is not transparent
		}
	};

	// TODO: verify that this should be in this file, likely needs to be moved to the netcontroller class instead of being here.
	// or moved to event local event emitter.
	/**
	 * @description emits a transfer event to net controller
	 */
	MATTIE.emitTransfer = function () {
		MATTIE.multiplayer.renderer.currentTransferObj = {};
		MATTIE.multiplayer.renderer.currentTransferObj.transfer = {};
		MATTIE.multiplayer.renderer.currentTransferObj.transfer.x = $gamePlayer.x;
		MATTIE.multiplayer.renderer.currentTransferObj.transfer.y = $gamePlayer.y;
		MATTIE.multiplayer.renderer.currentTransferObj.transfer.map = $gameMap.mapId();
		const netController = MATTIE.multiplayer.getCurrentNetController();

		// handle setting the proper actor id when the player loads into a map
		// this does also allow the main actor changing if we want
		netController.updatePlayerInfo();
		netController.emitTransferEvent(MATTIE.multiplayer.renderer.currentTransferObj);
	};

	// extend the follower change command to emit the event we need
	MATTIE.RPG.addActor = Game_Party.prototype.addActor;
	MATTIE.RPG.removeActor = Game_Party.prototype.removeActor;

	// extend the addActor method to update player info when a follower is added.
	Game_Party.prototype.addActor = function (actorId) {
		console.info('follower change event');
		MATTIE.RPG.addActor.call(this, actorId);
		MATTIE.multiplayer.getCurrentNetController().updatePlayerInfo();
	};

	const removedActorIds = [];
	// extend the addActor method to update player info when a follower is removed.
	Game_Party.prototype.removeActor = function (actorId) {
		MATTIE.RPG.removeActor.call(this, actorId);
		if (!removedActorIds.includes(actorId)) {
			MATTIE.multiplayer.getCurrentNetController().updatePlayerInfo();
			console.info('follower change event');
		}
		removedActorIds.push(actorId);
	};

	MATTIE_RPG.Game_PlayerStartMapEvent = Game_Player.prototype.startMapEvent;

	// Extend the start map event function to allow interacting with other players. We are doing this directly here
	// as it is easier than having event pages attached to the net players.
	Game_Player.prototype.startMapEvent = function (x, y, triggers, normal) {
		MATTIE_RPG.Game_PlayerStartMapEvent.call(this, x, y, triggers, normal);
		MATTIE.multiplayer.playerEmitter.checkPlayerActionBtnEvent.call(this, x, y, triggers);
	};

	// this handles checking if you can interact with the player on your tile.
	MATTIE.multiplayer.playerEmitter.checkPlayerActionBtnEvent = function (x, y, triggers) {
		const netCont = MATTIE.multiplayer.getCurrentNetController();
		Object.keys(netCont.netPlayers).forEach((key) => {
			/** @type {PlayerModel} */
			const netPlayer = netCont.netPlayers[key];
			if (netPlayer.map === $gameMap.mapId()) {
				if (!MATTIE.static.maps.onMenuMap()) {
					if (netPlayer.$gamePlayer.x === x && netPlayer.$gamePlayer.y === y) {
						if (triggers.contains(0)) { // if this is an action button trigger
							netPlayer.onInteract();
						}
					}
				}
			}
		});
	};

	/** @description the base set transparent method for game player */
	MATTIE_RPG.Game_Player_setTransparent = Game_Player.prototype.setTransparent;

	/**
	 * @description the overridden method for set transparent overridden to emit its event
	 * @param {boolean} bool whether the player is transparent or visible
	 * @param {boolean} doNotEmit whether to not send this event to net clients or not
	 */
	Game_Player.prototype.setTransparent = function (bool, doNotEmit = false) {
		const netCont = MATTIE.multiplayer.getCurrentNetController();
		MATTIE_RPG.Game_Player_setTransparent.call(this, bool);
		if (!doNotEmit)netCont.emitSetTransparentEvent(bool);
	};

	/** @description the base setCharacterImage method for game actor */
	MATTIE_RPG.Game_Actor_setCharacterImage = Game_Actor.prototype.setCharacterImage;
	/**
	 * @description the overridden method for set character image to handle emitting outfit changes
	 * @param {string} characterName the name of the charsheet (the file within www/imgs/charecters excluding .png)
	 * @param {ing} characterIndex the index within that sprite sheet
	 */
	Game_Actor.prototype.setCharacterImage = function (characterName, characterIndex) {
		this._lastCharName = this._characterName;
		this._lastCharIndex = this._characterIndex;
		MATTIE_RPG.Game_Actor_setCharacterImage.call(this, characterName, characterIndex);
		if (!(this instanceof MATTIE.multiplayer.NetActor)) { // only emit this event if this is a main actor and not a net actor
			if (this._lastCharName != characterName || this._lastCharIndex != characterIndex) { // check that anything has changed
				if ($gameParty._actors.contains(this.actorId())) { // only send if this char exists in party
					MATTIE.multiplayer.getCurrentNetController().emitSetCharacterImageEvent(characterName, characterIndex, this.actorId());
				}
			}
		}
	};

	/** @description the base isDashButtonPressed method for game player */
	MATTIE_RPG.Game_Player_updateDashing = Game_Player.prototype.updateDashing;
	/**
	 * @description override the update dashing method to emit the dash event when there is a change in dashing
	 */
	Game_Player.prototype.updateDashing = function () {
		MATTIE_RPG.Game_Player_updateDashing.call(this);
		// console.log(`isDashBtnPressed:${this._dashing}`);
		if (this.lastVal != this._dashing && this.isMoving()) {
			// dash is always false if player is not moving thus only check if player is moving
			// console.log(`IS DASH PRESSED CHANGED:\n -FROM:${this.lastVal}\n -TO:${this._dashing}`);
			MATTIE.multiplayer.getCurrentNetController().emitDashEvent(this._dashing);
			this.lastVal = this._dashing;
		}
	};

	/** @description the base set move speed method */
	MATTIE_RPG.Game_Player_SetMoveSpeed = Game_Player.prototype.setMoveSpeed;
	/** @description override the set move speed method to emit the event as well */
	Game_Player.prototype.setMoveSpeed = function (moveSpeed) {
		// console.log(`moveSpeedSetTo:${moveSpeed}`);
		if (!(this instanceof MATTIE.multiplayer.Secondary_Player)) { // if not a net player
			if (this.lastMoveSpeed != moveSpeed) {
				// console.log(`MOVE SPEED CHANGED:\n -FROM:${this.lastMoveSpeed}\n -TO:${moveSpeed}`);
				MATTIE.multiplayer.getCurrentNetController().emitSpeedEvent(moveSpeed);
				this.lastMoveSpeed = moveSpeed;
			}
		}
		MATTIE_RPG.Game_Player_SetMoveSpeed.call(this, moveSpeed);
	};

	// setup a move event to trigger when player stops moving
	MATTIE.inputAPI.onLongNoInput((() => {
		if (MATTIE.multiplayer.hasController()) {
			const netController = MATTIE.multiplayer.getCurrentNetController();
			if (SceneManager._scene.isActive() && SceneManager._scene instanceof Scene_Map) {
				// console.log(MATTIE.multiplayer.getCurrentNetController());
				// console.log(netController.emitMoveEvent);
				netController.emitMoveEvent(0, $gamePlayer.x, $gamePlayer.y);
			}
		}
	}));
};