/**
 * Checks whether building placement is valid
 *	1. Visibility is not hidden (may be fogged or visible)
 *	2. Check foundation
 *		a. Doesn't obstruct foundation-blocking entities
 *		b. On valid terrain, based on passability class
 *	3. Territory type is allowed (see note below)
 *	4. Dock is on shoreline and facing into water
 *	5. Distance constraints satisfied
 *
 * Returns result object:
 * 	{
 *		"success":             true iff the placement is valid, else false
 *		"message":             message to display in UI for invalid placement, else ""
 *		"parameters":          parameters to use in the GUI message
 *		"translateMessage":    always true
 *		"translateParameters": list of parameters to translate
 *		"pluralMessage":       we might return a plural translation instead (optional)
 *		"pluralCount":         plural translation argument (optional)
 *  }
 *
 * Note: The entity which is used to check this should be a preview entity
 *  (template name should be "preview|"+templateName), as otherwise territory
 *  checks for buildings with territory influence will not work as expected.
 */
BuildRestrictions.prototype.CheckPlacement = function()
{
	var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
	var name = cmpIdentity ? cmpIdentity.GetGenericName() : "Building";

	var result = {
		"success": false,
		"message": markForTranslation("%(name)s cannot be built due to unknown error"),
		"parameters": {
			"name": name,
		},
		"translateMessage": true,
		"translateParameters": ["name"],
	};

	var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
	if (!cmpPlayer)
		return result; // Fail

	// TODO: AI has no visibility info
	if (!cmpPlayer.IsAI())
	{
		// Check whether it's in a visible or fogged region
		var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
		var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
		if (!cmpRangeManager || !cmpOwnership)
			return result; // Fail

		var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner()) != "hidden");
		if (!explored)
		{
			result.message = markForTranslation("%(name)s cannot be built in unexplored area");
			return result; // Fail
		}
	}

	// Check obstructions and terrain passability
	var passClassName = "";
	switch (this.template.PlacementType)
	{
	case "shore":
		passClassName = "building-shore";
		break;

	case "land-shore":
		// 'default-terrain-only' is everywhere a normal unit can go, ignoring
		// obstructions (i.e. on passable land, and not too deep in the water)
		passClassName = "default-terrain-only";
		break;

	case "land":
	default:
		passClassName = "building-land";
	}

	var cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
	if (!cmpObstruction)
		return result; // Fail


	if (this.template.Category == "Wall")
	{
		// allow walls to be built on top of trees
		let collisions = cmpObstruction.GetEntitiesBlockingConstruction();
		let areWoodEnts = collisions.length > 0;
		for (let ent of collisions)
		{
			// don't let entities with unitmotion prevent us from laying a foundation
			let cmpUnitMotion = Engine.QueryInterface(ent, IID_UnitMotion);
			let cmpResource = Engine.QueryInterface(ent, IID_ResourceSupply);
			if ((!cmpResource || cmpResource.GetType().generic !== "wood") && !cmpUnitMotion)
			{
				areWoodEnts = false;
				break;
			}
		}
		// for walls, only test the center point
		var ret = areWoodEnts ? "success" : cmpObstruction.CheckFoundation(passClassName, true);
	}
	else
	{
		var ret = cmpObstruction.CheckFoundation(passClassName, false);
	}

	if (ret != "success")
	{
		switch (ret)
		{
		case "fail_error":
		case "fail_no_obstruction":
			error("CheckPlacement: Error returned from CheckFoundation");
			break;
		case "fail_obstructs_foundation":
			result.message = markForTranslation("%(name)s cannot be built on another building or resource");
			break;
		case "fail_terrain_class":
			// TODO: be more specific and/or list valid terrain?
			result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
		}
		return result; // Fail
	}

	// Check territory restrictions
	var cmpTerritoryManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TerritoryManager);
	var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
	if (!cmpTerritoryManager || !cmpPosition || !cmpPosition.IsInWorld())
		return result;	// Fail

	var pos = cmpPosition.GetPosition2D();
	var tileOwner = cmpTerritoryManager.GetOwner(pos.x, pos.y);
	var isConnected = !cmpTerritoryManager.IsTerritoryBlinking(pos.x, pos.y);
	var isOwn = tileOwner == cmpPlayer.GetPlayerID();
	var isMutualAlly = cmpPlayer.IsExclusiveMutualAlly(tileOwner);
	var isNeutral = tileOwner == 0;

	var invalidTerritory = "";
	if (isOwn)
	{
		if (!this.HasTerritory("own"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "own");
		else if (!isConnected && !this.HasTerritory("neutral"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "unconnected own");
	}
	else if (isMutualAlly)
	{
		if (!this.HasTerritory("ally"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "allied");
		else if (!isConnected && !this.HasTerritory("neutral"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "unconnected allied");
	}
	else if (isNeutral)
	{
		if (!this.HasTerritory("neutral"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "neutral");
	}
	else
	{
		// consider everything else enemy territory
		if (!this.HasTerritory("enemy"))
			// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
			invalidTerritory = markForTranslationWithContext("Territory type", "enemy");
	}

	if (invalidTerritory)
	{
		result.message = markForTranslation("%(name)s cannot be built in %(territoryType)s territory. Valid territories: %(validTerritories)s");
		result.translateParameters.push("territoryType");
		result.translateParameters.push("validTerritories");
		result.parameters.territoryType = { "context": "Territory type", "_string": invalidTerritory };
		// gui code will join this array to a string
		result.parameters.validTerritories = { "context": "Territory type list", "list": this.GetTerritories() };
		return result;	// Fail
	}

	// Check special requirements
	if (this.template.PlacementType == "shore")
	{
		if (!cmpObstruction.CheckShorePlacement())
		{
			result.message = markForTranslation("%(name)s must be built on a valid shoreline");
			return result;	// Fail
		}
	}

	let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);

	let templateName = cmpTemplateManager.GetCurrentTemplateName(this.entity);
	let template = cmpTemplateManager.GetTemplate(removeFiltersFromTemplateName(templateName));

	// Check distance restriction
	if (this.template.Distance)
	{
		var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
		var cat = this.template.Distance.FromClass;

		var filter = function(id)
		{
			var cmpIdentity = Engine.QueryInterface(id, IID_Identity);
			return cmpIdentity.GetClassesList().indexOf(cat) > -1;
		};

		if (this.template.Distance.MinDistance !== undefined)
		{
			let minDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MinDistance", +this.template.Distance.MinDistance, cmpPlayer.GetPlayerID(), template);
			if (cmpRangeManager.ExecuteQuery(this.entity, 0, minDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
			{
				let result = markForPluralTranslation(
					"%(name)s too close to a %(category)s, must be at least %(distance)s meter away",
					"%(name)s too close to a %(category)s, must be at least %(distance)s meters away",
					minDistance);

				result.success = false;
				result.translateMessage = true;
				result.parameters = {
					"name": name,
					"category": cat,
					"distance": minDistance
				};
				result.translateParameters = ["name", "category"];
				return result;  // Fail
			}
		}
		if (this.template.Distance.MaxDistance !== undefined)
		{
			let maxDistance = ApplyValueModificationsToTemplate("BuildRestrictions/Distance/MaxDistance", +this.template.Distance.MaxDistance, cmpPlayer.GetPlayerID(), template);
			if (!cmpRangeManager.ExecuteQuery(this.entity, 0, maxDistance, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions, false).some(filter))
			{
				let result = markForPluralTranslation(
					"%(name)s too far from a %(category)s, must be within %(distance)s meter",
					"%(name)s too far from a %(category)s, must be within %(distance)s meters",
					maxDistance);

				result.success = false;
				result.translateMessage = true;
				result.parameters = {
					"name": name,
					"category": cat,
					"distance": maxDistance
				};
				result.translateParameters = ["name", "category"];
				return result;	// Fail
			}
		}
	}

	// Success
	result.success = true;
	result.message = "";
	return result;
};

BuildRestrictions.prototype.IsWoodEntity = function(entity)
{
    let cmpResource = Engine.QueryInterface(entity, IID_ResourceSupply);
    return cmpResource && cmpResource.GetType().generic === "wood";
}

Engine.ReRegisterComponentType(IID_BuildRestrictions, "BuildRestrictions", BuildRestrictions);
