//An Idea to build a train service
class TrainServiceIdea extends Idea {
	trainSystem = null;
	townA = null
	townB = null
	depot = null
	constructor(trainSystem, townA, townB, data = null) {
		if (data) {
			this.trainSystem = data.misc[0]
			this.townA = data.misc[1]
			this.townB = data.misc[2]
			this.depot = data.misc[3]
			this.results = data.results
			this.plans = []
			this.plans.append(TrainStationPlan(data.plans[0], null, null))
			this.plans.append(TrainStationPlan(data.plans[1], null, null))
			this.plans.append(RailTrackPlan(null, null, null, data.plans[2]))
			this.plans.append(TrainBuyPlan(null, this.plans[0], data.plans[3]))
		} else {
			this.plans = []
			this.results = []
			LogWarning("Creating a Train Service Idea: " + AITown.GetName(townA) + " to " + AITown.GetName(townB))
			this.trainSystem = trainSystem
			this.townA = townA
			this.townB = townB
			AIRail.SetCurrentRailType(trainSystem[0])
			local plan
			plan = TrainStationPlan(null, AITown.GetLocation(townA), trainSystem, true)
			this.plans.append(plan)
			plan = TrainStationPlan(null, AITown.GetLocation(townB), trainSystem, false, plan.location)
			this.plans.append(plan)
			if (this.plans[0].exit1 && this.plans[1].exit2) {
				plan = RailTrackPlan(trainSystem, this.plans[0], this.plans[1])
				plans.append(plan)
				if (plan.path1) {
					plan = TrainBuyPlan(trainSystem, this.plans[0])
					this.plans.append(plan)
				} else {
					this.plans = []
					LogWarning("Could not plan to lay tracks")
				}
			} else {
				LogWarning("Could not plan both train stations")
				this.plans = []
			}
		}
	}
	
	function getType() {
		return "TrainServiceIdea"
	}
	
	function getMiscData() {
		return [trainSystem, townA, townB, depot]
	}
	
	function Execute(fromSave = false) {
		AIRail.SetCurrentRailType(trainSystem[0])
		if (!Perform(fromSave)) {
			LogError("Idea could not be executed")
			return false
		}
		local breakdownsEnabled = (AIGameSettings.GetValue("difficulty.vehicle_breakdowns") >= 1)
		local train = this.results[3]
		//First order is go to depot where it was built. Is used for cloning the train later
		if (breakdownsEnabled) {
			AIOrder.AppendOrder(train, this.results[0]["depot"], AIOrder.OF_SERVICE_IF_NEEDED)
		} else {
			AIOrder.AppendOrder(train, this.results[0]["depot"], AIOrder.OF_NONE)
		}
		//Go to the first train station
		AIOrder.AppendOrder(train, this.results[0]["location"], AIOrder.OF_NONE);
		//Don't go to the depot
		AIOrder.InsertConditionalOrder(train, 0, 1)
		AIOrder.SetOrderCondition(train, 0, AIOrder.OC_RELIABILITY);
		AIOrder.SetOrderCompareFunction(train, 0, AIOrder.CF_MORE_THAN);
		if (breakdownsEnabled) {
			AIOrder.SetOrderCompareValue(train, 0, 70);
		} else {
			AIOrder.SetOrderCompareValue(train, 0, 0);
		}
		// Do not depart if the train is less than 2 percent full,
		// then the train is probably following an other one too closely
		AIOrder.AppendConditionalOrder(train, 2);
		AIOrder.SetOrderCondition(train, 3, AIOrder.OC_LOAD_PERCENTAGE);
		AIOrder.SetOrderCompareFunction(train, 3, AIOrder.CF_LESS_THAN)
		AIOrder.SetOrderCompareValue(train, 3, 2);
		// Go to the second train station
		AIOrder.AppendOrder(train, this.results[1]["location"], AIOrder.OF_NONE);
		
		local group = AIGroup.CreateGroup(AIVehicle.GetVehicleType(train))
		AIGroup.MoveVehicle(group, train)
		
		AIVehicle.StartStopVehicle(train);
		return {trainSystem = trainSystem, stops = [this.results[0], this.results[1]], trains = [this.results[3]]}
		//return [trainSystem, [this.results[1], this.results[2]], [this.results[3]]]
	}

}


//An Idea to extend a train service
class TrainServiceExtendIdea extends Idea {
	trainSystem = null
	currentEnd = null
	train = null
	constructor(train, data = null) {
		if (data) {
			this.trainSystem = data.misc[0]
			this.currentEnd = data.misc[1]
			this.train = data.misc[2]
			this.results = data.results
			this.plans = []
			this.plans.append(TrainStationPlan(data.plans[0], null, null))
			this.plans.append(RailTrackPlan(null, null, null, data.plans[1]))
		} else {
			this.plans = []
			this.results = []
			this.train = train
			this.trainSystem = GetTrainSystem(train)
			AIRail.SetCurrentRailType(trainSystem[0])
			
			local stops = []
			for (local i = 0; i < AIOrder.GetOrderCount(train); i++) {
				if (AIOrder.IsGotoStationOrder(train, i)) {
					local dest = AIOrder.GetOrderDestination(train, i)
					local check = true
					for (local j = 0; j < stops.len(); j++) {
						if (stops[j] == dest)
							check = false
					}
					if (check)
						stops.append(dest)
				}
			}
			
			local startLocation = stops[0]
			local currentEndLocation = stops[stops.len()-1]
			if (IsTerminusTile(currentEndLocation)) return //then we cannot extend this route
			local trainsInGroup = AIVehicleList_Group(AIVehicle.GetGroupID(train)).Count()
			if (trainsInGroup < stops.len()) return //too few vehicles
			currentEnd = GetDataOfStationAt(currentEndLocation)
			
			local allTowns = AITownList();
			allTowns.Valuate(AITown.GetDistanceManhattanToTile, currentEndLocation); allTowns.KeepAboveValue(30);
			allTowns.Valuate(AITown.GetDistanceManhattanToTile, currentEndLocation); allTowns.KeepBelowValue(70);
			allTowns.Valuate(AITown.IsTownFurtherFromAthanFromB, startLocation, currentEndLocation); allTowns.KeepAboveValue(0);

			if (allTowns.Count() < 1) {
				return
				LogWarning("Couldn't find any town to extend the transervice to")
			}
			allTowns.RemoveTop(AIBase.RandRange(allTowns.Count()))
			local town = allTowns.Begin()
			Log("Trying to extend a train service towards " + AITown.GetName(town))
			
			local plan = TrainStationPlan(null, AITown.GetLocation(town), trainSystem, (AIBase.RandRange(10) < 4), currentEndLocation)
			this.plans.append(plan)
		
			if (this.plans[0].exit2) {
				plan = RailTrackPlan(trainSystem, currentEnd, this.plans[0])
				plans.append(plan)
				if (!plan.path1) {
					this.plans = []
					LogWarning("Could not plan to lay tracks")
				}
			} else {
				LogWarning("Could not plan new train station")
				this.plans = []
			}
		}
	}
	
	function getMiscData() {
		return [trainSystem, currentEnd, train]
	}
	
	function getType() {
		return "TrainServiceExtendIdea"
	}
	
	function Execute(fromSave = false) {
		Log("Executing plan")
		AIRail.SetCurrentRailType(trainSystem[0])
	
		if (!Perform(fromSave)) {
			LogError("Idea could not be executed")
			return false
		}
		local position = null
		for (local i = 0; i < AIOrder.GetOrderCount(train); i++) {
			if (AIOrder.IsGotoStationOrder(train, i) && AIOrder.GetOrderDestination(train, i) == currentEnd["location"]) {
				position = i
			}
		}
		if (!position) {
			LogError("Could not find the previous station in the list of orders of the train!")
			AIOrder.InsertOrder(train, AIOrder.GetOrderCount(train)/2 + 2, this.results[0]["location"], AIOrder.OF_NONE);
			return train
		}
		
		/*if (position < 5 && AIOrder.IsConditionalOrder(train, 3)) {
			//Change waiting until 10% load when the train service is extended for the first time
			AIOrder.SetOrderCompareFunction(train, 3, AIOrder.CF_EQUALS)
			AIOrder.SetOrderCompareValue(train, 3, 1);
		}*/
		
		//Go to the new train station
		AIOrder.InsertOrder(train, position+1, currentEnd["location"], AIOrder.OF_NONE);
		AIOrder.InsertOrder(train, position+1, this.results[0]["location"], AIOrder.OF_NONE);
		
		//An empty train should turn around at the next train station.
		if (position < 5 && AIOrder.IsConditionalOrder(train, 3)) {
			position -= 1
			AIOrder.InsertConditionalOrder(train, position, position+3)
		} else {
			AIOrder.InsertConditionalOrder(train, position, position+2)
		}
		AIOrder.SetOrderCondition(train, position, AIOrder.OC_LOAD_PERCENTAGE)
		AIOrder.SetOrderCompareFunction(train, position, AIOrder.CF_LESS_THAN)
		AIOrder.SetOrderCompareValue(train, position, 1);
		
		return train
	}
}


//An Idea to build a train service
class CargoTrainServiceIdea extends Idea {
	trainSystem = null;
	industryA = null
	industryB = null
	depot = null
	constructor(trainSystem, industryA, industryB, data = null) {
		if (data) {
			this.trainSystem = data.misc[0]
			this.industryA = data.misc[1]
			this.industryB = data.misc[2]
			this.depot = data.misc[3]
			this.results = data.results
			this.plans = []
			this.plans.append(TrainStationPlan(data.plans[0], null, null))
			this.plans.append(TrainStationPlan(data.plans[1], null, null))
			this.plans.append(RailTrackPlan(null, null, null, data.plans[2]))
			this.plans.append(TrainBuyPlan(null, this.plans[0], data.plans[3]))
		} else {
			this.plans = []
			this.results = []
			LogWarning("Creating a " + AICargo.GetCargoLabel(trainSystem[1]) + " Train Service Idea: " + AIIndustry.GetName(industryA) + " to " + AIIndustry.GetName(industryB))
			this.trainSystem = trainSystem
			this.industryA = industryA
			this.industryB = industryB
			AIRail.SetCurrentRailType(trainSystem[0])
			local plan
			plan = TrainStationPlan(null, AIIndustry.GetLocation(industryA), trainSystem, true, null, true)
			this.plans.append(plan)
			plan = TrainStationPlan(null, AIIndustry.GetLocation(industryB), trainSystem, true, plan.location)
			this.plans.append(plan)
			if (this.plans[0].exit1 && this.plans[1].exit2) {
				plan = RailTrackPlan(trainSystem, this.plans[0], this.plans[1])
				plans.append(plan)
				if (plan.path1) {
					plan = TrainBuyPlan(trainSystem, this.plans[0])
					this.plans.append(plan)
				} else {
					this.plans = []
					LogWarning("Could not plan to lay tracks")
				}
			} else {
				LogWarning("Could not plan both train stations")
				this.plans = []
			}
		}
	}
	
	function getType() {
		return "CargoTrainServiceIdea"
	}
	
	function getMiscData() {
		return [trainSystem, industryA, industryB, depot]
	}
	
	function Execute(fromSave = false) {
		AIRail.SetCurrentRailType(trainSystem[0])
		if (!Perform(fromSave)) {
			LogError("Idea could not be executed")
			return false
		}
		local train = this.results[3]
		local breakdownsEnabled = (AIGameSettings.GetValue("difficulty.vehicle_breakdowns") >= 1)
		//First order is go to depot where it was built. Is used for cloning the train later
		if (breakdownsEnabled) {
			AIOrder.AppendOrder(train, this.results[0]["depot"], AIOrder.OF_SERVICE_IF_NEEDED)
		} else {
			AIOrder.AppendOrder(train, this.results[0]["depot"], AIOrder.OF_NONE)
		}
		//Go to the first train station
		AIOrder.AppendOrder(train, this.results[0]["location"], AIOrder.OF_NONE);
		AIOrder.SetOrderFlags(train, 1, AIOrder.OF_FULL_LOAD_ANY)
		//Don't go to the depot
		AIOrder.InsertConditionalOrder(train, 0, 1)
		AIOrder.SetOrderCondition(train, 0, AIOrder.OC_RELIABILITY);
		AIOrder.SetOrderCompareFunction(train, 0, AIOrder.CF_MORE_THAN);
		if (breakdownsEnabled) {
			AIOrder.SetOrderCompareValue(train, 0, 70);
		} else {
			AIOrder.SetOrderCompareValue(train, 0, 0);
		}		
		
		// Go to the second train station
		AIOrder.AppendOrder(train, this.results[1]["location"], AIOrder.OF_NONE);
		
		local group = AIGroup.CreateGroup(AIVehicle.GetVehicleType(train))
		AIGroup.MoveVehicle(group, train)
		
		AIVehicle.StartStopVehicle(train);
		return {trainSystem = trainSystem, stops = [this.results[0], this.results[1]], trains = [this.results[3]]}
	}
}
