/* For each group of vehicles, this function checks if a vehicle should turn around at the next stop
 * If so, one of that group of vehicles will turn around at the next stop
 */
function RailwAI::RedistributeVehicles() {
	local groupList = AIGroupList()
	for (local group = groupList.Begin(); groupList.HasNext(); group = groupList.Next()) {
		local list = AIVehicleList_Group(group)
		local numVehicles = list.Count()
		if (numVehicles == 0) {
			LogError("I found an empty group. Deleting this group that has name " + AIGroup.GetName(group) + ".")
			AIGroup.DeleteGroup(group)
			continue
		}
		local maximum = 0
		local busyOrder = null
		local turnVehicle = list.Begin()
		local countOrder = array(AIOrder.GetOrderCount(turnVehicle), 0)
		for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
			local curr = AIOrder.ResolveOrderPosition(vehicle, AIOrder.ORDER_CURRENT)
			countOrder[curr] = countOrder[curr] + 1
			if (countOrder[curr] > maximum) {
				maximum = countOrder[curr]
				busyOrder = curr
				turnVehicle = vehicle
			}
		}
		if (maximum > 2) {
			local destination = AIOrder.GetOrderDestination(turnVehicle, busyOrder)
			for (local i = 0; i < AIOrder.GetOrderCount(turnVehicle); i++) {
				if (i != busyOrder && AIOrder.GetOrderDestination(turnVehicle, i) == destination && countOrder[i] + 1 < maximum) {
					AIOrder.SkipToOrder(turnVehicle, i)
					LogWarning("Vehicle " + AIVehicle.GetName(turnVehicle) + " will turn around at the next stop")
				}
			}
		}
	}
}

//Unprofitable vehicles should be sent to depots as well as too old vehicles
function RailwAI::CheckUnprofitableVehicles() {
	local vehicleList = AIVehicleList()
	local age_multiplier = 2 //if breakdowns are disabled, renew vehicle if it has twice its maximum age
	if (AIGameSettings.GetValue("difficulty.vehicle_breakdowns") >= 1) age_multiplier = 1

	for (local vehicle = vehicleList.Begin(); vehicleList.HasNext(); vehicle = vehicleList.Next()) {
		//sometimes it happens that there are vehicles with empty orders... get rid of those vehicles
		if (AIOrder.GetOrderCount(vehicle) < 2) {
			AIVehicle.SendVehicleToDepot(vehicle)
		} else if (AIVehicle.GetAge(vehicle) > 700 && AIVehicle.GetProfitThisYear(vehicle) < -100 && AIVehicle.GetProfitLastYear(vehicle) < -50) {
			local cargo = GetVehicleCargoType(vehicle)
			local load = AIVehicle.GetCargoLoad(vehicle, cargo)
			local capacity = AIVehicle.GetCapacity(vehicle, cargo)
			if (AIBase.RandRange(100) > (100 * load) / capacity) {
				AIVehicle.SendVehicleToDepot(vehicle)
			}
		} else if (AIVehicle.GetAge(vehicle) > AIVehicle.GetMaxAge(vehicle) * age_multiplier) {
			AIVehicle.SendVehicleToDepot(vehicle)
		}
	}
}

//Too old vehicles should be sent to depots
function RailwAI::CheckOldVehicles() {

	local vehicleList = AIVehicleList()
	for (local vehicle = vehicleList.Begin(); vehicleList.HasNext(); vehicle = vehicleList.Next()) {
		//sometimes it happens that there are vehicles with empty orders... get rid of those vehicles
		if (AIOrder.GetOrderCount(vehicle) < 2) {
			AIVehicle.SendVehicleToDepot(vehicle)
		} else if (AIVehicle.GetAge(vehicle) > 700 && AIVehicle.GetProfitThisYear(vehicle) < -100 && AIVehicle.GetProfitLastYear(vehicle) < -50) {
			local cargo = GetVehicleCargoType(vehicle)
			local load = AIVehicle.GetCargoLoad(vehicle, cargo)
			local capacity = AIVehicle.GetCapacity(vehicle, cargo)
			if (AIBase.RandRange(100) > (100 * load) / capacity) {
				AIVehicle.SendVehicleToDepot(vehicle)
			}
		}
	}		
}

/* It can occur that a vehicle doesn't go anywhere, e.g. when a ship is lost, or roads were removed
	around a tram or bus. This function will free vehicles
 */
function RailwAI::CheckNonMovingVehicles() {
	local vehicleList = AIVehicleList()

	// Make sure all vehicles are moving
	vehicleList = AIVehicleList()
	for (local vehicle = vehicleList.Begin(); vehicleList.HasNext(); vehicle = vehicleList.Next()) {
		if (!this.vehicleLocations.HasItem(vehicle)) {
			this.vehicleLocations.AddItem(vehicle, AIVehicle.GetLocation(vehicle))
			continue
		}
		local location = AIVehicle.GetLocation(vehicle)
		local vehicleType = AIVehicle.GetVehicleType(vehicle)
		if (AIVehicle.IsStoppedInDepot(vehicle)) {
			//Vehicles stopped in a depot have been sent for a reason
			if (AIOrder.GetOrderCount(vehicle) < 2) {
				LogWarning("Selling orderless vehicle with number " + AIVehicle.GetUnitNumber(vehicle))
				AIVehicle.SellVehicle(vehicle)
			} else if (AIVehicle.GetProfitLastYear(vehicle) < 0) {
				//the vehicle was unprofitable. Sell it
				//For trains, we sell 50% of the carriages in an attempt of making the train (more) profitable
				if (vehicleType == AIVehicle.VT_RAIL && AIVehicle.GetNumWagons(vehicle) > 2) {
					LogWarning("Decreasing the capacity of train " + AIVehicle.GetUnitNumber(vehicle))
					for (local i = 0; i < AIVehicle.GetNumWagons(vehicle)-1; i++) {
						if (AIVehicle.GetWagonEngineType(vehicle, i) == AIVehicle.GetWagonEngineType(vehicle, i+1)) {
							AIVehicle.SellWagon(vehicle, i+1) //the second wagon is usually loaded less
						}
					}
					AIVehicle.StartStopVehicle(vehicle)
				} else {
					LogWarning("Selling unprofitable vehicle with number " + AIVehicle.GetUnitNumber(vehicle))
					AIVehicle.SellVehicle(vehicle)
				}
			} else if (AIVehicle.GetAge(vehicle) > AIVehicle.GetMaxAge(vehicle)) {
				//buy a new, similar vehicle if available
				if (vehicleType == AIVehicle.VT_RAIL) {
					BuyTrainLikeThis(vehicle)
				} else {
					BuyVehicleLike(vehicle)
				}
				//sell this vehicle, because it is too old
				LogWarning("Selling too old vehicle with number " + AIVehicle.GetUnitNumber(vehicle))
				AIVehicle.SellVehicle(vehicle)
			} else if (vehicleType == AIVehicle.VT_RAIL) {
				//Train was sent to the depot because the train length must be adjusted
				local cargoType = GetVehicleCargoType(vehicle)
				local loadPercentage = AIVehicle.GetCargoLoad(vehicle, cargoType) * 100 / AIVehicle.GetCapacity(vehicle, cargoType)
				if (loadPercentage < 50) {
					//train is too long, sell 50% of carriages
					if (vehicleType == AIVehicle.VT_RAIL && AIVehicle.GetNumWagons(vehicle) > 2) {
						LogWarning("Decreasing the capacity of train " + AIVehicle.GetUnitNumber(vehicle))
						for (local i = 0; i < AIVehicle.GetNumWagons(vehicle)-1; i++) {
							if (AIVehicle.GetWagonEngineType(vehicle, i) == AIVehicle.GetWagonEngineType(vehicle, i+1)) {
								AIVehicle.SellWagon(vehicle, i+1) //the second wagon is usually loaded less
							}
						}
					}
				} else {
					//train is too short, add carriages
					LogWarning("Adding carriages to train " + AIVehicle.GetUnitNumber(vehicle))
					IncreaseTrainLength(vehicle)
				}
				AIVehicle.StartStopVehicle(vehicle)
			} else {
				//default: I don't know why this vehicle is in the depot. Selling it will return money.
				LogError("Selling vehicle with number " + AIVehicle.GetUnitNumber(vehicle) + ", but I don't know why I did this")
				AIVehicle.SellVehicle(vehicle)
			}
			Log("Vehicle status: " + AIVehicle.GetState(vehicle))
			if (AIVehicle.IsValidVehicle(vehicle) && AIVehicle.IsStoppedInDepot(vehicle)) {
				AIVehicle.StartStopVehicle(vehicle)
				Log("Vehicle status: " + AIVehicle.GetState(vehicle))
			}
			Log("Vehicle status: " + AIVehicle.GetState(vehicle))
		} else if (this.vehicleLocations.GetValue(vehicle) == location) {
			LogWarning("Vehicle " + vehicle + " hasn't moved in a long time.")
			//vehicle hasn't moved since last time we checked
			if (vehicleType == AIVehicle.VT_WATER) {
				//give this ship space to move around
				foreach (delta in [dXY(1,-1), dXY(1,0), dXY(1,1), dXY(-1,-1), dXY(-1,0), dXY(-1,1), dXY(0,1), dXY(0,-1)]) {
					local tile = location + delta
					if (!AITile.IsWaterTile(tile)) AIMarine.BuildCanal(tile)
				}
			} else if (vehicleType == AIVehicle.VT_RAIL && AIVehicle.GetCurrentSpeed(vehicle) < 5) {
				if (AIVehicle.GetProfitThisYear(vehicle) < -100 && AIVehicle.GetProfitLastYear(vehicle) < -50) {
					//Probably, multiple trains are in a deadlock. Reverse some, send some to a depot
					if (AIVehicle.GetState(vehicle) == AIVehicle.VS_RUNNING) {
						if (AIBase.RandRange(100) > 70)
							AIVehicle.ReverseVehicle(vehicle)
						if (AIBase.RandRange(100) > 70) {
							AIVehicle.SendVehicleToDepot(vehicle)
						}
					}
				}
			} else if (vehicleType == AIVehicle.VT_ROAD && AIVehicle.GetCurrentSpeed(vehicle) < 5) {
				if (AIRoad.IsRoadStationTile(location)) {
					local station = AIStation.GetStationID(location)
					local cargoType = GetVehicleCargoType(vehicle)
					local loadPercentage = AIVehicle.GetCargoLoad(vehicle, cargoType) * 100 / AIVehicle.GetCapacity(vehicle, cargoType)
					local groupList = AIVehicleList_Group(AIVehicle.GetGroupID(vehicle))
					local gotoNextStation = 0
					//if there are more road vehicles on this station tile, send this one to the next station if it has the most passengers/highest cargo load
					for (local otherVehicle = groupList.Begin(); groupList.HasNext(); otherVehicle = groupList.Next()) {
						if (gotoNextStation >= 0 && otherVehicle != vehicle && AIVehicle.GetLocation(otherVehicle) == location) {
							if (AIVehicle.GetCargoLoad(otherVehicle, cargoType) * 100 / AIVehicle.GetCapacity(otherVehicle, cargoType) > loadPercentage) {
								gotoNextStation = -1
							} else {
								gotoNextStation = 1
							}
						}
					}
					if (gotoNextStation == 1 && AIStation.GetCargoWaiting(station, cargoType) < 8) {
						//time to leave this station
						AIOrder.SkipToOrder(vehicle, (AIOrder.ResolveOrderPosition(vehicle, AIOrder.ORDER_CURRENT) + 1) % AIOrder.GetOrderCount(vehicle))
						LogWarning("Vehicle " + AIVehicle.GetName(vehicle) + " is just sent to the next station")
						continue
					}
				}
				
				//give the vehicle space to move (required for trams), and try to turn
				AIRoad.SetCurrentRoadType(AIVehicle.GetRoadType(vehicle))
				foreach (delta in [dXY(1,0), dXY(-1,0), dXY(0,1), dXY(0,-1)]) {
					AIRoad.BuildRoad(location, location + delta)
					foreach (d2 in [dXY(1,0), dXY(-1,0), dXY(0,1), dXY(0,-1)]) {
						if (AIRoad.IsRoadTile(location + delta + d2)) {
							AIRoad.BuildRoad(location + delta, location + delta + d2)
						}
					}
				}
				AIVehicle.ReverseVehicle(vehicle)
			}
		}
		this.vehicleLocations.SetValue(vehicle, location)
	}
	vehicleList = AIVehicleList()
	// Make sure all vehicles are moving
	for (local vehicle = vehicleList.Begin(); vehicleList.HasNext(); vehicle = vehicleList.Next()) {
		if (AIVehicle.IsValidVehicle(vehicle) && AIVehicle.IsStoppedInDepot(vehicle)) {
			LogError("Vehicle " + vehicle + " was still stopped in the depot" + AIVehicle.GetState(vehicle))
			AIVehicle.StartStopVehicle(vehicle)
		}
	}
}

function GetVehicleCargoType(vehicle) {
	local list = AICargoList();
	local cargoCapacity = 0
	local cargoType = 0
	for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
		local capacity = AIVehicle.GetCapacity(vehicle, cargo)
		if (capacity > cargoCapacity) {
			cargoType = cargo
			cargoCapacity = capacity
		}
	}
	return cargoType
}

function GetMaxVehicleSpeed(vehicle) {
	local maxSpeed = 100000;
	for (local i = 0; i < AIVehicle.GetNumWagons(vehicle); i++) {
		local max = AIEngine.GetMaxSpeed(AIVehicle.GetWagonEngineType(vehicle, i))
		if (max > 0 && max < maxSpeed)
			maxSpeed = max
	}
	return maxSpeed
}

function BuyVehicleLike(vehicle) {
	local depot = AIVehicle.GetLocation(vehicle) //probably the vehicle is at a depot
	if (!AIRoad.IsRoadDepotTile(depot)) depot = null //no it wasn't
	//We would prefer a depot that is in the order list of the vehicle
	for (local i = 0; i < AIOrder.GetOrderCount(vehicle); i++) {
		if (AIOrder.IsGotoDepotOrder(vehicle, i))
			depot = AIOrder.GetOrderDestination(vehicle, i)
	}
	if (depot == null) return false //no depot found
	if (AIVehicle.GetVehicleType(vehicle) == AIVehicle.VT_ROAD) {
		local roadSystem = GetRoadSystem(vehicle)
		local plan = RoadVehicleBuyPlan(roadSystem, {location = depot})
		//execute the plan of buying a road vehicle
		plan.Build(false)
	} else if (AIVehicle.GetVehicleType(vehicle) == AIVehicle.VT_WATER) {
		local shipSystem = GetShipSystem(vehicle)
		local plan = ShipBuyPlan(shipSystem, {location = depot})
		plan.Build(false)
	}
}