// Perform service on the network


//Each service has a group of vehicles
function RailwAI::CheckServices() {
	local groupList = AIGroupList()
	local everythingIsGood = true
	local numOfGroups = groupList.Count()
	for (local group = groupList.Begin(); groupList.HasNext(); group = groupList.Next()) {
		local vehicleType = AIGroup.GetVehicleType(group)
		local hasEnoughVehicles = true
		local list = AIVehicleList_Group(group)
		local firstVehicle = null
		local secondVehicle = null
		local numVehicles = list.Count()
		local runningVehicles = 0
		local stoppedVehicles = 0
		local profitableVehicles = 0
		local totalProfit = 0
		for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
			if (firstVehicle == null)
				firstVehicle = vehicle
			else if (secondVehicle == null) {
				if (AIBase.RandRange(10) < 5) {
					secondVehicle = firstVehicle
					firstVehicle = vehicle
				} else {
					secondVehicle = vehicle
				}
			}
			//Count running, profitable vehicles
			if (AIVehicle.GetCurrentSpeed(vehicle) > 20) {
				runningVehicles += 1		
			} else {
				stoppedVehicles += 1
			}
			local profit = AIVehicle.GetProfitThisYear(vehicle)
			if (profit > 1000) {
				profitableVehicles += 1		
			}
			totalProfit += profit
		}
		if (numVehicles == 0) {
			LogError("I found an empty group. Deleting this group that has name " + AIGroup.GetName(group) + ".")
			AIGroup.DeleteGroup(group)
			return false
		}
		if (numVehicles == 1) {
			Log("The group with name " + AIGroup.GetName(group) + " has only one vehicle. I should add one.")
			hasEnoughVehicles = false
		}
		
		local depot = null
		local station1 = null
		local cargoType = GetVehicleCargoType(firstVehicle)
		local cargoCapacity = AIVehicle.GetCapacity(firstVehicle, cargoType)
		local totalWaitingCargo = 0
		local totalTransportCapacity = 0
		for (local i = 0; i < AIOrder.GetOrderCount(firstVehicle); i++) {
			if (AIOrder.IsGotoDepotOrder(firstVehicle, i))
				depot = AIOrder.GetOrderDestination(firstVehicle, i)
			if (AIOrder.IsGotoStationOrder(firstVehicle, i)) {
				local location = AIOrder.GetOrderDestination(firstVehicle, i)
				if (station1 == null)
					station1 = location
			}
		}
		list = AIVehicleList_Group(group)
		for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
			totalTransportCapacity += AIVehicle.GetCapacity(vehicle, cargoType)
		}
		list = AIStationList_Vehicle(firstVehicle)
		for (local station = list.Begin(); list.HasNext(); station = list.Next()) {
			totalWaitingCargo += AIStation.GetCargoWaiting(station, cargoType)
		}
		
		if (totalWaitingCargo > totalTransportCapacity)
			hasEnoughVehicles = false
		
		if (station1 != null && !IsStationProperlyServed(station1, cargoType)) {
			hasEnoughVehicles = false
		}
		
		if (list.Count() > numVehicles)
			hasEnoughVehicles = false
			
		//Now check if we have a condition, in which we are not allowed to buy more vehicles for this connection
		//if (list.Count()*4-2 <= numVehicles)
		//	hasEnoughVehicles = true
		if (runningVehicles <= numVehicles/2)
			hasEnoughVehicles = true
		if (stoppedVehicles > 1)
			hasEnoughVehicles = true
		if (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS)) {
			if (numVehicles >= 2 && profitableVehicles < 2)
				hasEnoughVehicles = true				
			if (numVehicles >= 2 && numVehicles >= numOfGroups*2-2)
				hasEnoughVehicles = true
		} else {
			if (numVehicles >= 2 && totalProfit / numVehicles < 500)
				hasEnoughVehicles = true
			if (numVehicles >= 2 && numVehicles >= numOfGroups*4-2)
				hasEnoughVehicles = true
		}
		
			
		list = AIVehicleList_Group(group)
		for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
			//If we already bought a vehicle this month, don't buy a new one
			if (AIVehicle.GetAge(vehicle) < 30) {
				hasEnoughVehicles = true
			}
		}
		//If the service is not served well, or only has one vehicle, buy a new vehicle
		if (numVehicles == 1 && AIVehicle.GetVehicleType(firstVehicle) == AIVehicle.VT_RAIL) {
			//for trains, we want two different looking trains
			hasEnoughVehicles = BuyTrainLikeThis(firstVehicle)
		}
		if (!hasEnoughVehicles) {
			local requestedNewVehicles = 1 + totalWaitingCargo * numVehicles / totalTransportCapacity / 2
			if (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS))
				requestedNewVehicles = 1
			for (local i = 0; i < requestedNewVehicles; i++) {
				local vehicle = AIVehicle.CloneVehicle(depot, firstVehicle, true)
				if (AIVehicle.IsValidVehicle(vehicle)) {
					AIVehicle.StartStopVehicle(vehicle);
					Log("I have cloned a vehicle in group " + AIGroup.GetName(group));
				} else if (AIError.GetLastError() == AIVehicle.ERR_VEHICLE_NOT_AVAILABLE) {
					if ( AIVehicle.GetVehicleType(firstVehicle) == AIVehicle.VT_RAIL) {
						//We can not clone this train anymore, just buy a new train
						BuyTrainLikeThis(firstVehicle);
					} else {
						BuyVehicleLike(firstVehicle)
					}
				}
			}
		}
		if (everythingIsGood) {
			everythingIsGood = hasEnoughVehicles
		}
		//For trains older than 1 year, check if the train length should be adjusted
		if (vehicleType == AIVehicle.VT_RAIL) {
			local totalLoad = 0
			local totalCapacity = 0
			local maxCapacity = 0
			list = AIVehicleList_Group(group)
			for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
				local capacity = AIVehicle.GetCapacity(vehicle, cargoType)
				totalCapacity += capacity
				if (capacity > maxCapacity) maxCapacity = capacity
				totalLoad += AIVehicle.GetCargoLoad(vehicle, cargoType)
			}
			local usage = 0
			if (totalCapacity > 0) usage = totalLoad * 100 / totalCapacity
			if (usage < 20 && (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS) || AICargo.HasCargoClass(cargoType, AICargo.CC_MAIL))) {
				//usage of trains is too low, send some to depots
				list = AIVehicleList_Group(group)
				local capacityThreshold = maxCapacity * 3 / 5
				for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
					local capacity = AIVehicle.GetCapacity(vehicle, cargoType)
					if (AIVehicle.GetAge(vehicle) > 180 && AIVehicle.GetNumWagons(vehicle) > 2 && capacity > capacityThreshold &&
						AIVehicle.GetCargoLoad(vehicle, cargoType) * 100 / capacity <= usage+1) {
						AIVehicle.SendVehicleToDepot(vehicle)
					}
				}
			} else if (usage > 75) {
				//usage of trains is too high, send some full trains that are not maximum length yet to depots
				local maxTrainLength = 16 * GetPlatformLengthOfTrain(firstVehicle)
				list = AIVehicleList_Group(group)
				for (local vehicle = list.Begin(); list.HasNext(); vehicle = list.Next()) {
					if (AIVehicle.GetAge(vehicle) > 360 && AIVehicle.GetCargoLoad(vehicle, cargoType) * 100 / AIVehicle.GetCapacity(vehicle, cargoType) > 90) {
						local trainLength = AIVehicle.GetLength(vehicle)
						if (trainLength < maxTrainLength / 2 || (trainLength < maxTrainLength*3/4 && AIVehicle.GetNumWagons(vehicle) > 2)) {
							AIVehicle.SendVehicleToDepot(vehicle)
						}
					}
				}
			}
		}
	}
	return true //please lie that we're doing great
	//return everythingIsGood
}

function RailwAI::RemoveUnservedStations() {
	local list = AIStationList(AIStation.STATION_ANY)
	for (local station = list.Begin(); list.HasNext(); station = list.Next()) {
		if (AIVehicleList_Station(station).Count() != 0) continue
		local location = AIStation.GetLocation(station)
		for (local x = -1; x <= station1.sizeX; x++) {
			for (local y = -1; y <= station1.sizeY; y++) {
				local tile = location + dXY(x,y)
				if (AIStation.GetStationID(tile) == station) {
					AIRoad.RemoveRoadStation(tile)
					AIRail.RemoveRailStationTileRectangle(tile, tile, true)
					AIMarine.RemoveDock(tile)
				}
			}
		}		
	}
}

function RailwAI::RemoveDisconnectedRails() {
	foreach (tile in this.myTiles) {
		if (AIRail.IsRailTile(tile)) {
			//TODO
		
		}
	
	}
	
}

/* Search the map for railway bridges that were accidentally built disconnected.
 * For these bridges, try to reconnect them
 */
 
function RailwAI::ConnectRailBridges() {
	foreach (tile in this.myTiles) {
		if (IsRailBridge(tile)) {
			AIRail.SetCurrentRailType(AIRail.GetRailType(tile))
			local otherTile = AIBridge.GetOtherBridgeEnd(tile)
			local delta = (tile-otherTile)/AIMap.DistanceManhattan(tile, otherTile)
			local front = tile + delta
			if (!AIRail.IsRailTile(front) && !IsRailBridge(front)) {
				LogTile("connecting tile in front of Rail bridge", front)
				ConnectRailTileTowards(front, tile)
				AIRail.BuildSignal(front, tile, AIRail.SIGNALTYPE_PBS)
				local nextTiles = GetNextRailTiles(front, tile)
				if (nextTiles) {
					MigrateToPathSignalsAround(front, 4)
					foreach (next in nextTiles) {
						RemoveAllSignals(next)
						ConnectRailTile(next)
					}
				}
			} else {
				local nextTiles = GetNextRailTiles(front, tile)
				if (nextTiles) {
					foreach (next in nextTiles) {
						if (!GetNextRailTiles(next,front)) {
							RemoveAllSignals(next)
							ConnectRailTileTowards(next, front)
						}
					}
				} else {
					MigrateToPathSignalsAround(front, 4)
					LogTile("reconnecting tile in front of Rail bridge", front)
					AIRail.SetCurrentRailType(AIRail.GetRailType(front))
					ConnectRailTileTowards(front, tile)
				}
			}
			
		}
	}
}

/* Search the map for level crossings. We don't like accidents, so we try to avoid them.
 * For adjacent level crossings, we try to build a bridge around
 * For single track level crossings, we build path signals before and after the crossing
 */
function RailwAI::HandleLevelCrossings() {
	local Xmax = AIMap.GetMapSizeX()
	local Ymax = AIMap.GetMapSizeY()
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	LogWarning("Finding all level crossings on a map with size " + Xmax + ", " + Ymax)
	local crossings = []
	for (local x = 1; x < Xmax; x++) {
		for (local y = 1; y < Ymax; y++) {
			local tile = AIMap.GetTileIndex(x,y)
			if (AIRail.IsLevelCrossingTile(tile))
				crossings.append(tile)
		}
	}
	Log(crossings.len() + " level crossings found")
	foreach (tile in crossings) {
		local x = AIMap.GetTileX(tile)
		local y = AIMap.GetTileY(tile)
		//owner contains the owner of the rail
		local railOwner = AITile.GetOwner(tile)
		if (railOwner == myCompany) {
			//LogTile("My crossing at", tile)
			// Use path signals around the railway crossing
			MigrateToPathSignalsAround(tile, 5)
		}
		foreach (delta in [dXY(0,1), dXY(1,0)]) {
			if (AIRail.IsLevelCrossingTile(tile + delta) || AIRoad.HasRoadType(tile, AIRoad.ROADTYPE_TRAM)) {
				//I should build a bridge around it if the road of this crossing is mine
				if (AITile.GetOwner(tile-delta) == myCompany || AITile.GetOwner(tile+delta+delta)) {
					LogTile("Level crossing should be avoided at", tile)
					local idea = RoadDetourIdea(tile, tile+delta)
					if (idea.plans.len() > 0) {
						idea.Execute()
					}
					idea = null
					
				}
			}
		}
	}
}


/* Search the map for locks within canals. We don't like to accidentally block another canal.
 * if we did, we will build a detour canal
 */
function RailwAI::HandleLocks() {
	local Xmax = AIMap.GetMapSizeX()
	local Ymax = AIMap.GetMapSizeY()
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	LogWarning("Finding all locks on a map with size " + Xmax + ", " + Ymax)
	local locks = []
	for (local x = 1; x < Xmax; x++) {
		for (local y = 1; y < Ymax; y++) {
			local tile = AIMap.GetTileIndex(x,y)
			if (AIMarine.IsLockTile(tile) && AITile.GetSlope(tile) != AITile.SLOPE_FLAT)
				locks.append(tile)
		}
	}
	Log(locks.len() + " locks found")
	foreach (tile in locks) {
		local x = AIMap.GetTileX(tile)
		local y = AIMap.GetTileY(tile)
		//owner contains the owner of the rail
		local lockOwner = AITile.GetOwner(tile)
		if (lockOwner == myCompany || AITile.GetOwner(tile + dXY(1,1)) == myCompany || AITile.GetOwner(tile + dXY(-1,-1)) == myCompany) {
			//We are owner of something around this lock. Let's check if the world looks nice here.
			local directions = [Direction.SW, Direction.NE]
			if (AITile.GetSlope(tile) == AITile.SLOPE_NW || AITile.GetSlope(tile) == AITile.SLOPE_SE) {
				directions = [Direction.SE, Direction.NW]
			}
			foreach (direction in directions) {
				local coords = VC.VectorCoordinates(tile, direction)
				local checkTile = VC.GetTile(coords, [1, 2])
				local refTile = VC.GetTile(coords, [1, 1])
				if (AITile.IsWaterTile(refTile) && !AITile.IsWaterTile(checkTile)) {
						if (AITile.GetMaxHeight(checkTile) > AITile.GetMaxHeight(refTile)) {
							AITile.LowerTile(checkTile, AITile.GetSlope(checkTile))
						} else {
							AITile.RaiseTile(checkTile, AITile.GetComplementSlope(checkTile))
						}
					AIMarine.BuildCanal(checkTile)
				}
				
				checkTile = VC.GetTile(coords, [-1, 2])
				refTile = VC.GetTile(coords, [-1, 1])
				if (AITile.IsWaterTile(refTile) && !AITile.IsWaterTile(checkTile)) {
					if (!AITile.GetSlope(checkTile) != AITile.SLOPE_FLAT) {
						if (AITile.GetMaxHeight(checkTile) > AITile.GetMaxHeight(refTile)) {
							AITile.LowerTile(checkTile, AITile.GetSlope(checkTile))
						} else {
							AITile.RaiseTile(checkTile, AITile.GetComplementSlope(checkTile))
						}
					}
					AIMarine.BuildCanal(checkTile)
				}
			}
		}
	}
}

function RailwAI::BuildHQ() {
	local myCompany = AICompany.ResolveCompanyID(AICompany.COMPANY_SELF)
	if (AICompany.GetCompanyHQ(myCompany) != AIMap.TILE_INVALID) return true
	local deltas = [dXY(AIBase.RandRange(14)+3, AIBase.RandRange(14)+3), dXY(-AIBase.RandRange(14)-3, AIBase.RandRange(14)+3),
		dXY(-AIBase.RandRange(14)-3, -AIBase.RandRange(14)-3), dXY(AIBase.RandRange(14)+3, -AIBase.RandRange(14)-3)] 
	local list = AIStationList(AIStation.STATION_ANY)
	for (local station = list.Begin(); list.HasNext(); station = list.Next()) {
		local tile = AIStation.GetLocation(station)
		foreach (delta in deltas) {
			if (AICompany.BuildCompanyHQ(tile + delta)) {
				LogTile("Company headquarters has been built near", tile, "hq")
				LogTile("Company headquarters has been built at", tile + delta, "hq")
				return true
			}
		}
	}
}