const resName = GetParentResourceName();
let hasDoorsCreator = null; // editing this is useless, don't do it
/* Utils */
async function placeObjectAndReturnCoords(object) {
	$("html").hide();
	return new Promise((resolve, reject) => {
		$.post(`https://${resName}/placeObjectAndReturnCoords`, JSON.stringify({object: object}), function(coords) {
			$("html").show();
			resolve(coords);
		})
	})
}
async function placeGasPoints() {
	$("html").hide();
	return new Promise((resolve, reject) => {
		$.post(`https://${resName}/placeGasPoints`, JSON.stringify(), function(gasPoints) {
			$("html").show();
			resolve(gasPoints);
		})
	})
}
// Open/Close menu
function openMenu(version, fullConfig) {
	$("#robbery-creator-version").text(version);
	loadSettings(fullConfig);
	loadHeists();
	loadCargoRobberies();
	loadPlannings();
    $("#robbery-creator").show()
}
function closeMenu() {
	// Resets current active tab
	$("#robbery-creator").find(".nav-link, .tab-pane").each(function() {
		if($(this).data("isDefault") == "1") {
			$(this).addClass(["active", "show"])
		} else {
			$(this).removeClass(["active", "show"])
		}
	})
	
    $("#robbery-creator").hide();
    $.post(`https://${resName}/close`, {})
}
$("#close-main-btn").click(closeMenu);
// Messages received by client
window.addEventListener('message', (event) => {
	let data = event.data;
	let action = data.action;
	switch(action) {
		case "openMenu": {
			openMenu(data.version, data.fullConfig);
			break;
		}
		case "hasDoorsCreator": {
			hasDoorsCreator = data.hasDoorsCreator;
			break;
		}
	}
})
function loadSettings(fullConfig) {
	// Generic
	setTomSelectValue("#settings_locale", fullConfig.locale)
	$("#settings_acePermission").val(fullConfig.acePermission);
	$("#settings_can_always_carry").prop("checked", fullConfig.canAlwaysCarryItem);
	$("#settings_can_receive_multiple_same_item").prop("checked", fullConfig.canReceiveMultipleTimesTheSameItem);
	$("#settings-global-timeout").val(fullConfig.globalMinutesTimeout);
	// Targeting
	setTomSelectValue("#settings-targeting-script", fullConfig.targetingScript)
	// Help notification
	setTomSelectValue("#settings-help-notification-script", fullConfig.helpNotification)	
	// NPC Mugging - Shared
	$("#enable-npc-mugging").prop("checked", fullConfig.isNpcMuggingEnabled).change();
	$("#seconds-to-mug").val(fullConfig.secondsToMug);
	// NPC Mugging - Client
	$("#can-make-npc-to-follow").prop("checked", fullConfig.clNpcMugging.canMakeFollow);
	$("#npc-mugging-cooldown-on-mugging").val(fullConfig.clNpcMugging.cooldownOnMugging);
	// NPC Mugging - Keys
	$("#npc-mugging-key-to-mug").val(fullConfig.npcMuggingKeys.keyToMug);
	$("#npc-mugging-key-to-follow").val(fullConfig.npcMuggingKeys.keyToFollow);
	$("#npc-mugging-key-to-go-away").val(fullConfig.npcMuggingKeys.keyToGoAway);
	// NPC Mugging - Server
	setNpcMuggingRobbableObjects(fullConfig.svNpcMugging.robbableObjects)
	$("#npc-mugging-minimum-police").val(fullConfig.svNpcMugging.minimumPolice)
	$("#npc-mugging-probability-police-alert").val(fullConfig.svNpcMugging.probabilityPoliceAlert)
	$("#npc-mugging-minimum-objects-amount").val(fullConfig.svNpcMugging.minimumObjectsAmount)
	$("#npc-mugging-maximum-objects-amount").val(fullConfig.svNpcMugging.maximumObjectsAmount)
	$("#minutes-before-automatically-run-away").val(fullConfig.svNpcMugging.minutesAfterAutomaticallyRunAway);
	$("#npc-mugging-max-npcs-following").val(fullConfig.svNpcMugging.maxNPCsFollowing);
	// Gas mask
	$("#gas-mask-item-name").val(fullConfig.gasMask.name);
	$("#gas-mask-item-duration").val(fullConfig.gasMask.duration);
	$("#gas-mask-lose-on-use").prop("checked", fullConfig.gasMask.loseOnUse);
	$("#gas-mask-cloth-id").val(fullConfig.gasMaskClothId);
	// Drill
	$("#drill-required-for-cargo-robbery").prop("checked", fullConfig.drill.requiredForCargoRobbery);
	$("#drill-item-name").val(fullConfig.drill.name);
	$("#drill-lose-on-use").prop("checked", fullConfig.drill.loseOnUse);
	// Cargo robbery
	$("#cargo-robbery-minutes-delete-after-robbed").val(fullConfig.cargoRobbery.minutesBeforeDeleteAfterRobbed);
	$("#cargo-robbery-minutes-delete-after-arrived").val(fullConfig.cargoRobbery.minutesBeforeDeleteAfterArrived);
	$("#cargo-robbery-max-minutes-difference-from-defined-date").val(fullConfig.cargoRobbery.maxMinutesDifferenceFromDefinedDate);
	$("#cargo-robbery-probability-police-alert").val(fullConfig.cargoRobbery.probabilityPoliceAlert);
	// Discord logs
	$("#settings_isDiscordLogActive").prop("checked", fullConfig.areDiscordLogsActive);
	toggleDiscordLogsInSettings(fullConfig.areDiscordLogsActive);
	$("#settings_discordWebhook").val(fullConfig.mainDiscordWebhook);
	for(const[logType, webhook] of Object.entries(fullConfig.specificWebhooks)) {
		$("#settings_specific_webhooks").find(`[data-log-type="${logType}"]`).val(webhook);
	}
}
/* Discord logs */
function toggleDiscordLogsInSettings(enable) {
	$("#settings_discordWebhook").prop("disabled", !enable);
	$("#settings_discordWebhook").prop("required", enable);
	
	$("#settings_specific_webhooks").find(`.form-control`).prop("disabled", !enable);
}
$("#settings_isDiscordLogActive").change(function() {
	let enabled = $(this).prop("checked");
	toggleDiscordLogsInSettings(enabled);
})
function getSeparatedDiscordWebhooks() {
	let webhooks = {};
	$("#settings_specific_webhooks").find(".form-control").each(function(index, element) {
		let logType = $(element).data("logType");
		let webhook = $(element).val();
		if(webhook) {
			webhooks[logType] = webhook;
		}
	});
	return webhooks;
}
/* Discord logs END */
$("#settings").submit(async function(event) {
	if(isThereAnyErrorInForm(event)) return;
	let clientSettings = {
		gasMaskClothId: parseInt( $("#gas-mask-cloth-id").val() ),
		targetingScript:$("#settings-targeting-script").val(),
		helpNotification: $("#settings-help-notification-script").val(),
		// NPC Mugging - Keys
		npcMuggingKeys: {
			keyToMug: parseInt($("#npc-mugging-key-to-mug").val()),
			keyToFollow: parseInt($("#npc-mugging-key-to-follow").val()),
			keyToGoAway: parseInt($("#npc-mugging-key-to-go-away").val()),
		}
	}
	let sharedSettings = {
		locale: $("#settings_locale").val(),
	}
	let serverSettings = {
		
		// Generic
		canAlwaysCarryItem: $("#settings_can_always_carry").prop("checked"),
		canReceiveMultipleTimesTheSameItem: $("#settings_can_receive_multiple_same_item").prop("checked"),
		acePermission: $("#settings_acePermission").val(),
		globalMinutesTimeout: parseInt( $("#settings-global-timeout").val() ),
		gasMask: {
			name: $("#gas-mask-item-name").val(),
			duration: parseInt( $("#gas-mask-item-duration").val() ),
			loseOnUse: $("#gas-mask-lose-on-use").prop("checked"),
		},
		drill: {
			requiredForCargoRobbery: $("#drill-required-for-cargo-robbery").prop("checked"),
			name: $("#drill-item-name").val(),
			loseOnUse: $("#drill-lose-on-use").prop("checked")
		},
		
		cargoRobbery: {
			minutesBeforeDeleteAfterRobbed: parseInt( $("#cargo-robbery-minutes-delete-after-robbed").val() ),
			minutesBeforeDeleteAfterArrived: parseInt( $("#cargo-robbery-minutes-delete-after-arrived").val() ),
			maxMinutesDifferenceFromDefinedDate: parseInt( $("#cargo-robbery-max-minutes-difference-from-defined-date").val() ),
			probabilityPoliceAlert: parseInt( $("#cargo-robbery-probability-police-alert").val() ),
		},
		// Discord logs
		areDiscordLogsActive: $("#settings_isDiscordLogActive").prop("checked"),
		mainDiscordWebhook: $("#settings_discordWebhook").val(),
		specificWebhooks: getSeparatedDiscordWebhooks(),
	}
	const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
		clientSettings: clientSettings,
		sharedSettings: sharedSettings,
		serverSettings: serverSettings,
	}));
	showServerResponse(response)
	refreshTranslations(sharedSettings.locale);
});
/*
██   ██ ███████ ██ ███████ ████████ ███████ 
██   ██ ██      ██ ██         ██    ██      
███████ █████   ██ ███████    ██    ███████ 
██   ██ ██      ██      ██    ██         ██ 
██   ██ ███████ ██ ███████    ██    ███████ 
*/
let heistsDatatable = $("#heists-container").DataTable( {
	"lengthMenu": [10, 15, 20],
	"createdRow": function ( row, data, index ) {
		$(row).addClass("clickable");
		$(row).click(function() {
			let id = parseInt( data[0] );
			editHeist(id);
		})
	},
});
let heists = {};
function loadHeists() {
	$.post(`https://${resName}/getAllHeists`, {}, async function(rawHeists) {
		// Manually create the table to avoid incompatibilities due table indexing
		heists = {};
		for(const[k, heistData] of Object.entries(rawHeists)) {
			heists[heistData.id] = heistData;
		}
		heistsDatatable.clear();
		for(const[id, heistData] of Object.entries(heists)) {
			heistsDatatable.row.add([
				id,
				heistData.label,
				heistData.stages.length
			]);
		}
		heistsDatatable.draw();
	})
}
function setDefaultDataOfHeistModal() {
	$("#heist-label").val("Default");
	$("#heist-minimum-police").val(0);
	$("#heist-timeout").val(120);
	$("#heist-reset").val(60);
	$("#heist-time-limit-minutes").val(0);
	$("#heist-stages").empty();
}
$("#new-heist-btn").click(function() {
	let heistModal = $("#heist-modal");
	// Converts from edit modal to create modal
	heistModal.data("action", "create");
	
	$("#delete-heist-btn").hide();
	$("#save-heist-btn").text( getLocalizedText("menu:create") );
	
	setDefaultDataOfHeistModal();
	heistModal.modal("show");
})
function editHeist(id) {
	let heistData = heists[id];
	let heistModal = $("#heist-modal");
	heistModal.data("action", "edit");
	heistModal.data("heistId", id);
	
	$("#delete-heist-btn").show();
	$("#save-heist-btn").text( getLocalizedText("menu:save") );
	$("#heist-label").val(heistData.label);
	$("#heist-minimum-police").val(heistData.minimumPolice);
	$("#heist-timeout").val(heistData.timeoutMinutes);
	$("#heist-reset").val(heistData.minutesBeforeReset);
	$("#heist-time-limit-minutes").val(heistData.timeLimitMinutes);
	$("#heist-stages").empty();
	heistData.stages.forEach(stage => {
		addHeistStage(stage);
	})
	heistModal.modal("show");
}
/*
██████   ██████  ██████  ██████   █████  ██████  ██      ███████      ██████  ██████       ██ ███████  ██████ ████████ ███████ 
██   ██ ██    ██ ██   ██ ██   ██ ██   ██ ██   ██ ██      ██          ██    ██ ██   ██      ██ ██      ██         ██    ██      
██████  ██    ██ ██████  ██████  ███████ ██████  ██      █████       ██    ██ ██████       ██ █████   ██         ██    ███████ 
██   ██ ██    ██ ██   ██ ██   ██ ██   ██ ██   ██ ██      ██          ██    ██ ██   ██ ██   ██ ██      ██         ██         ██ 
██   ██  ██████  ██████  ██████  ██   ██ ██████  ███████ ███████      ██████  ██████   █████  ███████  ██████    ██    ███████ 
*/
function getAllRobbableObjectsFromStageDiv(stageDiv) {
	let robbableObjects = [];
	const stepMethod = "ROBBABLE_OBJECT"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let robbableObjectDiv = $(this);
		let robbableObject = {
			method: stepMethod,
			type: robbableObjectDiv.find(".object-type").val(),
			coords: {
				x: parseFloat( robbableObjectDiv.find(".coords-x").val() ),
				y: parseFloat( robbableObjectDiv.find(".coords-y").val() ),
				z: parseFloat( robbableObjectDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( robbableObjectDiv.find(".heading").val() ),	
			blipData : robbableObjectDiv.data("blipData") || getDefaultBlipCustomization(),
			item: {
				type: robbableObjectDiv.find(".item-type").val(),
				name: robbableObjectDiv.find(".item-name").val(),
				minQuantity: parseInt( robbableObjectDiv.find(".item-min-quantity").val() ),
				maxQuantity: parseInt( robbableObjectDiv.find(".item-max-quantity").val() ),
			},
			isOptional: robbableObjectDiv.find(".is-optional-checkbox").prop("checked"),
			isInvisible: robbableObjectDiv.find(".is-invisible-checkbox").prop("checked"),
		}
		robbableObjects.push(robbableObject);
	});
	return robbableObjects
}
async function addRobbableObjectHeist(stageDiv, robbableObject) {
	let robbableObjectDiv = $(`
		
			${getLocalizedText("menu:robbable_object")}
			
			
			
		 
	`);
	robbableObjectDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = robbableObjectDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			robbableObjectDiv.find(".coords-x").val(data.coords.x);
			robbableObjectDiv.find(".coords-y").val(data.coords.y);
			robbableObjectDiv.find(".coords-z").val(data.coords.z);
			robbableObjectDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	robbableObjectDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = robbableObjectDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		robbableObjectDiv.data("blipData", blipData);
	})
	robbableObjectDiv.find(".delete-step-btn").click(function() {
		robbableObjectDiv.remove();
	})
	robbableObjectDiv.find(".choose-robbable-item-btn").click(async function() {
		let objectType = robbableObjectDiv.find(".robbable-item").find(".item-type").val();
		let objectName = await objectDialog(objectType);
		robbableObjectDiv.find(".item-name").val(objectName);
	}).tooltip();
	// Sets value if the object is not new
	if(robbableObject) {
		robbableObjectDiv.find(".object-type").val(robbableObject.type);
		robbableObjectDiv.find(".coords-x").val(robbableObject.coords.x);
		robbableObjectDiv.find(".coords-y").val(robbableObject.coords.y);
		robbableObjectDiv.find(".coords-z").val(robbableObject.coords.z);
		robbableObjectDiv.find(".heading").val(robbableObject.heading);
		robbableObjectDiv.data("blipData", robbableObject.blipData);
		robbableObjectDiv.find(".item-type").val(robbableObject.item.type);
		robbableObjectDiv.find(".item-name").val(robbableObject.item.name);
		robbableObjectDiv.find(".item-min-quantity").val(robbableObject.item.minQuantity);
		robbableObjectDiv.find(".item-max-quantity").val(robbableObject.item.maxQuantity);
		robbableObjectDiv.find(".is-optional-checkbox").prop("checked", robbableObject.isOptional);
		robbableObjectDiv.find(".is-invisible-checkbox").prop("checked", robbableObject.isInvisible);
	}
	stageDiv.find(".steps-list").append(robbableObjectDiv);
}
/*
██████   █████  ██ ███    ██ ████████ ██ ███    ██  ██████  ███████ 
██   ██ ██   ██ ██ ████   ██    ██    ██ ████   ██ ██       ██      
██████  ███████ ██ ██ ██  ██    ██    ██ ██ ██  ██ ██   ███ ███████ 
██      ██   ██ ██ ██  ██ ██    ██    ██ ██  ██ ██ ██    ██      ██ 
██      ██   ██ ██ ██   ████    ██    ██ ██   ████  ██████  ███████ 
*/
function getAllPaintingsFromStageDiv(stageDiv) {
	let paintings = [];
	const stepMethod = "PAINTING"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let paintingDiv = $(this);
		let painting = {
			method: stepMethod,
			type: paintingDiv.find(".object-type").val(),
			coords: {
				x: parseFloat( paintingDiv.find(".coords-x").val() ),
				y: parseFloat( paintingDiv.find(".coords-y").val() ),
				z: parseFloat( paintingDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( paintingDiv.find(".heading").val() ),	
			blipData : paintingDiv.data("blipData") || getDefaultBlipCustomization(),
			item: {
				type: paintingDiv.find(".item-type").val(),
				name: paintingDiv.find(".item-name").val(),
				minQuantity: parseInt( paintingDiv.find(".item-min-quantity").val() ),
				maxQuantity: parseInt( paintingDiv.find(".item-max-quantity").val() ),
			},
			isOptional: paintingDiv.find(".is-optional-checkbox").prop("checked"),
			isInvisible: paintingDiv.find(".is-invisible-checkbox").prop("checked"),
		}
		paintings.push(painting);
	});
	return paintings
}
async function addPaintingHeist(stageDiv, painting) {
	let paintingDiv = $(`
		
			${getLocalizedText("menu:painting")}
			
			
			
		 
	`);
	paintingDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = paintingDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			paintingDiv.find(".coords-x").val(data.coords.x);
			paintingDiv.find(".coords-y").val(data.coords.y);
			paintingDiv.find(".coords-z").val(data.coords.z);
			paintingDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	paintingDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = paintingDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		paintingDiv.data("blipData", blipData);
	})
	paintingDiv.find(".delete-robbable-object-btn").click(function() {
		paintingDiv.remove();
	})
	paintingDiv.find(".choose-robbable-item-btn").click(async function() {
		let objectType = paintingDiv.find(".robbable-item").find(".item-type").val();
		let objectName = await objectDialog(objectType);
		paintingDiv.find(".item-name").val(objectName);
	}).tooltip();
	// Sets value if the object is not new
	if(painting) {
		paintingDiv.find(".object-type").val(painting.type);
		paintingDiv.find(".coords-x").val(painting.coords.x);
		paintingDiv.find(".coords-y").val(painting.coords.y);
		paintingDiv.find(".coords-z").val(painting.coords.z);
		paintingDiv.find(".heading").val(painting.heading);
		paintingDiv.data("blipData", painting.blipData);
		paintingDiv.find(".item-type").val(painting.item.type);
		paintingDiv.find(".item-name").val(painting.item.name);
		paintingDiv.find(".item-min-quantity").val(painting.item.minQuantity);
		paintingDiv.find(".item-max-quantity").val(painting.item.maxQuantity);
		paintingDiv.find(".is-optional-checkbox").prop("checked", painting.isOptional);
		paintingDiv.find(".is-invisible-checkbox").prop("checked", painting.isInvisible);
	}
	stageDiv.find(".steps-list").append(paintingDiv);
}
/*
 ██████  ██       █████  ███████ ███████     ██████  ██ ███████ ██████  ██       █████  ██    ██ ███████ 
██       ██      ██   ██ ██      ██          ██   ██ ██ ██      ██   ██ ██      ██   ██  ██  ██  ██      
██   ███ ██      ███████ ███████ ███████     ██   ██ ██ ███████ ██████  ██      ███████   ████   ███████ 
██    ██ ██      ██   ██      ██      ██     ██   ██ ██      ██ ██      ██      ██   ██    ██         ██ 
 ██████  ███████ ██   ██ ███████ ███████     ██████  ██ ███████ ██      ███████ ██   ██    ██    ███████ 
*/
function getAllGlassDisplaysFromStageDiv(stageDiv) {
	let glassDisplays = [];
	const stepMethod = "GLASS_DISPLAY"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let glassDisplayDiv = $(this);
		let glassDisplay = {
			method: stepMethod,
			type: glassDisplayDiv.find(".object-type").val(),
			coords: {
				x: parseFloat( glassDisplayDiv.find(".coords-x").val() ),
				y: parseFloat( glassDisplayDiv.find(".coords-y").val() ),
				z: parseFloat( glassDisplayDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( glassDisplayDiv.find(".heading").val() ),	
			blipData : glassDisplayDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: glassDisplayDiv.find(".is-optional-checkbox").prop("checked"),
			requiredItem: getRequiredItemFromDiv(glassDisplayDiv),
			item: {
				type: glassDisplayDiv.find(".item-type").val(),
				name: glassDisplayDiv.find(".item-name").val(),
				minQuantity: parseInt( glassDisplayDiv.find(".item-min-quantity").val() ),
				maxQuantity: parseInt( glassDisplayDiv.find(".item-max-quantity").val() ),
			},
		}
		glassDisplays.push(glassDisplay);
	});
	return glassDisplays
}
async function addGlassDisplayHeist(stageDiv, glassDisplay) {
	let stepDiv = $(`
		
			${getLocalizedText("menu:glass_display")}
			
			
			
			
		 
	`);
	stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = stepDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			stepDiv.find(".coords-x").val(data.coords.x);
			stepDiv.find(".coords-y").val(data.coords.y);
			stepDiv.find(".coords-z").val(data.coords.z);
			stepDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	stepDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = stepDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		stepDiv.data("blipData", blipData);
	})
	stepDiv.find(".delete-step-btn").click(function() {
		stepDiv.remove();
	})
	stepDiv.find(".choose-robbable-item-btn").click(async function() {
		let objectType = stepDiv.find(".robbable-item").find(".item-type").val();
		let objectName = await objectDialog(objectType);
		stepDiv.find(".item-name").val(objectName);
	}).tooltip();
	stepDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		stepDiv.find(".required-item-name").val(itemName);
	})
	stepDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(stepDiv.find(".required-item-div"));
	});
	// Sets value if the object is not new
	if(glassDisplay) {
		stepDiv.find(".object-type").val(glassDisplay.type);
		stepDiv.find(".coords-x").val(glassDisplay.coords.x);
		stepDiv.find(".coords-y").val(glassDisplay.coords.y);
		stepDiv.find(".coords-z").val(glassDisplay.coords.z);
		stepDiv.find(".heading").val(glassDisplay.heading);
		stepDiv.data("blipData", glassDisplay.blipData);
		stepDiv.find(".is-optional-checkbox").prop("checked", glassDisplay.isOptional);
		if(glassDisplay.requiredItem) {
			stepDiv.find(".required-item-name").val(glassDisplay.requiredItem.name);
			stepDiv.find(".required-item-quantity").val(glassDisplay.requiredItem.quantity);
			stepDiv.find(".required-item-lose-on-use-checkbox").prop("checked", glassDisplay.requiredItem.loseOnUse);
		}
		stepDiv.find(".item-type").val(glassDisplay.item.type);
		stepDiv.find(".item-name").val(glassDisplay.item.name);
		stepDiv.find(".item-min-quantity").val(glassDisplay.item.minQuantity);
		stepDiv.find(".item-max-quantity").val(glassDisplay.item.maxQuantity);
	}
	stepDiv.find(".requires-an-item-checkbox").prop("checked", glassDisplay?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(stepDiv);
}
/*
██       █████  ███████ ███████ ██████      ██████  ██████  ██ ██      ██      ███████ 
██      ██   ██ ██      ██      ██   ██     ██   ██ ██   ██ ██ ██      ██      ██      
██      ███████ ███████ █████   ██████      ██   ██ ██████  ██ ██      ██      ███████ 
██      ██   ██      ██ ██      ██   ██     ██   ██ ██   ██ ██ ██      ██           ██ 
███████ ██   ██ ███████ ███████ ██   ██     ██████  ██   ██ ██ ███████ ███████ ███████ 
*/
function getAllLaserDrillsFromStageDiv(stageDiv) {
	let laserDrills = [];
	const stepMethod = "LASER_DRILL"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let laserDrillDiv = $(this);
		let stepData = {
			type: "laser_drill", // for the object model
			method: stepMethod,
			coords: {
				x: parseFloat( laserDrillDiv.find(".coords-x").val() ),
				y: parseFloat( laserDrillDiv.find(".coords-y").val() ),
				z: parseFloat( laserDrillDiv.find(".coords-z").val() ),
			},
			blipData : laserDrillDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: laserDrillDiv.find(".is-optional-checkbox").prop("checked"),
			duration: parseInt( laserDrillDiv.find(".duration").val() ),
			requiredItem: getRequiredItemFromDiv( laserDrillDiv ),
			minObjectsAmount: parseInt( laserDrillDiv.find(".min-objects-amount").val() ),
			maxObjectsAmount: parseInt( laserDrillDiv.find(".max-objects-amount").val() ),
			items: getAllItemsFromDiv(laserDrillDiv),
		}
		laserDrills.push(stepData);
	});
	return laserDrills
}
async function addLaserDrillHeist(stageDiv, laserDrill) {
	let stepDiv = $(`
		
			${getLocalizedText("menu:laser_drill")}
			
			
			
			
			
			
				
			
			
		 
	`);
	stepDiv.find(".delete-step-btn").click(function() { stepDiv.remove(); })
	stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = stepDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			stepDiv.find(".coords-x").val(data.coords.x);
			stepDiv.find(".coords-y").val(data.coords.y);
			stepDiv.find(".coords-z").val(data.coords.z);
			stepDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	stepDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = stepDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		stepDiv.data("blipData", blipData);
	})
	stepDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(stepDiv.find(".required-item-div"));
	});
	stepDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		stepDiv.find(".required-item-name").val(itemName);
	})
	stepDiv.find(".add-item-btn").click(function() {
		addItemInStep(stepDiv);
	})
	// Sets value if the object is not new
	if(laserDrill) {
		stepDiv.find(".coords-x").val(laserDrill.coords.x);
		stepDiv.find(".coords-y").val(laserDrill.coords.y);
		stepDiv.find(".coords-z").val(laserDrill.coords.z);
		stepDiv.data("blipData", laserDrill.blipData);
		stepDiv.find(".is-optional-checkbox").prop("checked", laserDrill.isOptional);
		stepDiv.find(".duration").val(laserDrill.duration);
		if(laserDrill.requiredItem) {
			stepDiv.find(".required-item-name").val(laserDrill.requiredItem.name);
			stepDiv.find(".required-item-quantity").val(laserDrill.requiredItem.quantity);
			stepDiv.find(".required-item-lose-on-use-checkbox").prop("checked", laserDrill.requiredItem.loseOnUse);
		}
		stepDiv.find(".min-objects-amount").val(laserDrill.minObjectsAmount);
		stepDiv.find(".max-objects-amount").val(laserDrill.maxObjectsAmount);
		if(laserDrill.items) {
			laserDrill.items.forEach(item => {
				addItemInStep(stepDiv, item);
			});
		}
	} else {
		stepDiv.find(".min-objects-amount").val(1);
		stepDiv.find(".max-objects-amount").val(1);
		addItemInStep(stepDiv);
	}
	stepDiv.find(".requires-an-item-checkbox").prop("checked", laserDrill?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(stepDiv);
}
/*
 ██████  ██    ██  █████  ██████  ██████  ███████ 
██       ██    ██ ██   ██ ██   ██ ██   ██ ██      
██   ███ ██    ██ ███████ ██████  ██   ██ ███████ 
██    ██ ██    ██ ██   ██ ██   ██ ██   ██      ██ 
 ██████   ██████  ██   ██ ██   ██ ██████  ███████ 
*/
function getGuardsFromGuardsDiv(guardsDiv) {
	let guards = [];
	guardsDiv.find(".guard").each(function() {
		let guardDiv = $(this);
		let guard = {
			model: guardDiv.find(".guard-model").val(),
			weapon: guardDiv.find(".guard-weapon").val(),
			coords: {
				x: parseFloat( guardDiv.find(".coords-x").val() ),
				y: parseFloat( guardDiv.find(".coords-y").val() ),
				z: parseFloat( guardDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( guardDiv.find(".heading").val() ),
		}
		guards.push(guard);
	});
	return guards;
}
function getAllGuardsFromStageDiv(stageDiv) {
	let guards = [];
	const stepMethod = "GUARDS"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let guardsDiv = $(this);
		let currentGuards = {
			method: stepMethod,
			difficulty: parseInt( guardsDiv.find(".difficulty").val() ),
			headshotOneshot: guardsDiv.find(".headshot-oneshot-checkbox").prop("checked"),
			hideBlip: guardsDiv.find(".hide-blip-checkbox").prop("checked"),
			guardsList: getGuardsFromGuardsDiv(guardsDiv),
		}
		guards.push(currentGuards);
	});
	return guards
}
function addGuardToGuardsDiv(guardsDiv, guard) {
	let guardDiv = $(`
		
	`);
	
	guardDiv.find(".delete-guard-btn").click(function() {
		guardDiv.remove();
	});
	guardDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let data = await placeObjectAndReturnCoords();
		if(data) {
			guardDiv.find(".coords-x").val(data.coords.x);
			guardDiv.find(".coords-y").val(data.coords.y);
			guardDiv.find(".coords-z").val(data.coords.z);
			guardDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	if(guard) {
		guardDiv.find(".guard-model").val(guard.model);
		guardDiv.find(".guard-weapon").val(guard.weapon);
		guardDiv.find(".coords-x").val(guard.coords.x);
		guardDiv.find(".coords-y").val(guard.coords.y);
		guardDiv.find(".coords-z").val(guard.coords.z);
		guardDiv.find(".heading").val(guard.heading);
	}
	guardsDiv.find(".guards-list").append(guardDiv);
}
async function addGuardsHeist(stageDiv, guards) {
	let guardsDiv = $(`
		
			${getLocalizedText("menu:guards")}
			
			${ getLocalizedText("menu:guards_list") }
			
			
			
				
			
			
		 
	`);
	guardsDiv.find(".difficulty").tooltip();
	guardsDiv.find(".delete-step-btn").click(function() {
		guardsDiv.remove();
	});
	guardsDiv.find(".add-guard-btn").click(function() {
		addGuardToGuardsDiv(guardsDiv);
	});
	if(guards) {
		guardsDiv.find(".difficulty").val(guards.difficulty);
		guardsDiv.find(".headshot-oneshot-checkbox").prop("checked", guards.headshotOneshot);
		guardsDiv.find(".hide-blip-checkbox").prop("checked", guards.hideBlip);
		if(guards.guardsList) {
			guards.guardsList.forEach(guard => {
				addGuardToGuardsDiv(guardsDiv, guard);
			});
		}
	}
	stageDiv.find(".steps-list").append(guardsDiv);
}
/*
███████  █████  ███████ ███████ ███████ 
██      ██   ██ ██      ██      ██      
███████ ███████ █████   █████   ███████ 
     ██ ██   ██ ██      ██           ██ 
███████ ██   ██ ██      ███████ ███████ 
*/
function getAllSafesFromStageDiv(stageDiv) {
	let safes = [];
	const stepMethod = "SAFE"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let safeDiv = $(this);
		let safe = {
			method: stepMethod,
			type: safeDiv.find(".object-type").val(),
			coords: {
				x: parseFloat( safeDiv.find(".coords-x").val() ),
				y: parseFloat( safeDiv.find(".coords-y").val() ),
				z: parseFloat( safeDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( safeDiv.find(".heading").val() ),	
			blipData : safeDiv.data("blipData") || getDefaultBlipCustomization(),
			items: getAllItemsFromDiv(safeDiv),
			difficulty: parseInt( safeDiv.find(".difficulty").val() ),
			minObjectsAmount: parseInt( safeDiv.find(".min-objects-amount").val() ),
			maxObjectsAmount: parseInt( safeDiv.find(".max-objects-amount").val() ),
			isOptional: safeDiv.find(".is-optional-checkbox").prop("checked"),
			isInvisible: safeDiv.find(".is-invisible-checkbox").prop("checked"),
			requiredItem: getRequiredItemFromDiv( safeDiv ),
			alarms: safeDiv.data("alarms")
		}
		safes.push(safe);
	});
	return safes
}
function addSafeHeist(stageDiv, safe) {
	let safeDiv = $(`
		
			${getLocalizedText("menu:safe")}
			
			
			
			
			
			
				
			
			
		 
	`);
	safeDiv.find(".setup-alarm-btn").click(async function() {
		let currentAlarms = safeDiv.data("alarms");
		let alarms = await alarmsDialog(currentAlarms);
		
		safeDiv.data("alarms", alarms);
	}).tooltip();
	safeDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(safeDiv.find(".required-item-div"));
	});
	safeDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		safeDiv.find(".required-item-name").val(itemName);
	})
	safeDiv.find(".add-item-btn").click(function() {
		addItemInStep(safeDiv);
	})
	safeDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = safeDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			safeDiv.find(".coords-x").val(data.coords.x);
			safeDiv.find(".coords-y").val(data.coords.y);
			safeDiv.find(".coords-z").val(data.coords.z);
			safeDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	safeDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = safeDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		safeDiv.data("blipData", blipData);
	})
	safeDiv.find(".delete-step-btn").click(function() {
		safeDiv.remove();
	})
	// Sets value if the object is not new
	if(safe) {
		safeDiv.find(".object-type").val(safe.type);
		safeDiv.find(".coords-x").val(safe.coords.x);
		safeDiv.find(".coords-y").val(safe.coords.y);
		safeDiv.find(".coords-z").val(safe.coords.z);
		safeDiv.find(".heading").val(safe.heading);
		safeDiv.find(".difficulty").val(safe.difficulty);
		safeDiv.find(".min-objects-amount").val(safe.minObjectsAmount);
		safeDiv.find(".max-objects-amount").val(safe.maxObjectsAmount);
		safeDiv.find(".is-optional-checkbox").prop("checked", safe.isOptional);
		safeDiv.find(".is-invisible-checkbox").prop("checked", safe.isInvisible);
		safeDiv.data("blipData", safe.blipData);
		safeDiv.data("alarms", safe.alarms);
		safe.items.forEach(item => {
			addItemInStep(safeDiv, item);
		})
		if(safe.requiredItem) {
			safeDiv.find(".required-item-name").val(safe.requiredItem.name);
			safeDiv.find(".required-item-quantity").val(safe.requiredItem.quantity);
			safeDiv.find(".required-item-lose-on-use-checkbox").prop("checked", safe.requiredItem.loseOnUse);
		}
	} else {
		// If safe didn't exist, it will also add 1 empty item to it
		addItemInStep(safeDiv);
	}
	safeDiv.find(".requires-an-item-checkbox").prop("checked", safe?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(safeDiv);
}
/*
██   ██  █████   ██████ ██   ██  █████  ██████  ██      ███████     ██████   █████  ███    ██ ███████ ██      ███████ 
██   ██ ██   ██ ██      ██  ██  ██   ██ ██   ██ ██      ██          ██   ██ ██   ██ ████   ██ ██      ██      ██      
███████ ███████ ██      █████   ███████ ██████  ██      █████       ██████  ███████ ██ ██  ██ █████   ██      ███████ 
██   ██ ██   ██ ██      ██  ██  ██   ██ ██   ██ ██      ██          ██      ██   ██ ██  ██ ██ ██      ██           ██ 
██   ██ ██   ██  ██████ ██   ██ ██   ██ ██████  ███████ ███████     ██      ██   ██ ██   ████ ███████ ███████ ███████ 
*/
function getAllHackablePanelsFromStageDiv(stageDiv) {
	let hackablePanels = [];
	const stepMethod = "HACKABLE_PANEL"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let hackablePanelDiv = $(this);
		let hackablePanel = {
			type: "hackable_panel", // for the object model
			method: stepMethod,
			coords: {
				x: parseFloat( hackablePanelDiv.find(".coords-x").val() ),
				y: parseFloat( hackablePanelDiv.find(".coords-y").val() ),
				z: parseFloat( hackablePanelDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( hackablePanelDiv.find(".heading").val() ),	
			blipData : hackablePanelDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: hackablePanelDiv.find(".is-optional-checkbox").prop("checked"),
			difficulty: parseInt( hackablePanelDiv.find(".difficulty").val() ),
			isInvisible: hackablePanelDiv.find(".is-invisible-checkbox").prop("checked"),
			doorsToOpen: hackablePanelDiv.data("doorsToOpen"),
			alarms: hackablePanelDiv.data("alarms"),
			requiredItem: getRequiredItemFromDiv( hackablePanelDiv ),
			minigameToUse: hackablePanelDiv.find(".minigame-to-use").val()
		}
		hackablePanels.push(hackablePanel);
	});
	return hackablePanels
}
function addHackablePanelHeist(stageDiv, hackablePanel) {
	let hackablePanelDiv = $(`
		
			${getLocalizedText("menu:hackable_panel")}
			
			
				
				
			 
			
		 
	`);
	hackablePanelDiv.find(".delete-step-btn").click(function() {
		hackablePanelDiv.remove();
	})
	hackablePanelDiv.find(".difficulty").tooltip();
	hackablePanelDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(hackablePanelDiv.find(".required-item-div"));
	});
	hackablePanelDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		hackablePanelDiv.find(".required-item-name").val(itemName);
	})
	hackablePanelDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let data = await placeObjectAndReturnCoords("hackable_panel");
		if(data) {
			hackablePanelDiv.find(".coords-x").val(data.coords.x);
			hackablePanelDiv.find(".coords-y").val(data.coords.y);
			hackablePanelDiv.find(".coords-z").val(data.coords.z);
			hackablePanelDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	hackablePanelDiv.find(".setup-alarm-btn").click(async function() {
		let currentAlarms = hackablePanelDiv.data("alarms");
		let alarms = await alarmsDialog(currentAlarms);
		
		hackablePanelDiv.data("alarms", alarms);
	}).tooltip();
	hackablePanelDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = hackablePanelDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		hackablePanelDiv.data("blipData", blipData);
	})
	hackablePanelDiv.find(".choose-doors-btn").click(async function() {
		let doors = await doorsDialog( hackablePanelDiv.data("doorsToOpen") );
		hackablePanelDiv.data("doorsToOpen", doors);
	})
	// Sets value if the object is not new
	if(hackablePanel) {
		hackablePanelDiv.find(".coords-x").val(hackablePanel.coords.x);
		hackablePanelDiv.find(".coords-y").val(hackablePanel.coords.y);
		hackablePanelDiv.find(".coords-z").val(hackablePanel.coords.z);
		hackablePanelDiv.find(".heading").val(hackablePanel.heading);
		hackablePanelDiv.find(".difficulty").val(hackablePanel.difficulty);
		hackablePanelDiv.find(".is-optional-checkbox").prop("checked", hackablePanel.isOptional);
		hackablePanelDiv.find(".is-invisible-checkbox").prop("checked", hackablePanel.isInvisible);
		hackablePanelDiv.data("blipData", hackablePanel.blipData);
		hackablePanelDiv.data("doorsToOpen", hackablePanel.doorsToOpen);
		hackablePanelDiv.data("alarms", hackablePanel.alarms);
		hackablePanelDiv.find(".minigame-to-use").val(hackablePanel.minigameToUse || "bruteforce");
		if(hackablePanel.requiredItem) {
			hackablePanelDiv.find(".required-item-name").val(hackablePanel.requiredItem.name);
			hackablePanelDiv.find(".required-item-quantity").val(hackablePanel.requiredItem.quantity);
			hackablePanelDiv.find(".required-item-lose-on-use-checkbox").prop("checked", hackablePanel.requiredItem.loseOnUse);
		}
	}
	hackablePanelDiv.find(".requires-an-item-checkbox").prop("checked", hackablePanel?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(hackablePanelDiv);
}
/*
████████ ██   ██ ███████ ██████  ███    ███  █████  ██           ██████ ██   ██  █████  ██████   ██████  ███████ ███████ 
   ██    ██   ██ ██      ██   ██ ████  ████ ██   ██ ██          ██      ██   ██ ██   ██ ██   ██ ██       ██      ██      
   ██    ███████ █████   ██████  ██ ████ ██ ███████ ██          ██      ███████ ███████ ██████  ██   ███ █████   ███████ 
   ██    ██   ██ ██      ██   ██ ██  ██  ██ ██   ██ ██          ██      ██   ██ ██   ██ ██   ██ ██    ██ ██           ██ 
   ██    ██   ██ ███████ ██   ██ ██      ██ ██   ██ ███████      ██████ ██   ██ ██   ██ ██   ██  ██████  ███████ ███████ 
*/
function getAllThermalChargesFromStageDiv(stageDiv) {
	let thermalCharges = [];
	const stepMethod = "THERMAL_CHARGE"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let thermalChargeDiv = $(this);
		let thermalCharge = {
			type: "thermal_charge", // for the object model
			method: stepMethod,
			coords: {
				x: parseFloat( thermalChargeDiv.find(".coords-x").val() ),
				y: parseFloat( thermalChargeDiv.find(".coords-y").val() ),
				z: parseFloat( thermalChargeDiv.find(".coords-z").val() ),
			},
			heading: parseFloat( thermalChargeDiv.find(".heading").val() ),
			blipData : thermalChargeDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: thermalChargeDiv.find(".is-optional-checkbox").prop("checked"),
			doorsToOpen: thermalChargeDiv.data("doorsToOpen"),
			duration: parseInt( thermalChargeDiv.find(".duration").val() ),
			requiredItem: getRequiredItemFromDiv(thermalChargeDiv),
			alarms: thermalChargeDiv.data("alarms"),
		}
		thermalCharges.push(thermalCharge);
	});
	return thermalCharges
}
function addThermalChargeHeist(stageDiv, thermalCharge) {
	let thermalChargeHeistDiv = $(`
		
			${getLocalizedText("menu:thermal_charge")}
			
			
				
				
			 
			
		 
	`);
	thermalChargeHeistDiv.find(".setup-alarm-btn").click(async function() {
		let currentAlarms = thermalChargeHeistDiv.data("alarms");
		let alarms = await alarmsDialog(currentAlarms);
		
		thermalChargeHeistDiv.data("alarms", alarms);
	}).tooltip();
	thermalChargeHeistDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(thermalChargeHeistDiv.find(".required-item-div"));
	});
	thermalChargeHeistDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		thermalChargeHeistDiv.find(".required-item-name").val(itemName);
	})
	thermalChargeHeistDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let data = await placeObjectAndReturnCoords("thermal_charge");
		if(data) {
			thermalChargeHeistDiv.find(".coords-x").val(data.coords.x);
			thermalChargeHeistDiv.find(".coords-y").val(data.coords.y);
			thermalChargeHeistDiv.find(".coords-z").val(data.coords.z);
			thermalChargeHeistDiv.find(".heading").val(data.heading);
		}
	}).tooltip();
	thermalChargeHeistDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = thermalChargeHeistDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData);
		thermalChargeHeistDiv.data("blipData", blipData);
	});
	thermalChargeHeistDiv.find(".choose-doors-btn").click(async function() {
		let doors = await doorsDialog( thermalChargeHeistDiv.data("doorsToOpen") );
		thermalChargeHeistDiv.data("doorsToOpen", doors);
	});
	thermalChargeHeistDiv.find(".delete-step-btn").click(function() {
		thermalChargeHeistDiv.remove();
	});
	if(thermalCharge) {
		thermalChargeHeistDiv.find(".coords-x").val(thermalCharge.coords.x);
		thermalChargeHeistDiv.find(".coords-y").val(thermalCharge.coords.y);
		thermalChargeHeistDiv.find(".coords-z").val(thermalCharge.coords.z);
		thermalChargeHeistDiv.find(".heading").val(thermalCharge.heading);
		thermalChargeHeistDiv.find(".is-optional-checkbox").prop("checked", thermalCharge.isOptional);
		thermalChargeHeistDiv.find(".duration").val(thermalCharge.duration);
		thermalChargeHeistDiv.data("blipData", thermalCharge.blipData);
		thermalChargeHeistDiv.data("doorsToOpen", thermalCharge.doorsToOpen);
		thermalChargeHeistDiv.data("alarms", thermalCharge.alarms);
		
		if(thermalCharge.requiredItem) {
			thermalChargeHeistDiv.find(".required-item-name").val(thermalCharge.requiredItem.name);
			thermalChargeHeistDiv.find(".required-item-quantity").val(thermalCharge.requiredItem.quantity);
			thermalChargeHeistDiv.find(".required-item-lose-on-use-checkbox").prop("checked", thermalCharge.requiredItem.loseOnUse);
		}
	}
	thermalChargeHeistDiv.find(".requires-an-item-checkbox").prop("checked", thermalCharge?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(thermalChargeHeistDiv);
}
/*
██       ██████   ██████ ██   ██ ██████  ██  ██████ ██   ██  █████  ██████  ██      ███████     ██████   ██████   ██████  ██████  ███████ 
██      ██    ██ ██      ██  ██  ██   ██ ██ ██      ██  ██  ██   ██ ██   ██ ██      ██          ██   ██ ██    ██ ██    ██ ██   ██ ██      
██      ██    ██ ██      █████   ██████  ██ ██      █████   ███████ ██████  ██      █████       ██   ██ ██    ██ ██    ██ ██████  ███████ 
██      ██    ██ ██      ██  ██  ██      ██ ██      ██  ██  ██   ██ ██   ██ ██      ██          ██   ██ ██    ██ ██    ██ ██   ██      ██ 
███████  ██████   ██████ ██   ██ ██      ██  ██████ ██   ██ ██   ██ ██████  ███████ ███████     ██████   ██████   ██████  ██   ██ ███████ 
*/
function getAllLockpickableDoorsFromStageDiv(stageDiv) {
	let lockpickableDoors = [];
	const stepMethod = "LOCKPICKABLE_DOOR"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let lockpickableDoorDiv = $(this);
		let lockpickableDoor = {
			type: "lockpickable_door", // for the object model
			method: stepMethod,
			coords: {
				x: parseFloat( lockpickableDoorDiv.find(".coords-x").val() ),
				y: parseFloat( lockpickableDoorDiv.find(".coords-y").val() ),
				z: parseFloat( lockpickableDoorDiv.find(".coords-z").val() ),
			},
			blipData : lockpickableDoorDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: lockpickableDoorDiv.find(".is-optional-checkbox").prop("checked"),
			doorsToOpen: lockpickableDoorDiv.data("doorsToOpen"),
			difficulty: parseInt( lockpickableDoorDiv.find(".difficulty").val() ),
			requiredItem: getRequiredItemFromDiv(lockpickableDoorDiv),
			alarms: lockpickableDoorDiv.data("alarms"),
		}
		lockpickableDoors.push(lockpickableDoor);
	});
	return lockpickableDoors
}
function addLockpickableDoorHeist(stageDiv, lockpickableDoor) {
	let lockpickableDoorHeistDiv = $(`
		
			${getLocalizedText("menu:lockpickable_door")}
			
			
				
				
			 
			
		 
	`);
	lockpickableDoorHeistDiv.find(".setup-alarm-btn").click(async function() {
		let currentAlarms = lockpickableDoorHeistDiv.data("alarms");
		let alarms = await alarmsDialog(currentAlarms);
		
		lockpickableDoorHeistDiv.data("alarms", alarms);
	}).tooltip();
	lockpickableDoorHeistDiv.find(".requires-an-item-checkbox").change(function() {
		toggleRequiredItem(lockpickableDoorHeistDiv.find(".required-item-div"));
	});
	lockpickableDoorHeistDiv.find(".choose-item-btn").click(async function() {
		let itemName = await itemsDialog();
		lockpickableDoorHeistDiv.find(".required-item-name").val(itemName);
	})
	lockpickableDoorHeistDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let data = await placeObjectAndReturnCoords();
		if(data) {
			lockpickableDoorHeistDiv.find(".coords-x").val(data.coords.x);
			lockpickableDoorHeistDiv.find(".coords-y").val(data.coords.y);
			lockpickableDoorHeistDiv.find(".coords-z").val(data.coords.z);
		}
	}).tooltip();
	lockpickableDoorHeistDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = lockpickableDoorHeistDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		lockpickableDoorHeistDiv.data("blipData", blipData);
	});
	lockpickableDoorHeistDiv.find(".choose-doors-btn").click(async function() {
		let doors = await doorsDialog( lockpickableDoorHeistDiv.data("doorsToOpen") );
		lockpickableDoorHeistDiv.data("doorsToOpen", doors);
	});
	lockpickableDoorHeistDiv.find(".delete-step-btn").click(function() {
		lockpickableDoorHeistDiv.remove();
	});
	if(lockpickableDoor) {
		lockpickableDoorHeistDiv.find(".coords-x").val(lockpickableDoor.coords.x);
		lockpickableDoorHeistDiv.find(".coords-y").val(lockpickableDoor.coords.y);
		lockpickableDoorHeistDiv.find(".coords-z").val(lockpickableDoor.coords.z);
		lockpickableDoorHeistDiv.find(".is-optional-checkbox").prop("checked", lockpickableDoor.isOptional);
		lockpickableDoorHeistDiv.find(".difficulty").val(lockpickableDoor.difficulty);
		lockpickableDoorHeistDiv.data("blipData", lockpickableDoor.blipData);
		lockpickableDoorHeistDiv.data("doorsToOpen", lockpickableDoor.doorsToOpen);
		lockpickableDoorHeistDiv.data("alarms", lockpickableDoor.alarms);
		
		if(lockpickableDoor.requiredItem) {
			lockpickableDoorHeistDiv.find(".required-item-name").val(lockpickableDoor.requiredItem.name);
			lockpickableDoorHeistDiv.find(".required-item-quantity").val(lockpickableDoor.requiredItem.quantity);
			lockpickableDoorHeistDiv.find(".required-item-lose-on-use-checkbox").prop("checked", lockpickableDoor.requiredItem.loseOnUse);
		}
	}
	lockpickableDoorHeistDiv.find(".requires-an-item-checkbox").prop("checked", lockpickableDoor?.requiredItem ? true : false).change();
	stageDiv.find(".steps-list").append(lockpickableDoorHeistDiv);
}
/*
██      ███████ ████████ ██   ██  █████  ██           ██████   █████  ███████ ███████ ███████ ███████ 
██      ██         ██    ██   ██ ██   ██ ██          ██       ██   ██ ██      ██      ██      ██      
██      █████      ██    ███████ ███████ ██          ██   ███ ███████ ███████ ███████ █████   ███████ 
██      ██         ██    ██   ██ ██   ██ ██          ██    ██ ██   ██      ██      ██ ██           ██ 
███████ ███████    ██    ██   ██ ██   ██ ███████      ██████  ██   ██ ███████ ███████ ███████ ███████ 
*/
function getAllLethalGassesFromStageDiv(stageDiv) {
	let lethalGases = [];
	const stepMethod = "LETHAL_GAS"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let lethalGasDiv = $(this);
		let lethalGas = {
			type: "lethal_gas", // for the object model
			method: stepMethod,
			gasPoints: lethalGasDiv.data("gasPoints"),
			duration: parseInt( lethalGasDiv.find(".duration").val() ),
			damage: parseInt( lethalGasDiv.find(".damage").val() ),
			secondsBeforeStart: parseInt( lethalGasDiv.find(".seconds-before-start").val() ),
			color: hexToRgb( lethalGasDiv.find(".gas-color").val() ),
		}
		lethalGases.push(lethalGas);
	});
	return lethalGases
}
function addLethalGasHeist(stageDiv, lethalGas) {
	let lethalGasHeistDiv = $(`
		
			${getLocalizedText("menu:lethal_gas")}
			
			
		 
	`);
	lethalGasHeistDiv.find(".seconds-before-start").tooltip()
	lethalGasHeistDiv.find(".gas-color").tooltip()
	lethalGasHeistDiv.find(".choose-gas-points").click(async function() {
		let gasPoints = await placeGasPoints();
		lethalGasHeistDiv.data("gasPoints", gasPoints);
	}).tooltip();
	lethalGasHeistDiv.find(".delete-step-btn").click(function() {
		lethalGasHeistDiv.remove();
	});
	if(lethalGas) {
		lethalGasHeistDiv.find(".duration").val(lethalGas.duration);
		lethalGasHeistDiv.data("gasPoints", lethalGas.gasPoints);
		lethalGasHeistDiv.find(".damage").val(lethalGas.damage);
		lethalGasHeistDiv.find(".seconds-before-start").val(lethalGas.secondsBeforeStart);
		lethalGasHeistDiv.find(".gas-color").val( rgbToHex(lethalGas.color.r, lethalGas.color.g, lethalGas.color.b) );
	}
	stageDiv.find(".steps-list").append(lethalGasHeistDiv);
}
/*
███████ ███████  █████  ██████   ██████ ██   ██ 
██      ██      ██   ██ ██   ██ ██      ██   ██ 
███████ █████   ███████ ██████  ██      ███████ 
     ██ ██      ██   ██ ██   ██ ██      ██   ██ 
███████ ███████ ██   ██ ██   ██  ██████ ██   ██ 
*/
function getPossiblePointsFromSearchPointsDiv(searchPointsDiv) {
	let possiblePoints = [];
	searchPointsDiv.find(".point").each(function() {
		let pointDiv = $(this);
		let possiblePoint = {
			coords: {
				x: parseFloat( pointDiv.find(".coords-x").val() ),
				y: parseFloat( pointDiv.find(".coords-y").val() ),
				z: parseFloat( pointDiv.find(".coords-z").val() ),
			},
		}
		possiblePoints.push(possiblePoint);
	});
	return possiblePoints;
}
function getAllSearchPointsFromStageDiv(stageDiv) {
	let searchPoints = [];
	const stepMethod = "SEARCH_POINTS"
	stageDiv.find(`.steps-list > [data-step-method='${stepMethod}']`).each(function() {
		let searchPointslDiv = $(this);
		let stepData = {
			type: "search_points", // for the object model
			method: stepMethod,
			possiblePoints: getPossiblePointsFromSearchPointsDiv(searchPointslDiv),
			blipData : searchPointslDiv.data("blipData") || getDefaultBlipCustomization(),
			isOptional: searchPointslDiv.find(".is-optional-checkbox").prop("checked"),
			minObjectsAmount: parseInt( searchPointslDiv.find(".min-objects-amount").val() ),
			maxObjectsAmount: parseInt( searchPointslDiv.find(".max-objects-amount").val() ),
			items: getAllItemsFromDiv(searchPointslDiv),
		}
		searchPoints.push(stepData);
	});
	return searchPoints
}
function addPossiblePointToSearchPointsDiv(searchPointsDiv, possiblePoint) {
	let possiblePointDiv = $(`
		
	`);
	
	possiblePointDiv.find(".delete-point-btn").click(function() {
		possiblePointDiv.remove();
	});
	possiblePointDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let coords = await chooseCoords();
		if(coords) {
			possiblePointDiv.find(".coords-x").val(coords.x);
			possiblePointDiv.find(".coords-y").val(coords.y);
			possiblePointDiv.find(".coords-z").val(coords.z);
		}
	}).tooltip();
	if(possiblePoint) {
		possiblePointDiv.find(".coords-x").val(possiblePoint.coords.x);
		possiblePointDiv.find(".coords-y").val(possiblePoint.coords.y);
		possiblePointDiv.find(".coords-z").val(possiblePoint.coords.z);
	}
	searchPointsDiv.find(".possible-points-list").append(possiblePointDiv);
}
async function addSearchPointsHeist(stageDiv, searchPoints) {
	let stepDiv = $(`
		
			${getLocalizedText("menu:search_points")}
			
			
			
				
				
				
					
				
			 
			${getLocalizedText("menu:possible_points")}
			
			
			
				
			
			
		 
	`);
	stepDiv.find(".delete-step-btn").click(function() { stepDiv.remove(); })
	stepDiv.find(".choose-coords-and-heading-btn").click(async function() {
		let objectType = stepDiv.find(".object-type").val();
		let data = await placeObjectAndReturnCoords(objectType);
		if(data) {
			stepDiv.find(".coords-x").val(data.coords.x);
			stepDiv.find(".coords-y").val(data.coords.y);
			stepDiv.find(".coords-z").val(data.coords.z);
		}
	}).tooltip();
	stepDiv.find(".customize-blip-btn").click(async function() {
		let oldBlipData = stepDiv.data("blipData");
		let blipData = await blipDialog(oldBlipData)
		stepDiv.data("blipData", blipData);
	})
	stepDiv.find(".add-item-btn").click(function() {
		addItemInStep(stepDiv);
	})
	stepDiv.find(".add-point-btn").click(function() {
		addPossiblePointToSearchPointsDiv(stepDiv);
	})
	// Sets value if the object is not new
	if(searchPoints) {
		stepDiv.data("blipData", searchPoints.blipData);
		stepDiv.find(".is-optional-checkbox").prop("checked", searchPoints.isOptional);
		stepDiv.find(".min-objects-amount").val(searchPoints.minObjectsAmount);
		stepDiv.find(".max-objects-amount").val(searchPoints.maxObjectsAmount);
		if(searchPoints.items) {
			searchPoints.items.forEach(item => {
				addItemInStep(stepDiv, item);
			});
		}
		if(searchPoints.possiblePoints) {
			searchPoints.possiblePoints.forEach(possiblePoint => {
				addPossiblePointToSearchPointsDiv(stepDiv, possiblePoint);
			});
		}
	} else {
		stepDiv.find(".min-objects-amount").val(1);
		stepDiv.find(".max-objects-amount").val(1);
		addItemInStep(stepDiv);
		addPossiblePointToSearchPointsDiv(stepDiv);
	}
	stageDiv.find(".steps-list").append(stepDiv);
}
/*
██   ██ ███████ ██ ███████ ████████ ███████     ███    ███ ██ ███████  ██████ 
██   ██ ██      ██ ██         ██    ██          ████  ████ ██ ██      ██      
███████ █████   ██ ███████    ██    ███████     ██ ████ ██ ██ ███████ ██      
██   ██ ██      ██      ██    ██         ██     ██  ██  ██ ██      ██ ██      
██   ██ ███████ ██ ███████    ██    ███████     ██      ██ ██ ███████  ██████ 
*/
function getAllItemsFromDiv(stepDiv) {
	let items = [];
	stepDiv.find(".item").each(function() {
		let itemDiv = $(this);
		let item = {
			type: itemDiv.find(".item-type").val(),
			name: itemDiv.find(".item-name").val(),
			minQuantity: parseInt( itemDiv.find(".item-min-quantity").val() ),
			maxQuantity: parseInt( itemDiv.find(".item-max-quantity").val() ),
			chances: parseInt( itemDiv.find(".item-chances").val() ),
		}
		items.push(item);
	});
	return items
}
function getRequiredItemFromDiv(stepDiv) {
	const requiredItemDiv = stepDiv.find(".required-item-div");
	let requiresItem = requiredItemDiv.find(".requires-an-item-checkbox").prop("checked");
	
	if(requiresItem) {
		return {
			name: requiredItemDiv.find(".required-item-name").val(),
			quantity: parseInt( requiredItemDiv.find(".required-item-quantity").val() ),
			loseOnUse: requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("checked"),
		}
	} else {
		return null;
	}
}
function getHeistStages() {
	let stages = [];
	$("#heist-stages").find(".stage").each(function(index) {
		let stageDiv = $(this);
		let stage = {
			steps: [
				...getAllRobbableObjectsFromStageDiv(stageDiv),
				...getAllSafesFromStageDiv(stageDiv),
				...getAllPaintingsFromStageDiv(stageDiv),
				...getAllGlassDisplaysFromStageDiv(stageDiv),
				...getAllLaserDrillsFromStageDiv(stageDiv),
				...getAllGuardsFromStageDiv(stageDiv),
				...getAllHackablePanelsFromStageDiv(stageDiv),
				...getAllThermalChargesFromStageDiv(stageDiv),
				...getAllLockpickableDoorsFromStageDiv(stageDiv),
				...getAllLethalGassesFromStageDiv(stageDiv),
				...getAllSearchPointsFromStageDiv(stageDiv),
			],
		}
		stages.push(stage);
	})
	return stages
}
$("#heist-form").submit(async function(event) {
	if(isThereAnyErrorInForm(event)) return;
	let heistModal = $("#heist-modal");
	let action = heistModal.data("action");
	let heistData = {
		label: $("#heist-label").val(),
		minimumPolice: parseInt( $("#heist-minimum-police").val() ),
		timeoutMinutes: parseInt( $("#heist-timeout").val() ),
		minutesBeforeReset: parseInt( $("#heist-reset").val() ),
		timeLimitMinutes: parseInt( $("#heist-time-limit-minutes").val() ),
		stages: getHeistStages()
	}
	switch(action) {
		case "create": {
			const response = await $.post(`https://${resName}/createHeist`, JSON.stringify(heistData));
			
			showServerResponse(response)
			if(response === true) {
				heistModal.modal("hide");
				loadHeists();
			}
			break;
		}
		case "edit": {
			const response = await $.post(`https://${resName}/updateHeist`, JSON.stringify({heistId: heistModal.data("heistId"), heistData: heistData}));
			showServerResponse(response)
			if(response === true) {
				heistModal.modal("hide");
				loadHeists();
			}
			break;
		}
	}
});
$("#delete-heist-btn").click(async function() {
	if(!await confirmDeletion()) return;
	let heistModal = $("#heist-modal");
	let heistId = heistModal.data("heistId");
	const response = await $.post(`https://${resName}/deleteHeist`, JSON.stringify({heistId: heistId}));
	showServerResponse(response);
	if(response === true) {
		heistModal.modal("hide");
		loadHeists();
	}
})
async function addItemInStep(stepDiv, item) {
	let itemDiv = $(`
		
	`);
	itemDiv.find(".delete-item-btn").click(function() {
		itemDiv.remove();
	})
	itemDiv.find(".choose-item-btn").click(async function() {
		let objectType = itemDiv.find(".item-type").val();
		let objectName = await objectDialog(objectType);
		itemDiv.find(".item-name").val(objectName);
	}).tooltip();
	if(item) {
		itemDiv.find(".item-type").val(item.type);
		itemDiv.find(".item-name").val(item.name);
		itemDiv.find(".item-min-quantity").val(item.minQuantity);
		itemDiv.find(".item-max-quantity").val(item.maxQuantity);
		itemDiv.find(".item-chances").val(item.chances);
	}
	stepDiv.find(".items-list").append(itemDiv);
}
function toggleRequiredItem(requiredItemDiv) {
	let enabled = requiredItemDiv.find(".requires-an-item-checkbox").prop("checked");
	requiredItemDiv.find(".required-item-name").prop("disabled", !enabled).prop("required", enabled);
	requiredItemDiv.find(".required-item-quantity").prop("disabled", !enabled).prop("required", enabled);
	requiredItemDiv.find(".choose-item-btn").prop("disabled", !enabled);
	requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("disabled", !enabled);
	if(!enabled) {
		requiredItemDiv.find(".required-item-lose-on-use-checkbox").prop("checked", false);
	}
}
function renameAllStagesByTheirOrder() {
	$("#heist-stages").find(".stage-title").each(function(index) {
		let stageNumber = index + 1;
		$(this).prop("innerHTML", `${ getLocalizedText("menu:stage") }  ${stageNumber}`)
	})
	$("#heist-stages").find(".delete-stage-btn").each(function(index) {
		let stageNumber = index + 1;
		$(this).prop("innerHTML", `${ getLocalizedText("menu:delete_stage") } - ${stageNumber}`)
	})
}
function setDoorsCreatorStatusOnStageDiv(stageDiv) {
	if(hasDoorsCreator) return;
	let stepsRequiringDoorsCreator = {
		"HACKABLE_PANEL": true,
		"THERMAL_CHARGE": true,
		"LOCKPICKABLE_DOOR": true,
	}
	stageDiv.find("[data-step-method]").each(function() {
		let stepMethod = $(this).data("stepMethod");
		if(stepsRequiringDoorsCreator[stepMethod]) {
			$(this)
				.addClass("disabled")
				.parent()
					.data("bs-toggle", "tooltip")
					.data("bs-placement", "top")
					.prop("title", getLocalizedText("menu:requires_doors_creator"))
					.tooltip();
		}
	});
}
// If it's the first stage, the steps guards and lethal gas won't be usable
function setAvailableStepsForEachStage() {
	$("#heist-stages").find(".stage").each(function() {
		let stageNumber = $(this).index() + 1;
		const isFirstStage = stageNumber == 1;
		const guardDiv = $(this).find(".steps-methods-list").find("[data-step-method='GUARDS']");
		const lethalGasDiv = $(this).find(".steps-methods-list").find("[data-step-method='LETHAL_GAS']");
		guardDiv.toggleClass("disabled", isFirstStage);
		lethalGasDiv.toggleClass("disabled", isFirstStage);
		// Enable tooltip only if it's first stage
		if(isFirstStage) {
			$(this).find(".not-first-stage-list-element").tooltip("enable");
		} else {
			$(this).find(".not-first-stage-list-element").tooltip("disable");
		}
	})
}
function addStepDiv(stepMethod, stageDiv, stepData) {
	switch(stepMethod) {
		case "ROBBABLE_OBJECT":
			addRobbableObjectHeist(stageDiv, stepData);
			break;
		case "SAFE":
			addSafeHeist(stageDiv, stepData);
			break;
		case "PAINTING":
			addPaintingHeist(stageDiv, stepData);
			break;
		case "GLASS_DISPLAY":
			addGlassDisplayHeist(stageDiv, stepData);
			break;
		case "LASER_DRILL":
			addLaserDrillHeist(stageDiv, stepData);
			break;
		case "GUARDS":
			addGuardsHeist(stageDiv, stepData);
			break;
		case "HACKABLE_PANEL":
			addHackablePanelHeist(stageDiv, stepData);
			break;
		case "THERMAL_CHARGE":
			addThermalChargeHeist(stageDiv, stepData);
			break;
		case "LOCKPICKABLE_DOOR":
			addLockpickableDoorHeist(stageDiv, stepData);
			break;
		case "LETHAL_GAS":
			addLethalGasHeist(stageDiv, stepData);
			break;
		case "SEARCH_POINTS": {
			addSearchPointsHeist(stageDiv, stepData);
			break;
		}
		default:
			console.log("Unknown step method: " + stepMethod);
			break;
	}
}
function addHeistStage(stage) {
	let stageDiv = $(`
		
			${ getLocalizedText("menu:stage") }  ${$("#heist-stages").children(".stage").length + 1}
			
			
			
				
					
					
				
				
			 
			
		 
	`)
	stageDiv.find(".delete-stage-btn").click(function() {
		stageDiv.remove();
		renameAllStagesByTheirOrder();
		setAvailableStepsForEachStage();
	})
	stageDiv.find(".steps-methods-list a").click(function() {
		let stepMethod = $(this).data("stepMethod");
		addStepDiv(stepMethod, stageDiv);
	})
	
	if(stage) {
		stage.steps.forEach(stepData => {
			addStepDiv(stepData.method, stageDiv, stepData);
		});
	}
	setDoorsCreatorStatusOnStageDiv(stageDiv);
	$("#heist-stages").append(stageDiv);
	setAvailableStepsForEachStage();
}
$("#heist-add-stage-btn").click(function() {
	addHeistStage()
})
$("#heist-import-stages-from-heist-btn").click(async function() {
	let heistId = await heistsDialog()
	if(heistId) {
		heists[heistId].stages.forEach(stage => {
			addHeistStage(stage);
		})
	}
})
/*
 ██████  █████  ██████   ██████   ██████      ██████   ██████  ██████  ██████  ███████ ██████  ██ ███████ ███████ 
██      ██   ██ ██   ██ ██       ██    ██     ██   ██ ██    ██ ██   ██ ██   ██ ██      ██   ██ ██ ██      ██      
██      ███████ ██████  ██   ███ ██    ██     ██████  ██    ██ ██████  ██████  █████   ██████  ██ █████   ███████ 
██      ██   ██ ██   ██ ██    ██ ██    ██     ██   ██ ██    ██ ██   ██ ██   ██ ██      ██   ██ ██ ██           ██ 
 ██████ ██   ██ ██   ██  ██████   ██████      ██   ██  ██████  ██████  ██████  ███████ ██   ██ ██ ███████ ███████ 
*/
let cargoRobberiesDatatable = $("#cargo-robberies-container").DataTable( {
	"lengthMenu": [10, 15, 20],
	"createdRow": function ( row, data, index ) {
		$(row).addClass("clickable");
		$(row).click(function() {
			let id = parseInt( data[0] );
			editCargoRobbery(id);
		})
	},
});
let cargoRobberies = {};
function loadCargoRobberies() {
	$.post(`https://${resName}/getAllCargoRobberies`, {}, async function(rawCargoRobberies) {
		// Manually create the table to avoid incompatibilities due table indexing
		cargoRobberies = {};
		for(const[k, heistData] of Object.entries(rawCargoRobberies)) {
			cargoRobberies[heistData.id] = heistData;
		}
		cargoRobberiesDatatable.clear();
		for(const[id, heistData] of Object.entries(cargoRobberies)) {
			cargoRobberiesDatatable.row.add([
				id,
				heistData.label,
			]);
		}
		cargoRobberiesDatatable.draw();
	})
}
function getDefaultBlipCargoRobbery() {
	return {
		isEnabled: true,
		sprite: 67,
		label: getLocalizedText("menu:armored_truck"),
		scale: 0.8,
		color: 1,
		display: 2,
	}
}
function setDefaultDataOfCargoRobberyModal() {
	$("#cargo-robbery-label").val("Default");
	$("#cargo-robbery-vehicle-model").val("stockade");
	$("#cargo-robbery-driver-model").val("mp_m_securoguard_01");
	$("#cargo-robbery-max-speed").val(50.0);
	$("#cargo-robbery-seconds-to-drill").val(20);
	$(`input[name=cargo-drive-type][value='stops']`).prop("checked", true).change();
	$("#cargo-robbery-random-drive-coords-x").val("");
	$("#cargo-robbery-random-drive-coords-y").val("");
	$("#cargo-robbery-random-drive-coords-z").val("");
	$("#cargo-robbery-random-drive-heading").val("");
	$("#cargo-robbery-random-drive-minutes-before-despawn").val(30);
	$("#cargo-robbery-min-objects-quantity").val(1);
	$("#cargo-robbery-max-objects-quantity").val(2);
	$("#cargo-items-list").empty();
	$("#cargo-robbery-stops").empty();
	$("#cargo-robbery-modal").data("blipData", getDefaultBlipCargoRobbery());
}
function renameAllStopsByTheirOrder() {
	$("#cargo-robbery-stops").find(".stop-title").each(function(index) {
		let stopNumber = index + 1;
		$(this).prop("innerHTML", `${ getLocalizedText("menu:stop") }  ${stopNumber}`)
	})
	$("#cargo-robbery-stops").find(".delete-stop-btn").each(function(index) {
		let stopNumber = index + 1;
		$(this).prop("innerHTML", `${ getLocalizedText("menu:delete_stop") } - ${stopNumber}`)
	})
}
$("#new-cargo-robbery-btn").click(function() {
	let cargoRobberyModal = $("#cargo-robbery-modal");
	// Converts from edit modal to create modal
	cargoRobberyModal.data("action", "create");
	
	$("#delete-cargo-robbery-btn").hide();
	$("#save-cargo-robbery-btn").text( getLocalizedText("menu:create") );
	
	setDefaultDataOfCargoRobberyModal();
	cargoRobberyModal.modal("show");
})
function editCargoRobbery(id) {
	let cargoRobberyData = cargoRobberies[id];
	let cargoRobberyModal = $("#cargo-robbery-modal");
	cargoRobberyModal.data("action", "edit");
	cargoRobberyModal.data("cargoRobberyId", id);
	cargoRobberyModal.data("blipData", cargoRobberyData.blipData);
	
	$("#delete-cargo-robbery-btn").show();
	$("#save-cargo-robbery-btn").text( getLocalizedText("menu:save") );
	$("#cargo-robbery-label").val(cargoRobberyData.label);
	$("#cargo-robbery-vehicle-model").val(cargoRobberyData.data.vehicleModel);
	$("#cargo-robbery-driver-model").val(cargoRobberyData.data.driverModel);
	$("#cargo-robbery-bulletproof-wheels-checkbox").prop("checked", cargoRobberyData.data.hasBulletproofTires);
	$("#cargo-robbery-max-speed").val(cargoRobberyData.data.maxSpeed);
	$("#cargo-robbery-seconds-to-drill").val(cargoRobberyData.data.secondsToDrill)
	$(`input[name=cargo-drive-type][value='${cargoRobberyData.data.driveType}']`).prop("checked", true).change();
	$("#cargo-robbery-random-drive-coords-x").val(cargoRobberyData.data.startCoords?.x);
	$("#cargo-robbery-random-drive-coords-y").val(cargoRobberyData.data.startCoords?.y);
	$("#cargo-robbery-random-drive-coords-z").val(cargoRobberyData.data.startCoords?.z);
	$("#cargo-robbery-random-drive-heading").val(cargoRobberyData.data.startCoords?.heading);
	$("#cargo-robbery-random-drive-minutes-before-despawn").val(cargoRobberyData.data.minutesBeforeDespawn);
	$("#cargo-robbery-min-objects-quantity").val(cargoRobberyData.data.minObjectsAmount);
	$("#cargo-robbery-max-objects-quantity").val(cargoRobberyData.data.maxObjectsAmount);
	$("#cargo-items-list").empty();
	if(cargoRobberyData.data.rewardObjects) {
		cargoRobberyData.data.rewardObjects.forEach(rewardObject => {
			addObjectInCargoRobbery(rewardObject);
		})	
	}
	$("#cargo-robbery-stops").empty();
	if(cargoRobberyData.data.stops) {
		cargoRobberyData.data.stops.forEach(stop => {
			addCargoRobberyStop(stop);
		})
	}
	cargoRobberyModal.modal("show");
}
function addCargoRobberyStop(stopData) {
	let stopIndex = $("#cargo-robbery-stops").children(".stop").length + 1;
	
	let stopDiv = $(`
		
			${ getLocalizedText("menu:stop") } ${stopIndex}
			
			${ getLocalizedText("menu:destination") }
			
			
			
				
			
			
		 
	`)
	
	stopDiv.find(".current-coords-btn").click(async function() {
		let data = await getCurrentCoordsAndHeading();
		stopDiv.find(".coords-x").val(data.coords.x);
		stopDiv.find(".coords-y").val(data.coords.y);
		stopDiv.find(".coords-z").val(data.coords.z);
		stopDiv.find(".heading").val(data.heading);
	}).tooltip();
	stopDiv.find(".delete-stop-btn").click(function() {
		stopDiv.remove();
		renameAllStopsByTheirOrder();
	});
	if(stopData) {
		stopDiv.find(".coords-x").val(stopData.coords.x);
		stopDiv.find(".coords-y").val(stopData.coords.y);
		stopDiv.find(".coords-z").val(stopData.coords.z);
		stopDiv.find(".heading").val(stopData.heading);
		stopDiv.find(".minutes-to-wait").val(stopData.minutesToWait);
	}
	$("#cargo-robbery-stops").append(stopDiv);
}
$("#cargo-robbery-add-stop-btn").click(function() {
	addCargoRobberyStop()
})
$('input[name=cargo-drive-type]').change(function() {
	let type = $(this).val();
	switch(type) {
		case "stops": {
			$("#cargo-robbery-stops-container").show().find(".form-control").prop("required", true);
			$("#cargo-robbery-random-drive-container").hide().find(".form-control").prop("required", false);
			break;
		}
		case "random": {
			$("#cargo-robbery-stops-container").hide().find(".form-control").prop("required", false);
			$("#cargo-robbery-random-drive-container").show().find(".form-control").prop("required", true);
		
			break;
		}
	}
});
$("#cargo-robbery-random-drive-current-coords-btn").click(async function() {
	let data = await getCurrentCoordsAndHeading();
	$("#cargo-robbery-random-drive-coords-x").val(data.coords.x);
	$("#cargo-robbery-random-drive-coords-y").val(data.coords.y);
	$("#cargo-robbery-random-drive-coords-z").val(data.coords.z);
	$("#cargo-robbery-random-drive-heading").val(data.heading);
})
function getCargoRobberyStops() {
	let stops = [];
	$("#cargo-robbery-stops").find(".stop").each(function() {
		let stop = {
			coords: {
				x: parseFloat( $(this).find(".coords-x").val() ),
				y: parseFloat( $(this).find(".coords-y").val() ),
				z: parseFloat( $(this).find(".coords-z").val() ),
			},
			heading: parseFloat( $(this).find(".heading").val() ),
			minutesToWait: parseInt( $(this).find(".minutes-to-wait").val() ),
		};
		stops.push(stop);
	});
	return stops;
}
function getCargoRobberyRewards() {
	let rewards = [];
	$("#cargo-items-list").find(".cargo-robbery-item").each(function() {
		let currentObject = $(this);
		let reward = {
			type: currentObject.find(".object-type").val(),
			name: currentObject.find(".object-name").val(),
			minQuantity: parseInt(currentObject.find(".min-quantity").val()),
			maxQuantity: parseInt(currentObject.find(".max-quantity").val()),
			chances: parseInt(currentObject.find(".chances").val())
		}
		rewards.push(reward);
	})
	
	return rewards;
}
$("#cargo-robbery-form").submit(async function(event) {
	if(isThereAnyErrorInForm(event)) return;
	let cargoRobberyModal = $("#cargo-robbery-modal");
	let action = cargoRobberyModal.data("action");
	let cargoRobberyData = {
		label: $("#cargo-robbery-label").val(),
		blipData: cargoRobberyModal.data("blipData"),
		data: {
			vehicleModel: $("#cargo-robbery-vehicle-model").val(),
			driverModel: $("#cargo-robbery-driver-model").val(),
			hasBulletproofTires: $("#cargo-robbery-bulletproof-wheels-checkbox").prop("checked"),	
			maxSpeed: parseFloat( $("#cargo-robbery-max-speed").val() ),
			secondsToDrill: parseInt( $("#cargo-robbery-seconds-to-drill").val() ),
			minObjectsAmount: parseInt( $("#cargo-robbery-min-objects-quantity").val() ),
			maxObjectsAmount: parseInt( $("#cargo-robbery-max-objects-quantity").val() ),
			rewardObjects: getCargoRobberyRewards(),
			stops: getCargoRobberyStops(),
			driveType: $('input[name=cargo-drive-type]:checked').val(),
			startCoords: {
				x: parseFloat( $("#cargo-robbery-random-drive-coords-x").val() ),
				y: parseFloat( $("#cargo-robbery-random-drive-coords-y").val() ),
				z: parseFloat( $("#cargo-robbery-random-drive-coords-z").val() ),
				heading: parseFloat(  $("#cargo-robbery-random-drive-heading").val() ),
			},
			minutesBeforeDespawn: parseInt( $("#cargo-robbery-random-drive-minutes-before-despawn").val() )
		}
	}
	
	switch(action) {
		case "create": {
			const response = await $.post(`https://${resName}/createCargoRobbery`, JSON.stringify(cargoRobberyData));
			
			showServerResponse(response);
			if(response === true) {
				cargoRobberyModal.modal("hide");
				loadCargoRobberies();
			}
			break;
		}
		case "edit": {
			const response = await $.post(`https://${resName}/updateCargoRobbery`, JSON.stringify({cargoRobberyId: cargoRobberyModal.data("cargoRobberyId"), cargoRobberyData: cargoRobberyData}));
			showServerResponse(response);
			if(response === true) {
				cargoRobberyModal.modal("hide");
				loadCargoRobberies();
			}
			break;
		}
	}
});
$("#delete-cargo-robbery-btn").click(async function() {
	if(!await confirmDeletion()) return;
	let cargoRobberyModal = $("#cargo-robbery-modal");
	let cargoRobberyId = cargoRobberyModal.data("cargoRobberyId");
	const response = await $.post(`https://${resName}/deleteCargoRobbery`, JSON.stringify({cargoRobberyId: cargoRobberyId}));
	showServerResponse(response);
	if(response === true) {
		cargoRobberyModal.modal("hide");
		loadCargoRobberies();
	}
})
$("#cargo-robbery-customize-blip-btn").click(async function() {
	oldBlipData = $("#cargo-robbery-modal").data("blipData");
	let blipData = await blipDialog(oldBlipData)
	$("#cargo-robbery-modal").data("blipData", blipData);
});
async function addObjectInCargoRobbery(objectData) {
	let objectDiv = $(`
		
	`);
	objectDiv.find(".delete-cargo-robbery-item-btn").click(function() {
		objectDiv.remove();
	})
	objectDiv.find(".choose-item-btn").click(async function() {
		let objectType = objectDiv.find(".object-type").val();
		let objectName = await objectDialog(objectType);
		objectDiv.find(".object-name").val(objectName);
	}).tooltip();
	if(objectData) {
		objectDiv.find(".object-type").val(objectData.type);
		objectDiv.find(".object-name").val(objectData.name);
		objectDiv.find(".min-quantity").val(objectData.minQuantity);
		objectDiv.find(".max-quantity").val(objectData.maxQuantity);
		objectDiv.find(".chances").val(objectData.chances);
	}
	$("#cargo-items-list").append(objectDiv);
}
$("#cargo-robbery-add-item-btn").click(function() {
	addObjectInCargoRobbery();
});
/*
██████  ██       █████  ███    ██ ███    ██ ██ ███    ██  ██████  
██   ██ ██      ██   ██ ████   ██ ████   ██ ██ ████   ██ ██       
██████  ██      ███████ ██ ██  ██ ██ ██  ██ ██ ██ ██  ██ ██   ███ 
██      ██      ██   ██ ██  ██ ██ ██  ██ ██ ██ ██  ██ ██ ██    ██ 
██      ███████ ██   ██ ██   ████ ██   ████ ██ ██   ████  ██████ 
*/
let planningsDatatable = $("#plannings-container").DataTable( {
	"lengthMenu": [10, 15, 20],
	"createdRow": function ( row, data, index ) {
		$(row).addClass("clickable");
		$(row).click(function() {
			let id = parseInt( data[0] );
			editPlanning(id);
		})
	},
});
let plannings = {};
function getRemainingTimeForPlannings() {
	return new Promise((resolve, reject) => {
		$.post(`https://${resName}/getRemainingTimeForPlannings`, function(remainingTimes) {
			resolve(remainingTimes)
		});
	})
}
async function loadPlannings() {
	let remainingTimes = await getRemainingTimeForPlannings();
	$.post(`https://${resName}/getAllPlannings`, {}, async function(rawPlannings) {
		// Manually create the table to avoid incompatibilities due table indexing
		plannings = {};
		for(const[k, planningData] of Object.entries(rawPlannings)) {
			plannings[planningData.id] = planningData;
		}
		planningsDatatable.clear();
		for(const[id, planningData] of Object.entries(plannings)) {
			planningsDatatable.row.add([
				id,
				planningData.label,
				cargoRobberies[planningData.cargoRobberyId]?.label || "None",
				remainingTimes[id] || "?",
			]);
		}
		planningsDatatable.draw();
	})
}
function planningSetTargetRobbery(robberyId = null) {
	let planningModal = $("#planning-modal");
	planningModal.data("cargoRobberyId", robberyId);
	if(robberyId) {
		let robberyLabel = cargoRobberies[robberyId].label;
		$("#planning-target-robbery").val( robberyLabel );
	} else {
		$("#planning-target-robbery").val( getLocalizedText("menu:none") );
	}
}
function setDefaultDataOfPlanningModal() {
	$("#planning-label").val("Default");
	$("#planning-min-police").val(0);
	planningSetTargetRobbery(null);
	$("#planning-date-type").val("now").change();
}
$("#new-planning-btn").click(function() {
	let planningModal = $("#planning-modal");
	// Converts from edit modal to create modal
	planningModal.data("action", "create");
	
	$("#delete-planning-btn").hide();
	$("#save-planning-btn").text( getLocalizedText("menu:create") );
	
	setDefaultDataOfPlanningModal();
	planningModal.modal("show");
})
$("#planning-choose-robbery-btn").click(async function() {
	let robberyId = await cargoRobberiesDialog();
	planningSetTargetRobbery(robberyId);
});
function togglePlanningDateType() {
	let val = $("#planning-date-type").val();
	if(val == "date") {
		$("#planning-date").show().prop("required", true);
	} else {
		$("#planning-date").hide().prop("required", false);
	}
	if(val == "interval") {
		$("#planning-interval-div").show();
		$("#planning-interval").prop("required", true);
	} else {
		$("#planning-interval-div").hide();
		$("#planning-interval").prop("required", false);
	}
}
$("#planning-date-type").change(togglePlanningDateType);
function getUnixFromDate(date) {
	return Math.floor(new Date(date).getTime() / 1000 )
}
function getDateFromUnix(unix) {
	const padL = (nr, len = 2, chr = `0`) => `${nr}`.padStart(2, chr);
	let date = new Date(unix * 1000)
	let formatted = `${
		date.getFullYear()}-${
		padL(date.getMonth()+1)}-${
		padL(date.getDate())}T${
		padL(date.getHours())}:${
		padL(date.getMinutes())}`
	return formatted;
}
$("#planning-form").submit(async function(event) {
	if(isThereAnyErrorInForm(event)) return;
	let planningModal = $("#planning-modal");
	let action = planningModal.data("action");
	let planningData = {
		label: $("#planning-label").val(),
		minimumPolice: parseInt($("#planning-min-police").val()),
		cargoRobberyId: parseInt(planningModal.data("cargoRobberyId")),
		data: {
			dateType: $("#planning-date-type").val(),
			date: getUnixFromDate( $("#planning-date").val() ),
			interval: parseInt( $("#planning-interval").val() ),
		}
	}
	
	switch(action) {
		case "create": {
			const response = await $.post(`https://${resName}/createPlanning`, JSON.stringify(planningData));
			showServerResponse(response);
			if(response === true) {
				planningModal.modal("hide");
				loadPlannings();
			}
			break;
		}
		case "edit": {
			const response = await $.post(`https://${resName}/updatePlanning`, JSON.stringify({planningId: planningModal.data("planningId"), planningData: planningData}));
			showServerResponse(response);
			if(response === true) {
				planningModal.modal("hide");
				loadPlannings();
			}
			break;
		}
	}
})
function editPlanning(id) {
	let planningData = plannings[id];
	let planningsModal = $("#planning-modal");
	planningsModal.data("action", "edit");
	planningsModal.data("planningId", id);
	
	$("#delete-planning-btn").show();
	$("#save-planning-btn").text( getLocalizedText("menu:save") );
	$("#planning-label").val(planningData.label);
	$("#planning-min-police").val(planningData.minimumPolice);
	planningSetTargetRobbery(planningData.cargoRobberyId);
	$("#planning-date-type").val(planningData.data.dateType).change();
	$("#planning-date").val( getDateFromUnix(planningData.data.date) );
	$("#planning-interval").val(planningData.data.interval);
	planningsModal.modal("show");
}
$("#delete-planning-btn").click(async function() {
	if(!await confirmDeletion()) return;
	let planningModal = $("#planning-modal");
	let planningId = planningModal.data("planningId");
	const response = await $.post(`https://${resName}/deletePlanning`, JSON.stringify({planningId: planningId}));
	showServerResponse(response);
	if(response === true) {
		planningModal.modal("hide");
		loadPlannings();
	}
})
/*
███    ██ ██████   ██████     ███    ███ ██    ██  ██████   ██████  ██ ███    ██  ██████  
████   ██ ██   ██ ██          ████  ████ ██    ██ ██       ██       ██ ████   ██ ██       
██ ██  ██ ██████  ██          ██ ████ ██ ██    ██ ██   ███ ██   ███ ██ ██ ██  ██ ██   ███ 
██  ██ ██ ██      ██          ██  ██  ██ ██    ██ ██    ██ ██    ██ ██ ██  ██ ██ ██    ██ 
██   ████ ██       ██████     ██      ██  ██████   ██████   ██████  ██ ██   ████  ██████  
*/
function toggleNpcMugging() {
	let enableNpcMuggingDiv = $("#enable-npc-mugging")
	let enable = enableNpcMuggingDiv.prop("checked");
	$("#npc-mugging-options").find("input, button").not(enableNpcMuggingDiv).prop("disabled", !enable);
}
$("#enable-npc-mugging").change(toggleNpcMugging);
async function addRobbableObjectNpcMugging(object) {
	let robbableObjectDiv = $(`
		
	`)
	robbableObjectDiv.find(".choose-btn").click(async function() {
		let objectType = robbableObjectDiv.find(".object-type").val();
		let objectName = await objectDialog(objectType);
		robbableObjectDiv.find(".object-name").val(objectName);
	}).tooltip();
	robbableObjectDiv.find(".remove-btn").click(function() {
		robbableObjectDiv.remove();
	})
	// Sets the object property if exist
	if(object) {
		robbableObjectDiv.find(".object-type").val(object.type);
		robbableObjectDiv.find(".object-name").val(object.name);
		robbableObjectDiv.find(".min-quantity").val(object.minQuantity);
		robbableObjectDiv.find(".max-quantity").val(object.maxQuantity);
		robbableObjectDiv.find(".chances").val(object.chances);
	}
	$("#npc-mugging-robbable-items").append(robbableObjectDiv);
}
$("#npc-mugging-add-object").click(function() {
	addRobbableObjectNpcMugging();
});
function getNpcMuggingRobbableObjects() {
	let robbableObjects = [];
	$("#npc-mugging-robbable-items").find(".robbable-object").each(function() {
		let currentObject = $(this);
		let object = {
			type: currentObject.find(".object-type").val(),
			name: currentObject.find(".object-name").val(),
			minQuantity: parseInt(currentObject.find(".min-quantity").val()),
			maxQuantity: parseInt(currentObject.find(".max-quantity").val()),
			chances: parseInt(currentObject.find(".chances").val())
		}
		robbableObjects.push(object);
	})
	return robbableObjects;
}
function setNpcMuggingRobbableObjects(objects) {
	$("#npc-mugging-robbable-items").empty();
	objects.forEach(object => {
		addRobbableObjectNpcMugging(object);
	});
}
$("#npc-mugging").submit(async function(event) {
	if(isThereAnyErrorInForm(event)) return;
	let clientSettings = {
		clNpcMugging: {
			canMakeFollow: $("#can-make-npc-to-follow").prop("checked"),
			cooldownOnMugging: parseInt( $("#npc-mugging-cooldown-on-mugging").val() )
		}
	}
	let sharedSettings = {
		isNpcMuggingEnabled: $("#enable-npc-mugging").prop("checked"),
		secondsToMug: parseInt( $("#seconds-to-mug").val() )
	}
	let serverSettings = {
		svNpcMugging: {
			robbableObjects: getNpcMuggingRobbableObjects(),
			minimumPolice: parseInt( $("#npc-mugging-minimum-police").val() ),
			probabilityPoliceAlert: parseInt( $("#npc-mugging-probability-police-alert").val() ),
			minimumObjectsAmount: parseInt( $("#npc-mugging-minimum-objects-amount").val() ),
			maximumObjectsAmount: parseInt( $("#npc-mugging-maximum-objects-amount").val() ),
			minutesAfterAutomaticallyRunAway: parseInt( $("#minutes-before-automatically-run-away").val() ),
			maxNPCsFollowing: parseInt( $("#npc-mugging-max-npcs-following").val() )
		}
	}
	const response = await $.post(`https://${resName}/saveSettings`, JSON.stringify({
		clientSettings: clientSettings,
		sharedSettings: sharedSettings,
		serverSettings: serverSettings,
	}));
	showServerResponse(response);
});