var MATTIE_RPG = MATTIE_RPG || {};

MATTIE.betterCrowMauler = MATTIE.betterCrowMauler || {};
/**
 *  @description the number of milliseconds util it is checked if crow mauler can spawn
 *  @default 10000
 * */
MATTIE.betterCrowMauler.spawnInterval = 10000;
/**
 *  @description the chance that crow mauler will spawn every interval
 *  @default .03
 * */
MATTIE.betterCrowMauler.spawnChance = 0.03;

/**
 *  @description the chance that crow mauler will follow you into the next room
 *  @default .15
 * */
MATTIE.betterCrowMauler.followChance = 0.15;

/**
 *  @description the chance that crow mauler will despawn when you leave a room
 *  @default .15
 * */
MATTIE.betterCrowMauler.despawnChance = 0.15;

/**
 * @description the chance of crow mauler entering someones battle
 * @default .005
 */
MATTIE.betterCrowMauler.combatEnterChance = 0.005;

/**
 * @description the min amount of time that must pass before crow can spawn in ms
 * @default 100,000 milli seconds
 *
*/
MATTIE.betterCrowMauler.timeToSpawnMin = 100000;

MATTIE.betterCrowMauler.timeToSpawnMax = 10000000;
MATTIE.betterCrowMauler.timeScaler = (t) => (0.2 + (10 * (t / MATTIE.betterCrowMauler.timeToSpawnMax)));

/**
 * @description a scaler to scale the crow's spawn chance by
 * @default 1
 * */
MATTIE.betterCrowMauler.scaler = 1;

/**
 * @author Mattie_FM
 */
MATTIE.betterCrowMauler.CrowController = class {
	constructor(params) {
		setTimeout(() => {
			this.disableBaseCrowMauler();
		}, 5000);

		this.timeElapsed = 0;

		/** @description whether this instance of the controller has spawned a crow mauler on this level */
		this.hasSpawned = false;

		/** @description whether a crow mauler has forced its way into this combat */
		this.inBattle = false;

		/** @description whether this instance of the controller's crow mauler is on screen or not  */
		this.onScreen = false;

		/** @description the id of the mapId crow mauler was spawned in last */
		this.mapId = 0;
		/**
		 * @description The current crow obj if spawned
		 * @type {MapEvent}
		 * */
		this.self = new MapEvent();
		this.self.setPersist(true);

		const prevFunc = Game_Player.prototype.performTransfer;
		const that = this;
		Game_Player.prototype.performTransfer = function () {
			prevFunc.call(this);
			that.onEnterRoom();
			this.inBattle = false;
		};

		setInterval(() => {
			this.timeElapsed += MATTIE.betterCrowMauler.spawnInterval;
			if (this.crowCanSpawn()) {
				if ((!MATTIE.static.maps.onMenuMap())) { this.spawnTick(); }
				this.battleSpawnTick();
			}
		}, MATTIE.betterCrowMauler.spawnInterval);
	}

	/** @description whether this instance's crow mauler is dead */
	isDead() {
		const bool = this.self.checkSelfSwitch('A');
		return bool || !this.crowCanSpawn();
	}

	/** @description a function to return the scaler, meat to be overridden if other mods need to change this */
	getScaling() {
		return MATTIE.betterCrowMauler.scaler * MATTIE.betterCrowMauler.timeScaler(this.timeElapsed);
	}

	/**
	 * @description disable crow mauler
	 * @todo make sure this works properly
	 */
	disableBaseCrowMauler() {
		$gameSwitches.setValue(MATTIE.static.switch.crowMaulerDisabled, true);
	}

	/**
	 * @description the function called every x ms to check if crow mauler should spawn
	 */
	spawnTick() {
		if (!this.onScreen && !this.hasSpawned && !this.isDead() && !$gameParty.inBattle() && SceneManager._scene instanceof Scene_Map) this.update();
	}

	/**
	 * @description the function called every x ms to check if crow mauler should invade
	 */
	battleSpawnTick() {
		if (!this.isDead() && $gameParty.inBattle() && this.shouldEnterCombat() && !this.inBattle) {
			MATTIE.msgAPI.footerMsg('A terrifying presence has entered the room...');
			MATTIE.msgAPI.footerMsg('A terrifying presence is getting closer');

			setTimeout(() => {
				MATTIE.msgAPI.footerMsg('A terrifying presence is behind you');

				setTimeout(() => {
					this.invadeBattle();
				}, 35000);
			}, 5000);
		}
	}

	/**
	 * @description have crow mauler invade the current battle
	 * @param {*} net multiplayer only (fix this later)
	 */
	invadeBattle(net = false) {
		if ($gameParty.inBattle()) {
			this.inBattle = true;
			const additionalTroop = new MATTIE.troopAPI.RuntimeTroop(MATTIE.static.troops.crowMauler, 500, 0);
			additionalTroop.spawn(net);
		}
	}

	/**
	 * @description checks if crow mauler can spawn
	 * @returns {boolean}
	 */
	crowCanSpawn() {
		return $gameSwitches.value(MATTIE.static.switch.crowMaulerCanSpawn)
		&& !$gameSwitches.value(MATTIE.static.switch.crowMaulerDead)
		&& this.timeElapsed >= MATTIE.betterCrowMauler.timeToSpawnMin;
	}

	/**
	 * @description get all possible entry or exit event tiles in the room
	 * @returns an array of every event that has an active transfer event in its list
	 */
	getAllTransferPointsOnMap() {
		let arr = [];
		if ($gameMap) {
			arr = $gameMap.events().filter((event) => {
				if (event.event()) {
					return event.event().pages.some((page) => {
						if (page) {
							const list = page.list.map((cmd) => cmd.code);
							const commonEvents = page.list.filter((cmd) => cmd.code === MATTIE.static.commands.commonEventid);
							const hasPhaseStep = page.list.some((cmd) => (cmd.code == 121 && cmd.parameters[0] == MATTIE.static.switch.phaseStep));
							if (hasPhaseStep) return false;
							if (commonEvents.length > 0) {
								return list.includes(MATTIE.static.commands.transferId)
							&& !list.includes(MATTIE.static.commands.battleProcessingId)
							&& commonEvents.filter((cmd) => cmd.parameters[0] === 104).length > 0; // has room change common event
							}
						}
						return false;
					});
				}
				return false;
			});
		}

		console.log(arr);
		return arr;
	}

	/**
	 * @description find the closest spawn point to a provided x and y
	 * @param {int} x;
	 * @param {int} y;
	 * @returns {Game_Event} the closest possible spawn point to the players current x and y
	 */
	findClosestSpawnPoint(x, y) {
		const spawnPoints = this.getAllTransferPointsOnMap();
		let closest = spawnPoints[0];
		if (!closest) closest = { x: 0, y: 0 };
		if (!closest.x) closest = { x: 0, y: 0 };
		if (closest) {
			let dist = MATTIE.util.getDist(x, closest.x, y, closest.y);
			for (let index = 1; index < spawnPoints.length; index++) {
			/** @type {Game_Event} */
				const element = spawnPoints[index];
				const thisDist = MATTIE.util.getDist(x, element.x, y, element.y);
				if (thisDist < dist) {
					for (let x1 = -2; x1 < 2; x1++) {
						for (let y1 = -2; y1 < 2; y1++) {
							if (MATTIE.isPassableAnyDir(element)) {
								dist = thisDist;
								closest = element;
							}
						}
					}
				}
			}
		}
		return closest;
	}

	findNearestPassablePoint(X, Y, centerX, centerY, dist = 5) {
		const max = (Math.max(X, Y) ** 2);

		let x = 0;
		let y = 0;
		let dx = 0;
		let t = 0;
		let dy = -1;
		for (let index = 0; index < max; index++) {
			if ((-X / 2 < x <= X / 2) && (-Y / 2 < y <= Y / 2)) {
				const obj = {};
				obj.x = x + centerX;
				obj.y = y + centerY;
				if (MATTIE.isPassableXDirs(obj, 2)) return obj;
			}
			if (x == y || (x < 0 && x == -y) || (x > 0 && x == 1 - y)) {
				t = dx;
				dx = -dy;
				dy = t;
			}
			x += dx;
			y += dy;
		}
		console.log('returned default');
		return { x: X, y: Y };
	}

	/**
	 * @description create a new MapEvent for crow mauler. Does not spawn the event.
	 * @returns {MapEvent}
	 */
	createCrowObj() {
		const event = MATTIE.eventAPI.createEnemyFromExisting(53, 207, 2, 9);
		return event;
	}

	/**
	 * @description crow mauler enter the room
	 */
	enter() {
		MATTIE.msgAPI.footerMsg('A terrifying presence has entered the room...');
		this.spawn();
	}

	/**
	 * @description spawn the crow mauler event removing the previous one if it exists
	 */
	spawn() {
		this.timeElapsed = 0;
		if (!this.hasSpawned && !this.isDead() && !$gameParty.inBattle()) {
			this.onScreen = true;
			const s = this.findClosestSpawnPoint($gamePlayer.x, $gamePlayer.y);
			const spot = this.findNearestPassablePoint(15, 15, s.x, s.y);
			if (spot) {
				this.self = this.createCrowObj();
				this.self.spawn(spot.x, spot.y);
				this.mapId = $gameMap.mapId();
				this.hasSpawned = true;
			}
		}
	}

	/**
	 * @description crow mauler follow player from last room to this room
	 */
	follow() {
		setTimeout(() => {
			MATTIE.msgAPI.footerMsg('A terrifying presence has followed you into the room...');
		}, 4000);
		setTimeout(() => {
			this.spawn();
		}, 6000);
	}

	/**
	 * @description checks if the crow mauler can follow the player, if the crow mauler was in the last room and rand chance.
	 *
	 */
	canFollow() {
		return MATTIE.util.randChance(MATTIE.betterCrowMauler.followChance * this.getScaling()) && (this.mapId === $gameMap.lastMapId());
	}

	/**
	 * @description checks a random chance to see if the crow should despawn
	 *
	 * */
	shouldDespawn() {
		return !this.isDead() && MATTIE.util.randChance(MATTIE.betterCrowMauler.despawnChance);
	}

	/**
	 * @description checks a random chance to see if the crow should appear in combat
	 *
	 * */

	shouldEnterCombat() {
		return !this.isDead() && !this.inBattle && !this.onScreen && MATTIE.util.randChance(MATTIE.betterCrowMauler.combatEnterChance * this.getScaling());
	}

	/**
	 * @description remove the current crow mauler event from the world
	 */
	despawn() {
		if (!this.isDead()) {
			this.self.removeThisEvent();
			this.hasSpawned = false;
		}
	}

	/**
	 * @description the function called to handle spawning logic every time the player enters a new room
	 */
	onEnterRoom() {
		this.inBattle = false;
		if ($gameMap.mapId() != this.mapId) this.onScreen = false;
		if (!this.isDead() && !this.onScreen) {
			if (this.canFollow() && this.crowCanSpawn()) {
				this.despawn();
				this.follow();
			} else if (this.shouldDespawn()) {
				this.despawn();
			}
		}
	}

	/**
	 * @description the update method used to spawn crow mauler
	 */
	update() {
		console.log(`chance${MATTIE.betterCrowMauler.spawnChance * this.getScaling()}`);
		if (MATTIE.util.randChance(MATTIE.betterCrowMauler.spawnChance * this.getScaling())) {
			this.enter();
		}
	}
};

/**
 * @description set up the crow mauler class and assign it to the variable we use
 */
MATTIE.betterCrowMauler.betterCrowMaulerInit = function () {
	const crowCont = new MATTIE.betterCrowMauler.CrowController();
	if (MATTIE.betterCrowMauler) { MATTIE.betterCrowMauler.crowCont = crowCont; }
};

/** @description cleanup the crow controller if it exists */
MATTIE.betterCrowMauler.betterCrowMaulerCleanup = function () {
	if (MATTIE.betterCrowMauler) { MATTIE.betterCrowMauler.crowCont = null; }
};

//--------------------------------------------------
// Multiplayer Compatibility
//--------------------------------------------------
MATTIE.betterCrowMauler.multiplayerCompatPRevFunc = MATTIE.betterCrowMauler.CrowController.prototype.getScaling;
if (MATTIE.multiplayer) {
	MATTIE.betterCrowMauler.CrowController.prototype.getScaling = function () {
		let val = (() => {
			if (MATTIE.multiplayer.getCurrentNetController) {
				const netCont = MATTIE.multiplayer.getCurrentNetController();
				return 1 / Object.keys(netCont.netPlayers).length * MATTIE.betterCrowMauler.multiplayerCompatPRevFunc.call(this);
			}

			return MATTIE.betterCrowMauler.scaler;
		})();
		if (val > 50) {
			val = 1;
		}
		return val;
	};
}