// Alias of AIMap.GetTileIndex(), this looks cleaner
function dXY(x, y) {
	return AIMap.GetTileIndex(x,y);
}

function add(tile, delta) {
	return tile + AIMap.GetTileIndex(delta[0], delta[1]);
}

/**
 * Add a rectangular area to an AITileList containing tiles that are within /radius/
 * tiles from the center tile, taking the edges of the map into account.
 */  
function SafeAddRectangle(list, tile, radius) {
	local x1 = max(1, AIMap.GetTileX(tile) - radius);
	local y1 = max(1, AIMap.GetTileY(tile) - radius);
	
	local x2 = min(AIMap.GetMapSizeX() - 2, AIMap.GetTileX(tile) + radius);
	local y2 = min(AIMap.GetMapSizeY() - 2, AIMap.GetTileY(tile) + radius);
	
	list.AddRectangle(AIMap.GetTileIndex(x1, y1),AIMap.GetTileIndex(x2, y2)); 
}

//Find the cargo ID for a given class of Cargo
function GetCargoID(cargoClass) {
	local list = AICargoList();
	local candidate = -1;
	for (local i = list.Begin(); list.HasNext(); i = list.Next()) {
		if (AICargo.HasCargoClass(i, cargoClass))
		candidate = i;
	}
	
	if(candidate != -1)
		return candidate;
	
	throw "missing required cargo class";
}
function Range(from, to) {
	local range = [];
	for (local i=from; i<to; i++) {
		range.append(i);
	}
	
	return range;
}

function BuildTunnel(vehicleType, from, to, direction, length, testmode = false) {
	LogTile("Building tunnel from", from, "tunnel")
	LogTile("Building tunnel to", to, "tunnel")
	local delta = (to-from)/length
	if (AITile.IsBuildable(from+delta) && AITile.IsBuildable(to-delta)) {
		if (testmode) {
			return true										
		} else {
			// Let's try to build a tunnel
			if (direction == Direction.SW) {AITile.LowerTile(from, AITile.SLOPE_N); AITile.LowerTile(from, AITile.SLOPE_E); AITile.LowerTile(to, AITile.SLOPE_S); AITile.LowerTile(to, AITile.SLOPE_W);}
			if (direction == Direction.SE) {AITile.LowerTile(from, AITile.SLOPE_N); AITile.LowerTile(from, AITile.SLOPE_W); AITile.LowerTile(to, AITile.SLOPE_S); AITile.LowerTile(to, AITile.SLOPE_E);}
			if (direction == Direction.NE) {AITile.LowerTile(from, AITile.SLOPE_S); AITile.LowerTile(from, AITile.SLOPE_W); AITile.LowerTile(to, AITile.SLOPE_N); AITile.LowerTile(to, AITile.SLOPE_E);}
			if (direction == Direction.NW) {AITile.LowerTile(from, AITile.SLOPE_S); AITile.LowerTile(from, AITile.SLOPE_E); AITile.LowerTile(to, AITile.SLOPE_N); AITile.LowerTile(to, AITile.SLOPE_W);}
			if (AITunnel.GetOtherTunnelEnd(from+delta) == to-delta) {
				if (AITunnel.BuildTunnel(vehicleType, from+delta) || IsAlreadyBuilt()) {
					if (vehicleType == AIVehicle.VT_ROAD) {
						if ((AIRoad.BuildRoad(from, from+delta) || IsAlreadyBuilt()) && (AIRoad.BuildRoad(to-delta, to) || IsAlreadyBuilt()))
							return true
					} else if (vehicleType == AIVehicle.VT_RAIL) {
						if ((AIRail.BuildRail(from-delta, from, from+delta) || IsAlreadyBuilt()) && (AIRail.BuildRail(to-delta, to, to+delta) || IsAlreadyBuilt()))
							return true
					}
				}
			}
		}
	}
	return false
}


function IsAlreadyBuilt() {
	local e = AIError.GetLastError()
	return (e == AIError.ERR_ALREADY_BUILT) // || e == AIError.ERR_UNKNOWN)
}
function VehicleInTheWay() {
	local e = AIError.GetLastError()
	return (e == AIError.ERR_VEHICLE_IN_THE_WAY) // || e == AIError.ERR_UNKNOWN)
}



//Function to get an indication whether two tiles are connected by land
function IsConnectedByLand(from, to) {
	local x1 = AIMap.GetTileX(from)
	local y1 = AIMap.GetTileY(from)
	local x2 = AIMap.GetTileX(to)
	local y2 = AIMap.GetTileY(to)
	local xmax = max(x1,x2)
	local ymax = max(y1,y2)
	local xmin = min(x1,x2)
	local ymin = min(y1,y2)
	if (IsLandStrokeX(xmin, xmax, y1) && IsLandStrokeY(x2, ymin, ymax))
		return true
	return (IsLandStrokeX(xmin, xmax, y2) && IsLandStrokeY(x1, ymin, ymax))
}

function IsLandStrokeX(xmin, xmax, y) {
	local water = 0
	for (local x = xmin; x < xmax; x++) {
		if (AITile.IsWaterTile(AIMap.GetTileIndex(x, y)))
			water++
		else
			water /= 2
		if (water > 10)
			return false
	}
	return true
}
function IsLandStrokeY(x, ymin, ymax) {
	local water = 0
	for (local y = ymin; y < ymax; y++) {
		if (AITile.IsWaterTile(AIMap.GetTileIndex(x, y)))
			water++
		else
			water /= 2
		if (water > 10)
			return false
	}
	return true
}

function HasAnyRoad(tile) {
	return AIRoad.HasRoadType(tile, AIRoad.ROADTYPE_ROAD) || AIRoad.HasRoadType(tile, AIRoad.ROADTYPE_TRAM)
}

function CanBuildRoadOn(tile) {
	return AITile.IsBuildable(tile) || AIRoad.HasRoadType(tile, AIRoad.ROADTYPE_ROAD) || AIRoad.HasRoadType(tile, AIRoad.ROADTYPE_TRAM)
}

function ConnectRailTile(tile) {
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	local railType = AIRail.GetRailType(tile)
	LogTile("Connecting rail tile", tile)
	if (railType == AIRail.RAILTYPE_INVALID) railType = AIRail.GetCurrentRailType()
	if (railType != AIRail.GetCurrentRailType()) throw("Wrong rail type!")
	//AIRail.SetCurrentRailType(railType)
	foreach (delta in [dXY(0,1), dXY(0,-1), dXY(1,0)]) {
		foreach (delta2 in [dXY(0,-1), dXY(1,0), dXY(-1,0)]) {
			if (delta != delta2) {
				local connect = false
				local t1 = tile + delta
				local t2 = tile + delta2
				//if (IsARailTile(t1) && AIRail.GetRailType(t1) == railType && IsARailTile(t2) && AIRail.GetRailType(t2) == railType &&
				//	((AIBridge.IsBridgeTile(t1 + delta) || AITunnel.IsTunnelTile(t1 + delta)) || (IsARailTile(t1 + delta) && AIRail.GetRailType(t1 + delta) == railType) || (IsARailTile(t1 - delta2) && AIRail.GetRailType(t1 - delta2) == railType)) &&
				//	((AIBridge.IsBridgeTile(t2 + delta2) || AITunnel.IsTunnelTile(t2 + delta2)) || (IsARailTile(t2 + delta2) && AIRail.GetRailType(t2 + delta2) == railType) || (IsARailTile(t2 - delta) && AIRail.GetRailType(t2 - delta) == railType))) {
				if (AIRail.GetRailType(t1) == railType && AITile.GetOwner(t1) == myCompany && AIRail.GetRailType(t2) == railType && AITile.GetOwner(t2) == myCompany) {
					AIRail.BuildRail(t1, tile, t2)
					LogTile("Building rail from", t1)
					LogTile("Building rail to", t2)
				} else {
					AIRail.RemoveRail(t1, tile, t2)
					LogTile("Removing rail from", t1)
					LogTile("Removing rail to", t2)
				}
			}
		}
	}
}

function ConnectRailTileTowards(tile, towardsTile) {
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	local railType = AIRail.GetRailType(tile)
	LogTile("Connecting rail tile", tile)
	if (railType == AIRail.RAILTYPE_INVALID) railType = AIRail.GetCurrentRailType()
	if (railType != AIRail.GetCurrentRailType()) throw("Wrong rail type!")
	local delta = towardsTile - tile
	foreach (delta2 in [dXY(0,1), dXY(0,-1), dXY(1,0), dXY(-1,0)]) {
		if (delta != delta2) {
			local connect = false
			local t1 = towardsTile
			local t2 = tile + delta2
			if (AIRail.GetRailType(t1) == railType && AIRail.GetRailType(t2) == railType && AITile.GetOwner(t2) == myCompany) {
				AIRail.BuildRail(t1, tile, t2)
			} else {
				AIRail.RemoveRail(t1, tile, t2)
			}
		}
	}
}


function RemoveAllSignals(tile) {
	LogTile("Removing all signals", tile)
	foreach (delta in [dXY(0,1), dXY(0,-1), dXY(1,0), dXY(-1,0)]) {
		if (AIRail.GetSignalType(tile, tile+delta) != AIRail.SIGNALTYPE_NONE) {
			if (!AIRail.RemoveSignal(tile, tile+delta)) {
				LogError("Something went wrong when I tried to remove a railroad signal!")
				return false //we probably don't have enough money anymore
			}
		}
	}
	return true
}

function MigrateToPathSignalsAround(tile, radius = 4) {
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	for (local x = -radius; x <= radius; x++) {
		for (local y = -radius; y <= radius; y++) {
			local checkTile = tile + dXY(x, y)
			if (AITile.GetOwner(checkTile) == myCompany && AIRail.IsRailTile(checkTile)) {
				foreach (delta in [dXY(0,1), dXY(0,-1), dXY(1,0), dXY(-1,0)]) {
					local signalType = AIRail.GetSignalType(checkTile, checkTile+delta)
					if (signalType == AIRail.SIGNALTYPE_NORMAL) {
						AIRail.RemoveSignal(checkTile, checkTile+delta)
						AIRail.BuildSignal(checkTile, checkTile+delta, AIRail.SIGNALTYPE_PBS)
					}
				}
			}
		}
	}
}

//check if a tile is part of a station owned by you or another company
function IsStationTile(tile) {
	return IsAnyValidStation(AIStation.GetStationID(tile))
	//return (AIRoad.IsRoadStationTile(tile) || AIRail.IsRailStationTile(tile) || AIMarine.IsDockTile(tile) || AIAirport.IsAirportTile(tile))
}

//check if a station ID seems valid for a station owned by your or another company
function IsAnyValidStation(stationID) {
	return (stationID < 65535)
}

//check if a tile is a rail bridge. Unluckily, this is not as straightforward as IsRailTile && IsBridgeTile,
//because IsRailTile returns false for bridges
function IsRailBridge(tile) {
	return (AIBridge.IsBridgeTile(tile) && IsARailTile(tile))
}

//returns if a tile is any rail tile (a normal rail tile, a rail bridge tile or a rail tunnel tile)
function IsARailTile(tile) {
	return (AIRail.GetRailType(tile) != AIRail.RAILTYPE_INVALID)
}

function AreRailTilesConnected(from, tile, to) {
	if (AITunnel.IsTunnelTile(tile))
		return (AITunnel.GetOtherTunnelEnd(tile) == from || AITunnel.GetOtherTunnelEnd(tile) == to)
	if (AIBridge.IsBridgeTile(tile))
		return (AIBridge.GetOtherBridgeEnd(tile) == from || AIBridge.GetOtherBridgeEnd(tile) == to)
	return AIRail.AreTilesConnected(from,tile,to)
}

//Get the next rail tile. If a piece of rail is a dead end, it will return the tile that could be the next one
function GetNextRailTile(current, prev) {
	local next = GetNextRailTiles(current, prev)
	if (next == false) return false
	if (next.len() > 1) return false
	return next[0]
}

//get next rail tiles
function GetNextRailTiles(current, prev) {
	if (AIBridge.IsBridgeTile(current)) {
		local otherTile = AIBridge.GetOtherBridgeEnd(current)
		local length = AIMap.DistanceManhattan(current, otherTile)
		local delta = (current-otherTile)/length
		if (otherTile == prev) {
			return [current + delta]
		} else {
			if (current + delta == prev)
				return [otherTile]
			else
				return false
		}
	}
	
	if (AITunnel.IsTunnelTile(current)) {
		local otherTile = AITunnel.GetOtherTunnelEnd(current)
		local length = AIMap.DistanceManhattan(current, otherTile)
		local delta = (current-otherTile)/length
		if (otherTile == prev) {
			return [current + delta]
		} else {
			if (current + delta == prev)
				return [otherTile]
			else
				return false
		}	
	}
	local length = AIMap.DistanceManhattan(current, prev)
	local pre = (prev-current)/length
	local nextTiles = []
	local next = false
	foreach (delta in [dXY(0,1), dXY(0,-1), dXY(1,0), dXY(-1,0)]) {
		if (delta != pre && AIRail.AreTilesConnected(current + pre, current, current + delta)) {
			next = current + delta
			nextTiles.append(next)
		}
	}
	if (next == false) return false
	return nextTiles
}