require("plan.nut")


function IsProperlyServed(railservice) {
	Log(railservice[2].len() + " trains in this service")
	local stationA = AIStation.GetStationID(railservice[1][0][0])
	local cargoWaiting = AIStation.GetCargoWaiting(stationA, railservice[0][3]);
	local cargoRating = AIStation.GetCargoRating(stationA, railservice[0][3]);

	Log(cargoWaiting + " passengers are waiting. Rating is "+ cargoRating)
	return (cargoWaiting < 100 && cargoRating > 66)
}

//Builds a path signal 2 tiles before the tile (potentially a level crossing)
/*function buildPathSignalBefore(tile, forwardDirection) {
	local allowSignalPlacement = false
	local coordinates = VC.VectorCoordinates(tile, forwardDirection)
	local pre = VC.GetTile(coordinates, [0, -1])
	if (AIRail.IsRailTile(pre) && !AIRail.IsLevelCrossingTile(pre) && AIRail.GetSignalType(pre, tile) == AIRail.SIGNALTYPE_NONE) {
		foreach (pos in [[0,-2], [1,-1], [-1,-1]]) {
			local prepre = VC.GetTile(coordinates, pos)
			if (AIRail.AreTilesConnected(tile, pre, prepre) && AIRail.GetSignalType(prepre, pre) == AIRail.SIGNALTYPE_NONE) {
				foreach (delta in [[0, -1], [-1, 0], [1, 0]]) {
					local preprepre = prepre + VC.GetDXY(coordinates, delta)
					if (AIRail.AreTilesConnected(pre, prepre, preprepre)) {
						local signal = AIRail.GetSignalType(prepre, preprepre)
						if (signal == AIRail.SIGNALTYPE_NONE && allowSignalPlacement) {
							LogTile("Building a signal at", prepre, "rail")
							AIRail.BuildSignal(prepre, preprepre, AIRail.SIGNALTYPE_PBS)
						} else if (signal == AIRail.SIGNALTYPE_NORMAL) {
							allowSignalPlacement = true
							LogTile("Replacing a signal at", prepre, "rail")
							AIRail.RemoveSignal(prepre, preprepre)
							AIRail.BuildSignal(prepre, preprepre, AIRail.SIGNALTYPE_PBS)
						}
						//remove other signal if there is one between the new path signal and the crossing
						if (AIRail.GetSignalType(pre, prepre) != AIRail.SIGNALTYPE_NONE) {
							AIRail.RemoveSignal(pre, prepre)
							LogTile("Removing a signal at", pre, "rail")
							allowSignalPlacement = true
						}
						return true
					}
				}
			}
		}
	}
}*/


// A Plan to build tracks between two locations
class RailTrackPlan extends Plan {
	path1 = null
	tiles1 = null
	tiles2 = null
	repairTiles1 = []
	repairTiles2 = []
	reparation1 = null
	reparation2 = null
	passingTrackTiles = []
	exitA = null
	exitB = null
	avoidArea = []
	counters = [2, 2, 2, 2, 1, 0] //tiles1, tiles2, repairTiles1, repairTiles2, passingTrackFollow, passingTrack
	designSpeed = 160
	randomSeed = 0
	passingTrackLength = 20
	
	constructor(trainsystem, station1, station2, data = null) {
		if (data) {
			this.tiles1 = data.tiles1
			this.tiles2 = data.tiles2
			this.repairTiles1 = data.repairTiles1
			this.repairTiles2 = data.repairTiles2
			this.exitA = data.exitA
			this.exitB = data.exitB
			this.avoidArea = data.avoidArea
			this.reparation1 = data.reparation1
			this.reparation2 = data.reparation2
			this.counters = data.counters
			this.designSpeed = data.designSpeed
			this.randomSeed = data.randomSeed
			this.passingTrackTiles = data.passingTrackTiles
			this.passingTrackLength = data.passingTrackLength
			if (this.reparation1) {
				if (BuildRailOnTiles(this.reparation1, true, backwards, false, this.counters, 2)) {
					this.repairTiles1.append(this.reparation1)
					this.reparation1 = null
				} else {
					this.reparation1 = null
				}
			}
			if (this.reparation2) {
				if (BuildRailOnTiles(this.reparation2, false, backwards, false, this.counters, 3)) {
					this.repairTiles2.append(this.reparation2)
					this.reparation2 = null
				} else {
					this.reparation2 = null
				}
			}
		} else {
			this.exitA = station1.exit1
			this.exitB = station2.exit2
			this.path1 = null
			this.tiles1 = null
			this.tiles2 = null
			this.repairTiles1 = []
			this.repairTiles2 = []
			this.avoidArea = []
			this.counters = [2, 2, 2, 2, 1, 0]
			this.designSpeed = trainsystem[3]
			this.passingTrackLength = trainsystem[2]*2+6
			randomSeed = AIBase.RandRange(1000)
			reparation1 = null
			reparation2 = null
			// Check if planning rail towards a terminus station
			if (this.exitB == null) {
				this.exitB = station2.exit1
			}
			//Randomly do pathfinding the other way around
			if (AIBase.RandRange(10) < 5) {
				this.exitA = this.exitB
				this.exitB = station1.exit1
			}
			//Don't do pathfinding through the station
			for (local x = -1; x <= station1.sizeX; x++) {
				for (local y = -1; y <= station1.sizeY; y++) {
					this.avoidArea.append(station1.location + dXY(x, y))
				}
			}
			for (local x = -1; x <= station2.sizeX; x++) {
				for (local y = -1; y <= station2.sizeY; y++) {
					this.avoidArea.append(station2.location + dXY(x, y))
				}
			}
			this.path1 = FindPath(this.exitA, this.exitB, this.avoidArea)
			if (!this.path1)
				this.path1 = FindPath(this.exitB, this.exitA, this.avoidArea)
			if (!this.path1) {
				LogError("Failed finding a suitable path to build rail tracks", "rail")
				return false
			}
			this.tiles1 = GetTilesOn(this.path1)
			this.tiles2 = GetTilesLeftFrom(this.path1)
		}
	}
	
	function getData() {
		return {tiles1 = this.tiles1,
			tiles2 = this.tiles2,
			repairTiles1 = this.repairTiles1,
			repairTiles2 = this.repairTiles2,
			reparation1 = this.reparation1,
			reparation2 = this.reparation2,
			exitA = this.exitA,
			exitB = this.exitB,
			avoidArea = this.avoidArea,
			counters = this.counters,
			designSpeed = this.designSpeed,
			randomSeed = this.randomSeed,
			passingTrackTiles = this.passingTrackTiles,
			passingTrackLength = this.passingTrackLength}
	}
	
		
	function Build(testmode = false) {
		if (!this.tiles2) return false
		if (!BuildRailOnTiles(this.tiles1, true, false, testmode, this.counters, 0)) {
			LogError("Building first track failed", "rail")
			RemoveRailOnTiles(this.tiles1)
			foreach (repair in this.repairTiles1) {
				RemoveRailOnTiles(repair);
			}
			return false
		}
		if (!BuildRailOnTiles(this.tiles2, false, true, testmode, this.counters, 1)) {
			if (testmode) {
				LogWarning("Building second track failed in testmode", "rail")
			} else {
				LogWarning("Building second track failed. I'll try to build parts with two tracks.", "rail")
				foreach (repair in this.repairTiles2) {
					RemoveRailOnTiles(repair);
				}
				if (!BuildPassingSectionsLeftFromTiles(this.tiles1)) {
					LogError("Building parts with two tracks failed, probably ran out of money. Reverting plans.", "rail")
					RemoveRailOnTiles(this.tiles1)
					foreach (repair in this.repairTiles1) {
						RemoveRailOnTiles(repair);
					}
					RemoveRailOnTiles(this.tiles2)
					foreach (repair in this.repairTiles2) {
						RemoveRailOnTiles(repair);
					}
					return false
				}
				ConnectAllStationExitTracks(this.exitA)
				ConnectAllStationExitTracks(this.exitB)
			}
		}
		return true //success
	
	}
	
	function Undo() {
		//RemoveRailOnPath(this.path1);
		RemoveRailOnTiles(this.tiles1);
		RemoveRailOnTiles(this.tiles2);
		foreach (repair in this.repairTiles1) {
			RemoveRailOnTiles(repair);
		}
		foreach (repair in this.repairTiles2) {
			RemoveRailOnTiles(repair);
		}
	}
	
	
	function FindPath(from, to, avoidArea, follow = false) {
		local pathfinder = Rail();
		if (follow)
			pathfinder.setPathToFollow(1)
		pathfinder.cost.max_bridge_length = 12;
		pathfinder.cost.max_tunnel_length = 8;
		pathfinder.estimate_multiplier = 5;
		
		local u = pathfinder.cost.tile;
		pathfinder.cost.slope = 2*u;
		pathfinder.cost.coast = 0.1*u;
		
		// straight, avoiding obstacles
		pathfinder.cost.turn = 1*u;
		pathfinder.cost.diagonal_tile = 55;
		pathfinder.cost.adj_obstacle = 8*u;
		
		local avoidTiles = []
		foreach (avoid in avoidArea) {
			avoidTiles.append(avoid);
		}
		avoidTiles.extend([VC.GetTile(from, [-1,0]),  VC.GetTile(from, [1,0])]);
		avoidTiles.extend([VC.GetTile(from, [2,1]),  VC.GetTile(from, [3,1])]);
		avoidTiles.extend([VC.GetTile(from, [-1,1]), VC.GetTile(from, [-2,2]), VC.GetTile(from, [-3,2])]);
		avoidTiles.extend([VC.GetTile(from, [0,-1]),  VC.GetTile(from, [1,-1]),  VC.GetTile(from, [-1,-1])]);
		avoidTiles.extend([VC.GetTile(from, [0,-2]),  VC.GetTile(from, [1,-2]),  VC.GetTile(from, [-1,-2])]);
		//avoidArea.extend([VC.GetTile(from, [-1,-12]), VC.GetTile(from, [0,-12]), VC.GetTile(from, [1,-12])]);
		//avoidArea.extend([VC.GetTile(to, [-1,-12]), VC.GetTile(to, [0,-12]), VC.GetTile(to, [1,-12])]);
		//avoidArea.extend([VC.GetTile(from, [-1,0]), VC.GetTile(from, [-2,1])]);
		avoidTiles.extend([VC.GetTile(to, [-2,0]), VC.GetTile(to, [0,0])]);
		avoidTiles.extend([VC.GetTile(to, [-3,1]), VC.GetTile(to, [-4,1])]);
		avoidTiles.extend([VC.GetTile(to, [0,1]),  VC.GetTile(to, [1,2]),  VC.GetTile(to, [2,2])]);
		avoidTiles.extend([VC.GetTile(to, [0,-1]),  VC.GetTile(to, [1,-1]),  VC.GetTile(to, [-1,-1])]);
		avoidTiles.extend([VC.GetTile(to, [0,-2]),  VC.GetTile(to, [1,-2]),  VC.GetTile(to, [-1,-2])]);
		pathfinder.InitializePath([[VC.GetTile(to, [-1,0]) , VC.GetTile(to, [-1, -1])]], [[VC.GetTile(from, [0,0]), VC.GetTile(from, [0, -1])]], avoidTiles);
		
		
		local path = false;
		Log("Starting pathfinder at " + RailwAI.GetTick(), "rail")
		LogTiles("path attempt from ", from.location, " to ", VC.GetTile(to, [-1,0]), "rail")
		path = pathfinder.FindPath(20000);
		Log("Ending pathfinder at " + RailwAI.GetTick(), "rail")
		if (!path) {
			LogError("No path found :-(", "rail");
			return false
		}
		if (!ValidatePath(path)) {
			LogError("The path contained a loop or was way too long. Stupid pathfinder :-(", "rail")
			return false
		}
		return path
	}
	
	//Sometimes, the pathfinder finds a path that has a loop in it. Trains fail to follow loops.
	//This function validates whether the path is a path that trains can follow
	function ValidatePath(pathToCheck) {
		local closed = AIList()
		local path = pathToCheck
		local tile = null
		local count = 0
		local start = path.GetTile()
		while (path != null) {
			tile = path.GetTile()
			if (closed.HasItem(tile)) return false
			closed.AddItem(tile, 1)
			path = path.GetParent()
			count++
		}
		if (AIMap.DistanceManhattan(tile, start)*3 + 10 < count) return false
		return true
	}	
	
	//Sometimes, the pathfinder finds a path that has a loop in it, or a bridge at the begin or end of the repaired path.
	//Both should not be allowed, therefore we use this function
	//This function validates whether the path is a path that trains can follow
	function ValidateRepairPath(pathToCheck) {
		local closed = AIList()
		local path = pathToCheck
		local tile = null
		local prevtile = null
		while (path != null) {
			if (prevtile == null && tile != null) {
				prevtile = tile
				tile = path.GetTile()
				if (AIMap.DistanceManhattan(prevtile, tile) > 1)
					return false // no bridge or tunnel allowed here
			}
			prevtile = tile
			tile = path.GetTile()
			if (closed.HasItem(tile)) return false
			closed.AddItem(tile, 1)
			path = path.GetParent()
			if (path == null) {
				if (prevtile == null || AIMap.DistanceManhattan(prevtile, tile) > 1)
					return false // no bridge or tunnel allowed here
			}
		}
		return true
	}
	
	function RepairPath(from, pre, to, after, firstTrack = true, backwards = false, avoidPath = [], avoidPathFrom = 0) {
		local pathfinder = Rail();
		local accountant = AIAccounting()
		//pathfinder.setPathToFollow(1)
		//local lol = AIExecMode()
		pathfinder.cost.max_bridge_length = 14;
		pathfinder.cost.max_tunnel_length = 10;
		pathfinder.estimate_multiplier = 1;
		local u = pathfinder.cost.tile;
		pathfinder.cost.slope = 2*u;
		pathfinder.cost.coast = 0.1*u;
		
		// not avoiding obstacles
		pathfinder.cost.turn = 0.5*u;
		pathfinder.cost.diagonal_tile = 55;
		pathfinder.cost.adj_obstacle = 0;
		//Prevent 90 degree turns by ensuring that the path starts and ends with a straight track section
		local avoidTiles = []
		if (to - after == 1 || after - to == 1) {
			avoidTiles.append(to + dXY(0, 1))
			avoidTiles.append(to + dXY(0, -1))
		} else {
			avoidTiles.append(to + dXY(1, 0))
			avoidTiles.append(to + dXY(-1, 0))
		}
		if (from - pre == 1 || pre - from == 1) {
			avoidTiles.append(from + dXY(0, 1))
			avoidTiles.append(from + dXY(0, -1))
		} else {
			avoidTiles.append(from + dXY(1, 0))
			avoidTiles.append(from + dXY(-1, 0))
		}
		for (local i = avoidPathFrom; i < avoidPath.len(); i++) {
			avoidTiles.append(avoidPath[i][0])
		}
		if (firstTrack) {
			if (this.tiles2) {
				for (local i = 0; i < this.tiles2.len(); i++) {
					avoidTiles.append(this.tiles2[i][0])
				}
			}
		} else {
			for (local i = 0; i < this.tiles1.len(); i++) {
				avoidTiles.append(this.tiles1[i][0])
			}
			foreach (repair in this.repairTiles1) {
				for (local i = 0; i < repair.len(); i++) {
					avoidTiles.append(repair[i][0])
				}
			}
		}
		pathfinder.InitializePath([[to, after]], [[from, pre]], avoidTiles);
			
		local path = false;
		if (AIMap.DistanceManhattan(to, from) < 64) {
			LogWarning("Starting repairing pathfinder at " + RailwAI.GetTick(), "repairpath")
			path = pathfinder.FindPath(1000)
		}
		if (!path) {
			//LogError("No repair path found :-(", "repairpath")
			return false
		}
		if (!ValidateRepairPath(path)) {
			LogTiles("repair path attempt from ", from, " to ", to, "repairpath")
			LogError("Found repair path should not be used :-(", "repairpath")
			return false
		}

		LogTiles("repair path attempt from ", from, " to ", to, "repairpath")
		LogWarning("Ending repairing pathfinder at " + RailwAI.GetTick(), "repairpath")
		if (firstTrack) {
			this.reparation1 = GetTilesOn(path)
			this.counters[2] = 2
			if (BuildRailOnTiles(this.reparation1, true, backwards, false, this.counters, 2)) {
				this.repairTiles1.append(this.reparation1)
				this.reparation1 = null
				return true
			} else {
				this.reparation1 = null
				return false
			}
		} else {
			this.reparation2 = GetTilesOn(path)
			this.counters[3] = 2
			if (BuildRailOnTiles(this.reparation2, false, backwards, false, this.counters, 3)) {
				this.repairTiles2.append(this.reparation2)
				this.reparation2 = null
				return true
			} else {
				this.reparation2 = null
				return false
			}
		}
	}
	
	function BuildPassingSectionsLeftFromTiles(pathToFollow) {
		/*Build passing sections on existing tracks. This method should not be called within testmode!
		* A tile from the resulting list contains an array with data 
		* leftTile[0] = the actual tile
		* leftTile[1] = A boolean representing if there was already rail on this tile (true in corners to the left)
		* leftTile[2] = The direction of the tile
		*/
		//First remove all signals at parts we are not able to build a second track
		foreach (reparation in this.repairTiles1) {
			foreach (tile in reparation) {
				if (!RemoveAllSignals(tile[0]))
					return false
			}
		}
		
		
		local i = counters[4]
		local j = counters[5]
		local k = 0
		local passingLength = 0 //count in half-tiles
		local path = pathToFollow
		local passingTrackTiles = []
		local leftTile = null
		local prevTile = null
		local leftTileBefore = null
		
		local tileP = null
		local prevP = null
		local prevprevP = null
		local directionP = 0
		local prevdirectionP = 0
		local doubleP = false
		local prev_doubleP = false
		local s = 0
		for (; i < path.len(); i++) {
			local tile = path[i][0]
			local prev = path[i-1][0]
			local prevprev = null
			local doublerail = path[i][1]
			local prev_double = path[i-1][1]
			local newDirection = path[i][2]
			local direction = path[i-1][2]
			if (i > 1) {
				prevprev = path[i-2][0]
			} else {
				prevprev = prev - tile + prev
			}
			local succes = true		
			local leftTiles = []
			
			//if (AIRail.GetSignalType(tile, prev) != AIRail.SIGNALTYPE_NONE) AIRail.RemoveSignal(tile, prev)
			if (AIRail.GetSignalType(prev, tile) != AIRail.SIGNALTYPE_NONE) {
				if (!AIRail.RemoveSignal(prev, tile)) {
					return false
				}
			}
			if (!RemoveAllSignals(tile)) return false
			
			//check if we are actually following the other track
			if (AIRail.IsRailTile(tile) && AIRail.IsRailTile(prev) && AIRail.IsRailTile(prevprev) && AreRailTilesConnected(tile,prev,prevprev)) {
				if (newDirection == 255 || newDirection == 0) newDirection = direction
				if (newDirection == direction) {
					//easy stuff
					if (direction == Direction.SE) leftTile = tile + 1
					if (direction == Direction.NW) leftTile = tile - 1
					if (direction == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
					if (direction == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
					if (prevTile != leftTile && (!AIRail.IsRailTile(leftTile) || passingTrackTiles.len() == 0)) {
						leftTiles.append([leftTile, false, newDirection])
					}
					passingLength += 2
				} else if ((newDirection % 4) == ((direction+1) % 4)) {//diagonal to the left
					passingLength++
				} else if ((direction % 4) == ((newDirection+1) % 4)) {
					local turnDirection = (newDirection %4) + 1
					//diagonal to the right
					if (direction == Direction.SE) leftTile = tile + 1
					if (direction == Direction.NW) leftTile = tile - 1
					if (direction == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
					if (direction == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
					if (prevTile != leftTile && (!AIRail.IsRailTile(leftTile) || passingTrackTiles.len() == 0)) {
						leftTiles.append([leftTile, false, turnDirection])
					}
					leftTile = tile
					leftTiles.append([leftTile, true, newDirection])
					if (newDirection == Direction.SE) leftTile = tile + 1
					if (newDirection == Direction.NW) leftTile = tile - 1
					if (newDirection == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
					if (newDirection == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
					if (!AIRail.IsRailTile(leftTile) || passingTrackTiles.len() == 0)
						leftTiles.append([leftTile, false, turnDirection])
					passingLength++
				} else {
					LogError("old " + direction + "  new " + newDirection)
					throw("shouldn't come here in following a path")
				}
				prevTile = leftTile
			} else {
				succes = false
			}
			if (i < 2 && !leftTileBefore && leftTile) {
				leftTileBefore = leftTile + prev - tile
			}
			if (passingTrackTiles.len() < 2) {
				if (leftTiles.len() != 1 || AIRail.GetRailTracks(prev) != AIRail.GetRailTracks(tile)) {
					leftTiles = []
					succes = false
				}
			}
			if (succes) {
				passingTrackTiles.append(leftTiles)
			}
			s++
			for (k = 0; k < leftTiles.len() && succes; k++) {
				prevprevP = prevP
				prevP = tileP
				prevdirectionP = directionP
				prev_doubleP = doubleP
				tileP = leftTiles[k][0]
				doubleP = leftTiles[k][1]
				directionP = leftTiles[k][2]
				if (prevprevP == null) {
					if (prevP != null) {
						if (i > 2)
							succes = AIRail.BuildRail(tileP, prevP, path[i-2][0])
						else
							succes = AIRail.BuildRail(tileP, prevP, prevP + prevP - tileP)
						if (!RemoveAllSignals(prevP)) return false
						AIRail.BuildSignal(prevP, tileP, AIRail.SIGNALTYPE_PBS)
						if (i+1 < path.len() && i > 0) {
							if (!RemoveAllSignals(path[i][0])) return false
							AIRail.BuildSignal(path[i][0], path[i+1][0], AIRail.SIGNALTYPE_PBS)
						}
						s = 0
					}
				} else {
					if (AIMap.DistanceManhattan(prevP, tileP) > 1) {
						/*if (AITunnel.GetOtherTunnelEnd(prevP) == tileP) {
							if (!AITunnel.BuildTunnel(AIVehicle.VT_RAIL, prevP) && !IsAlreadyBuilt()) {
								succes = false
							}
						} else {
							local length = AIMap.DistanceManhattan(tileP, prevP)
							local bridge_list = AIBridgeList_Length(length + 1)
							bridge_list.Valuate(AIBridge.GetMaxSpeed)
							bridge_list.KeepAboveValue(designSpeed)
							if (bridge_list.Count() > 0) {
								bridge_list.Valuate(AIBridge.GetMaxSpeed)
								bridge_list.KeepBottom(4)
								bridge_list.RemoveTop(randomSeed % bridge_list.Count())
							} else {
								bridge_list = AIBridgeList_Length(length + 1)
								bridge_list.Valuate(AIBridge.GetMaxSpeed)
								bridge_list.Sort(AIAbstractList.SORT_BY_VALUE, false)
							}
							if (!AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridge_list.Begin(), prevP, tileP) && !IsAlreadyBuilt()) {
								succes = (BuildTunnel(AIVehicle.VT_RAIL, prevP, tileP, directionP, length, false))
								LogWarning(succes)
							}
						}*/
						succes = false //we fail anyways
					} else {
						if (!AIRail.BuildRail(prevprevP, prevP, tileP) && !IsAlreadyBuilt()) {
							if (prev_doubleP) {
								if (prevdirectionP == Direction.SW) (AITile.RaiseTile(prevP, AITile.SLOPE_W) || AITile.LowerTile(prevP, AITile.SLOPE_W))
								if (prevdirectionP == Direction.SE) (AITile.RaiseTile(prevP, AITile.SLOPE_S) || AITile.LowerTile(prevP, AITile.SLOPE_S))
								if (prevdirectionP == Direction.NE) (AITile.RaiseTile(prevP, AITile.SLOPE_E) || AITile.LowerTile(prevP, AITile.SLOPE_E))
								if (prevdirectionP == Direction.NW) (AITile.RaiseTile(prevP, AITile.SLOPE_N) || AITile.LowerTile(prevP, AITile.SLOPE_N))
								if (!AIRail.BuildRail(prevprevP, prevP, tileP) && !IsAlreadyBuilt())
									succes = false
							} else if (prevdirectionP != directionP) { //diagonal tile
								if (prevdirectionP == Direction.SW) (AITile.RaiseTile(prevP, AITile.SLOPE_E)  || AITile.LowerTile(prevP, AITile.SLOPE_E))
								if (prevdirectionP == Direction.SE) (AITile.RaiseTile(prevP, AITile.SLOPE_N)  || AITile.LowerTile(prevP, AITile.SLOPE_N))
								if (prevdirectionP == Direction.NE) (AITile.RaiseTile(prevP, AITile.SLOPE_W)  || AITile.LowerTile(prevP, AITile.SLOPE_W))
								if (prevdirectionP == Direction.NW) (AITile.RaiseTile(prevP, AITile.SLOPE_S)  || AITile.LowerTile(prevP, AITile.SLOPE_S))
								if (!AIRail.BuildRail(prevprevP, prevP, tileP) && !IsAlreadyBuilt()) {
									succes = false
								}
							} else {
								succes = false
							}
						}
					}
					if (succes && k == 0 && s >= 4) {
						//Note: this part sometimes causes to have signals where there should be no signals
						if (!RemoveAllSignals(prevP)) return false
						AIRail.BuildSignal(prevP, tileP, AIRail.SIGNALTYPE_PBS_ONEWAY)
						if (passingLength >= passingTrackLength) {
							if (!RemoveAllSignals(path[i][0])) return false
							AIRail.BuildSignal(path[i][0], path[i-1][0], AIRail.SIGNALTYPE_PBS_ONEWAY)
						}
						s = 0
					}
				}
			}
			if (i + 1 == path.len()) {
				if (!tileP || !prevP || (!AIRail.BuildRail(prevP, tileP, tileP + tile - prev) && !IsAlreadyBuilt())) {
					succes = false
					if (tileP)
						ConnectRailTile(tileP + tile - prev)
				} else {
					if (!RemoveAllSignals(path[i][0])) return false
					if (!RemoveAllSignals(tileP)) return false
					AIRail.BuildSignal(path[i][0], path[i-1][0], AIRail.SIGNALTYPE_PBS)
					AIRail.BuildSignal(tileP, prevP, AIRail.SIGNALTYPE_PBS)
				}
				//this piece was too short to have signals
				if (passingLength < passingTrackLength && succes) {
					while (j > 0) {
						for (k = 0; k < passingTrackTiles[j].len(); k++) {
							tileP = passingTrackTiles[j][k][0]
							prevP = 0
							if (k > 0) {
								prevP = passingTrackTiles[j][k-1][0]
							} else if (passingTrackTiles[j-1].len() > 0) {
								prevP = passingTrackTiles[j-1][passingTrackTiles[j-1].len()-1][0]
							} else if (passingTrackTiles[j-2].len() > 0) {
								prevP = passingTrackTiles[j-2][passingTrackTiles[j-2].len()-1][0]
							} else if (j > 2 && passingTrackTiles[j-3].len() > 0) {
								prevP = passingTrackTiles[j-3][passingTrackTiles[j-3].len()-1][0]
							}
							if (AIRail.GetSignalType(prevP, tileP) != AIRail.SIGNALTYPE_NONE)
								AIRail.RemoveSignal(prevP, tileP)
							if (AIRail.GetSignalType(tileP, prevP) != AIRail.SIGNALTYPE_NONE)
								AIRail.RemoveSignal(tileP, prevP)
						}
						j--
					}
				}
			}
			if (!succes) {
				/*if (j > 2) {
					LogWarning("Failed building passing track: " + AIError.GetLastErrorString())
					LogTile("prevprevP", prevprevP)
					LogTile("prevP", prevP)
					LogTile("tileP", tileP)
				}*/
				if (!RemoveAllSignals(path[i][0])) return false
				j--
				i--
				local countRemoved = 0
				while(!succes && j > 0) {
					//remove signals (if there)
					if (AIRail.GetSignalType(prevP, tileP) != AIRail.SIGNALTYPE_NONE)
						AIRail.RemoveSignal(prevP, tileP)
					if (AIRail.GetSignalType(tileP, prevP) != AIRail.SIGNALTYPE_NONE)
						AIRail.RemoveSignal(tileP, prevP)
					if (!RemoveAllSignals(path[i][0])) return false
					if (i > 0) {
						if (!RemoveAllSignals(path[i-1][0])) return false
					}
					
					//make end to passing track section if straight section
					if (j > 7 && passingLength >= passingTrackLength && j + 1 < passingTrackTiles.len() && passingTrackTiles[j].len() == 1 && passingTrackTiles[j+1].len() == 1
						&& passingTrackTiles[j-1].len() == 1) {
						tileP = passingTrackTiles[j][0][0]
						prevP = passingTrackTiles[j-1][0][0]
						prevprevP = prevP - (tileP - prevP)
						local next = path[i+1][0]
						succes = AIRail.BuildRail(prevP, tileP, next)
						if (AIRail.GetSignalType(prevprevP, prevP) != AIRail.SIGNALTYPE_NONE)
							AIRail.RemoveSignal(prevprevP, prevP)
						if (AIRail.GetSignalType(prevP, tileP) != AIRail.SIGNALTYPE_NONE)
							AIRail.RemoveSignal(prevP, tileP)
						if (succes) {
							if (!RemoveAllSignals(prevP))
								return false
							AIRail.BuildSignal(prevP, prevprevP, AIRail.SIGNALTYPE_PBS)
							if (AIRail.GetSignalType(path[i-1][0], path[i-2][0]) == AIRail.SIGNALTYPE_NONE) {
								if (!RemoveAllSignals(path[i-1][0])) return false
								AIRail.BuildSignal(path[i-1][0], path[i-2][0], AIRail.SIGNALTYPE_PBS) //SIGNALTYPE_PBS_ONEWAY
							}
							ConnectRailTile(path[i][0]) //make sure it is connected
						}
						
						//LogTile("next", next, "rail")
						//LogTile("prevP", prevP, "rail")
						//LogTile("tileP", tileP, "rail")
					}
					if (j == 1) {
						local nextP = passingTrackTiles[1][0][0] + passingTrackTiles[1][0][0] - passingTrackTiles[0][0][0]
						AIRail.RemoveRail(passingTrackTiles[0][0][0], passingTrackTiles[1][0][0], nextP)
						if (i > 2) {
							AIRail.RemoveRail(passingTrackTiles[1][0][0], passingTrackTiles[0][0][0], path[i-2][0])
						} else {
							nextP = passingTrackTiles[0][0][0] + passingTrackTiles[0][0][0] - passingTrackTiles[1][0][0]
							AIRail.RemoveRail(passingTrackTiles[1][0][0], passingTrackTiles[0][0][0], nextP)
							ConnectRailTile(nextP)
						}
					}
					//Log(j + " - " + passingTrackTiles[j].len(), "rail")
					if (j + 1 < passingTrackTiles.len() && j > 1 && passingTrackTiles[j].len() >= 1) {

						//remove rail
						for (k = 0; k < passingTrackTiles[j].len(); k++) {
							tileP = passingTrackTiles[j][k][0]
							prevP = 0
							local nextP = 0
							if (k > 0) {
								prevP = passingTrackTiles[j][k-1][0]
							} else if (passingTrackTiles[j-1].len() > 0) {
								prevP = passingTrackTiles[j-1][passingTrackTiles[j-1].len()-1][0]
							} else if (passingTrackTiles[j-2].len() > 0) {
								prevP = passingTrackTiles[j-2][passingTrackTiles[j-2].len()-1][0]
							} else if (j > 2 && passingTrackTiles[j-3].len() > 0) {
								prevP = passingTrackTiles[j-3][passingTrackTiles[j-3].len()-1][0]
							}
							if (k + 1 < passingTrackTiles[j].len()) {
								nextP = passingTrackTiles[j][k+1][0]
							} else if (passingTrackTiles[j+1].len() > 0) {
								nextP = passingTrackTiles[j+1][0][0]
							} else if (j + 2 < passingTrackTiles.len() && passingTrackTiles[j+2].len() > 0) {
								nextP = passingTrackTiles[j+2][0][0]
							} else if (j + 3 < passingTrackTiles.len() && passingTrackTiles[j+3].len() > 0) {
								nextP = passingTrackTiles[j+3][0][0]
							}
							
							//LogTile("nextP", nextP, "rail")
							//LogTile("prevP", prevP, "rail")
							//LogTile("tileP", tileP, "rail")
							AIRail.RemoveRail(nextP, tileP, prevP)
						}
					}
					j--
					i--
					countRemoved++
					passingLength -= 2
				}
				
				passingLength = 0
				i += countRemoved + 1
				passingTrackTiles = []
				tileP = null
				prevP = null
				prevprevP = null
				directionP = 0
				prevdirectionP = 0
				doubleP = false
				prev_doubleP = false
				j = 0
				
			} else {
				j++
			}
			if (counters != null) {
				counters[4] = i
				counters[5] = j
			}
		}
		if (j < 10) {
			//this part was too short to have signals here
			while (j > 0) {
				if (passingTrackTiles.len() > j) {
					for (k = 0; k < passingTrackTiles[j].len(); k++) {
						tileP = passingTrackTiles[j][k][0]
						prevP = 0
						if (k > 0) {
							prevP = passingTrackTiles[j][k-1][0]
						} else if (passingTrackTiles[j-1].len() > 0) {
							prevP = passingTrackTiles[j-1][passingTrackTiles[j-1].len()-1][0]
						} else if (passingTrackTiles[j-2].len() > 0) {
							prevP = passingTrackTiles[j-2][passingTrackTiles[j-2].len()-1][0]
						} else if (j > 2 && passingTrackTiles[j-3].len() > 0) {
							prevP = passingTrackTiles[j-3][passingTrackTiles[j-3].len()-1][0]
						}
						if (AIRail.GetSignalType(prevP, tileP) != AIRail.SIGNALTYPE_NONE)
							AIRail.RemoveSignal(prevP, tileP)
						if (AIRail.GetSignalType(tileP, prevP) != AIRail.SIGNALTYPE_NONE)
							AIRail.RemoveSignal(tileP, prevP)
						
					}
				}
				j--
			}
		}
		if (leftTileBefore)
			ConnectRailTile(leftTileBefore)
		return true
	}

	
	//Build rail on a list of Tiles (or repair parts if we cannot build those parts)
	//When not in testmode, we should update the counter in counters with index cIndex
	
	function BuildRailOnTiles(path, firstTrack, backwards = false, testmode = false, counters = null, cIndex = 0) {
		local s = 2;
		local first_fail = -1
		local last_repair = -1
		local i = 2;
		//restore starting value if provided
		if (!testmode && counters) {
			i = counters[cIndex]
			s = 3
		}
		for (; i < path.len(); i++) {
			//RailwAI.Sleep(2)
			local tile = path[i][0]
			local prev = path[i-1][0]
			local prevprev = path[i-2][0]
			local pprevprev = i<3?null:path[i-3][0]
			local doublerail = path[i][1]
			local prev_double = path[i-1][1]
			local direction = path[i][2]
			local prev_direction = path[i-1][2]
			local succes = true
			if (first_fail == -1) {
				if (AIMap.DistanceManhattan(prev, tile) > 1) {
					if (AITunnel.GetOtherTunnelEnd(prev) == tile) {
						if (!AITunnel.BuildTunnel(AIVehicle.VT_RAIL, prev) && !IsAlreadyBuilt()) {
							LogError("Failed building tunnel", "rail");
							succes = false
							i--
						}
					} else {
						local length = AIMap.DistanceManhattan(tile, prev)
						
						local bridge_list = AIBridgeList_Length(length + 1)
						bridge_list.Valuate(AIBridge.GetMaxSpeed)
						bridge_list.KeepAboveValue(designSpeed)
						if (bridge_list.Count() > 0) {
							bridge_list.Valuate(AIBridge.GetMaxSpeed)
							bridge_list.KeepBottom(4)
							bridge_list.RemoveTop(randomSeed % bridge_list.Count())
						} else {
							bridge_list = AIBridgeList_Length(length + 1)
							bridge_list.Valuate(AIBridge.GetMaxSpeed)
							bridge_list.Sort(AIAbstractList.SORT_BY_VALUE, false)
						}
						LogTile("Building bridge from", prev)
						LogTile("Building bridge to", tile)
						if (!AIBridge.BuildBridge(AIVehicle.VT_RAIL, bridge_list.Begin(), prev, tile) && !IsAlreadyBuilt()) {
							succes = false
							i--
							if (BuildTunnel(AIVehicle.VT_RAIL, prev, tile, direction, length, testmode)) {
								succes = true
								i++
							} else {
								LogError("Failed building a bridge or tunnel:", "rail")
								LogTile("Start tile:", prev, "rail")
								LogTile("End tile:  ", tile, "rail")
							}
							
						}
					}
					i++
					//build path signal before any bridge/tunnel
					s == 4;
				} else {
					if (!AIRail.BuildRail(prevprev, prev, tile) && !IsAlreadyBuilt()) {
						if (prev_double) {
							//There is already rail on this tile, we may not clear this tile
							//We couldn't build it because of the slope
							if (prev_direction == Direction.SW) (AITile.RaiseTile(prev, AITile.SLOPE_W) || AITile.LowerTile(prev, AITile.SLOPE_W))
							if (prev_direction == Direction.SE) (AITile.RaiseTile(prev, AITile.SLOPE_S) || AITile.LowerTile(prev, AITile.SLOPE_S))
							if (prev_direction == Direction.NE) (AITile.RaiseTile(prev, AITile.SLOPE_E) || AITile.LowerTile(prev, AITile.SLOPE_E))
							if (prev_direction == Direction.NW) (AITile.RaiseTile(prev, AITile.SLOPE_N) || AITile.LowerTile(prev, AITile.SLOPE_N))
							if (!testmode && !AIRail.BuildRail(prevprev, prev, tile) && !IsAlreadyBuilt())
								succes = false
						} else if (prev_direction != direction) { //diagonal tile
							if (prev_direction == Direction.SW) (AITile.RaiseTile(prev, AITile.SLOPE_E)  || AITile.LowerTile(prev, AITile.SLOPE_E))
							if (prev_direction == Direction.SE) (AITile.RaiseTile(prev, AITile.SLOPE_N)  || AITile.LowerTile(prev, AITile.SLOPE_N))
							if (prev_direction == Direction.NE) (AITile.RaiseTile(prev, AITile.SLOPE_W)  || AITile.LowerTile(prev, AITile.SLOPE_W))
							if (prev_direction == Direction.NW) (AITile.RaiseTile(prev, AITile.SLOPE_S)  || AITile.LowerTile(prev, AITile.SLOPE_S))
							if (!testmode && !AIRail.BuildRail(prevprev, prev, tile) && !IsAlreadyBuilt()) {
								succes = false
							}
						} else { //non-diagonal
							succes = false
						}
					}
				}
				if (!succes) {
					LogError("Failed building rail: " + AIError.GetLastErrorString(), "rail");
					LogTile("Failed correcting at", prev, "rail")
					first_fail = i-1
					i++
				} else {
					if (!testmode && counters != null) {
						counters[cIndex] = i
					}
				}
				if (i == 2) {
					if (AIRail.GetSignalType(prev, tile) == AIRail.SIGNALTYPE_NONE)
						AIRail.BuildSignal(prev, tile, AIRail.SIGNALTYPE_PBS)
				} else if (i == path.len() - 1) {
					if (AIRail.GetSignalType(prev,  prevprev) == AIRail.SIGNALTYPE_NONE)
						AIRail.BuildSignal(prev,  prevprev, AIRail.SIGNALTYPE_PBS)
				} else if (s >= 4) {
					if (AIRail.GetSignalType(prevprev,  backwards?prev:pprevprev) == AIRail.SIGNALTYPE_NONE) {
						if (AIRail.BuildSignal(prevprev,  backwards?prev:pprevprev, AIRail.SIGNALTYPE_NORMAL))
							s = 0
					} else {
						s = 0
					}
				}
				s += 1;
				
			//Don't repair the path if we are building a repaired path (cIndex == 2)
			} else if (cIndex < 2 && RepairPath(path[first_fail][0], path[first_fail-1][0], path[i-2][0], path[i-1][0], firstTrack, backwards, path, i)) {
				Log("Path repaired", "rail")	
				//LogTile("prevprev", prevprev)
				//LogTile("prev", prev)
				//LogTile("tile", tile)
				i--
				first_fail = -1
			} else if (first_fail > 2) {
				tile = path[first_fail][0]
				prev = path[first_fail-1][0]
				prevprev = path[first_fail-2][0]
				direction = path[first_fail][2]
				prev_direction = path[first_fail-1][2]
				LogTile("Removing rail at", prev, "rail")
				if (first_fail > last_repair && (AIRail.RemoveRail(prevprev, prev, tile) || (AIBase.RandRange(10) < 2))) {
					first_fail -= 1
				}
				if (i == path.len() - 1 && i - first_fail < 32) {
					i -= 1
				}
			}
		}
		if (first_fail != -1) {
			RemoveRailOnTiles(path, first_fail)
			return false
		}
		return true
	}
	
	function GetTilesOn(pathToFollow) {
		/*A tile from the resulting list contains an array with data 
		* tile[0] = the actual tile
		* tile[1] = A boolean representing if there was already rail on this tile (must be false of course)
		* tile[2] = The direction of the tile
		*/
		local path = pathToFollow
		local tile = path.GetTile()
		local tiles = []
		local prevPath = path.GetParent()
		local prevTile = null
		if (prevPath)
			prevTile = prevPath.GetTile()
		local direction = Rail._dir(prevTile, tile, true)
		prevTile = null
		while (path != null) {
			tile = path.GetTile()
			local newDirection = path.GetDirection()
			//In the begin, direction is zero (why?)
			//In the end, direction is 255 (why?)
			//Assume that we start and end with a straight piece of rail
			if (newDirection == 255 || newDirection == 0) newDirection = direction
			tiles.append([tile, false, newDirection])
			
			//LogTile("orig" + direction, tile)
			prevTile = tile
			direction = newDirection
			path = path.GetParent()
		}
		return tiles
	
	}
	
	function GetTilesLeftFrom(pathToFollow) {
		/*A tile from the resulting list contains an array with data 
		* leftTile[0] = the actual tile
		* leftTile[1] = A boolean representing if there was already rail on this tile (true in corners to the left)
		* leftTile[2] = The direction of the tile
		*/
		local path = pathToFollow
		local tile = path.GetTile()
		local leftTiles = []
		local leftTile = null
		local prevPath = path.GetParent()
		local prevTile = null
		local direction = 255
		if (prevPath) {
			prevTile = prevPath.GetTile()
			direction = Rail._dir(prevTile, tile)
		}
		prevTile = null
		while (path != null) {
			tile = path.GetTile()
			local newDirection = path.GetDirection()
			//In the begin, direction is zero (why?)
			//In the end, direction is 255 (why?)
			//Assume that we start and end with a straight piece of rail
			if (newDirection == 255 || newDirection == 0) newDirection = direction
			if (newDirection == direction) {
				//easy stuff
				if (direction == Direction.SE) leftTile = tile + 1
				if (direction == Direction.NW) leftTile = tile - 1
				if (direction == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
				if (direction == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
				if (prevTile != leftTile && (!AIRail.IsRailTile(leftTile) || leftTiles.len() == 0)) {
					leftTiles.append([leftTile, false, newDirection])
					//LogTile("left" + newDirection, leftTile)
				}
			} else if ((newDirection % 4) == ((direction+1) % 4)) {
				//diagonal to the left
			} else if ((direction % 4) == ((newDirection+1) % 4)) {
				local turnDirection = (newDirection %4) + 1
				//diagonal to the right
				if (direction == Direction.SE) leftTile = tile + 1
				if (direction == Direction.NW) leftTile = tile - 1
				if (direction == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
				if (direction == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
				if (prevTile != leftTile && (!AIRail.IsRailTile(leftTile) || leftTiles.len() == 0)) {
					leftTiles.append([leftTile, false, turnDirection])
					//LogTile("right" + ((newDirection %4) + 1), leftTile)
				}
				leftTile = tile
				//LogTile("right" + newDirection, leftTile)
				leftTiles.append([leftTile, true, newDirection])
				if (newDirection == Direction.SE) leftTile = tile + 1
				if (newDirection == Direction.NW) leftTile = tile - 1
				if (newDirection == Direction.NE) leftTile = tile + AIMap.GetMapSizeX()
				if (newDirection == Direction.SW) leftTile = tile - AIMap.GetMapSizeX()
				if (!AIRail.IsRailTile(leftTile) || leftTiles.len() == 0)
					leftTiles.append([leftTile, false, turnDirection])
				//LogTile("right" + turnDirection, leftTile)
			} else {
				LogError("old " + direction + "  new " + newDirection)
				throw("shouldn't come here in following a path")
			}
			//LogTile("orig" + direction, tile)
			prevTile = leftTile
			direction = newDirection
			path = path.GetParent()
			if (path == null && AIRail.IsRailTile(leftTile)) {
				leftTiles.append([leftTile, false, newDirection])
			}
		}
		return leftTiles
	}
	
	function RemoveRailOnTiles(path, removeUntil = 0) {
		if (path == null) {
			return true
			LogError("Removing from null!")
		}
		local end = path.len()
		if (removeUntil > 0 && removeUntil < end) end = removeUntil+1
		LogWarning("Removing rails on a set of tiles until " + end + " / " + removeUntil)
		for (local i = 2; i < end; i++) {
			local tile = path[i][0]
			local prev = path[i-1][0]
			local prevprev = path[i-2][0]
			if (AIMap.DistanceManhattan(prev, tile) > 1) {
				if (AITunnel.GetOtherTunnelEnd(prev) == tile) {
					AITunnel.RemoveTunnel(prev)
				} else if (AIBridge.IsBridgeTile(prev)) {
					AIBridge.RemoveBridge(prev)
				} else {
					local length = AIMap.DistanceManhattan(tile, prev)
					local delta = (tile-prev)/length
					if (AITunnel.GetOtherTunnelEnd(prev+delta) == tile-delta) {
						AITunnel.RemoveTunnel(prev+delta)
						AIRail.RemoveRail(prev-delta, prev, prev+delta)
						LogTile("Removing tunnel rail at", prev, "rail")
						AIRail.RemoveRail(tile-delta, tile, tile+delta)
						LogTile("Removing tunnel rail at", tile, "rail")
					}
				}
			} else {
				LogTile("Removing rail at", prev, "rail")
				AIRail.RemoveRail(prevprev, prev, tile)
			}
		}
		return true
	}
	
	function RemoveRailOnPath(path) {
		local tile = null
		local prev = null
		local prevprev = null
		local pprevprev = null
		while (path != null) {
			tile = path.GetTile()
			if (prevprev != null) {
				//Tunnels and Bridges will be demolished too. It costs money :-(
				if (AIMap.DistanceManhattan(prev, tile) > 1) {
					if (AITunnel.GetOtherTunnelEnd(prev) == tile) {
						AITunnel.RemoveTunnel(prev)
					} else {
						AIBridge.RemoveBridge(prev)
					}
				} else {
					AIRail.RemoveRail(prevprev, prev, tile);
				}
			}
			if (path != null) {
				pprevprev = prevprev;
				prevprev = prev;
				prev = tile;
				path = path.GetParent();
			}
		}
	}
}