//Get the platform length of stations on the route of this train
function GetPlatformLengthOfTrain(vehicle) {
	for (local i = 0; i < AIOrder.GetOrderCount(vehicle); i++) {
		if (AIOrder.IsGotoStationOrder(vehicle, i)) {
			local station = AIOrder.GetOrderDestination(vehicle, i)
			local j = 1;
			if (AIRail.GetRailStationDirection(station) == AIRail.RAILTRACK_NE_SW) {
				while (AIRail.IsRailStationTile(station + dXY(j,0))) {
					j++
				}
			} else {
				while (AIRail.IsRailStationTile(station + dXY(0,j))) {
					j++
				}
			}
			return j
		}
	}
	return 0
}

/* Buy a new train, similar to another train and share the orders
 * train = (VehicleID) train to share orders with
 */
function BuyTrainLikeThis(train) {
	local depot = null
	local station = null
	local cargoType = null
	local cargoCapacity = 0
	for (local i = 0; i < AIOrder.GetOrderCount(train); i++) {
		if (AIOrder.IsGotoDepotOrder(train, i))
			depot = AIOrder.GetOrderDestination(train, i)
		if (station == null && AIOrder.IsGotoStationOrder(train, i))
			station = AIOrder.GetOrderDestination(train, i)
	}
	if (!station || !depot) {
		LogError("Tried to buy a train like one that doesn't go to a depot or train station")
		return false
	}
	local list = AICargoList();
	for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
		local capacity = AIVehicle.GetCapacity(train, cargo)
		if (capacity > cargoCapacity) {
			cargoType = cargo
			cargoCapacity = capacity
		}
	}
	local requestLength = GetPlatformLength(station)
	local doubleTrainLength = AIVehicle.GetLength(train) / 8
	if (doubleTrainLength < requestLength)
		requestLength = doubleTrainLength
	local newTrain = BuyTrain(depot, cargoType, requestLength)
	if (!newTrain)
		return false

	AIOrder.ShareOrders(newTrain, train)
	AIGroup.MoveVehicle(AIVehicle.GetGroupID(train), newTrain) //copy train to the same service group
	AIVehicle.StartStopVehicle(newTrain)
	return newTrain
}

/* Buy a Train in an existing depot (depot not in Test Mode!)
 * depot = (TileIndex) location of the depot
 * cargoType = type of cargo
 * platformLength = maximum length of the train
 */
function BuyTrain(depot, cargoType, platformLength) {
	local trainLength = platformLength*16;
	local trains = SelectTrains(cargoType, AIRail.GetRailType(depot))
	local wgns = trains[0]
	local locs = trains[1]
	local sets = trains[2]
	local trainsetChance = sets.Count();
	local pushpullChance = 2*min(wgns.Count(), locs.Count())
	local fortuna = AIBase.RandRange(trainsetChance + pushpullChance)
	local engineType
	local wagonType
	local maxParts = 40;
	if (fortuna < trainsetChance) {
		//We decided to build a trainset, pick a random one!
		sets.Valuate(AIBase.RandRangeItem, trainsetChance*10)
		sets.Sort(AIList.SORT_BY_VALUE, true);
		engineType = sets.Begin()
		wagonType = sets.Begin()
		maxParts = 3; //max 3 trainsets coupled by default
	} else {
		//We decided to build a train with a locomotive and carriages, pick random types
		wgns.Valuate(AIBase.RandRangeItem, wgns.Count()*10)
		wgns.Sort(AIList.SORT_BY_VALUE, true);
		locs.Valuate(AIBase.RandRangeItem, locs.Count()*10)
		locs.Sort(AIList.SORT_BY_VALUE, true);
		engineType = locs.Begin()
		wagonType  = wgns.Begin()
		maxParts = 12;
	}
	if (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS) || AICargo.HasCargoClass(cargoType, AICargo.CC_MAIL)) maxParts *= 2
	//Now, let's buy the train
	local train = AIVehicle.BuildVehicle(depot, engineType)
	if (!AIVehicle.IsValidVehicle(train)) {
		LogWarning("I cannot buy a train")
		LogWarning(AIError.GetLastErrorString())
		return null;
	}
	local engineLength = AIVehicle.GetLength(train)
	local wagonLength = engineLength
	local numEngines = 1
	local trainPart = null
	if (engineLength*2 <= trainLength) {
		trainPart = AIVehicle.BuildVehicle(depot, wagonType);
		if (!AIVehicle.IsValidVehicle(trainPart)) {
			LogWarning("Cannot buy a train carriage")
			AIVehicle.SellVehicle(train);
			return null;
		}
		AIVehicle.MoveWagon(trainPart, 0, train, 0);
		wagonLength = AIVehicle.GetLength(train) - engineLength;
	} else if (trainLength < engineLength) {
		//Train was already longer than the maximum length. We shouldn't do this
		AIVehicle.SellVehicle(train);
		LogWarning("Train that I bought is too long for the platforms. I have sold it instead.")
		return null;
	}
	
	local locoWeight = AIEngine.GetWeight(engineType)
	local wagonWeight = AIEngine.GetWeight(wagonType)//
	LogWarning("Wagon weight: " + wagonWeight)
	if (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS)) {
		wagonWeight += AIEngine.GetCapacity(wagonType) / 16
		LogWarning("Wagon weight with passengers: " + wagonWeight)
	} else if (AICargo.HasCargoClass(cargoType, AICargo.CC_MAIL)) {
		wagonWeight += AIEngine.GetCapacity(wagonType) / 4
		LogWarning("Wagon weight with mail: " + wagonWeight)
	} else {
		wagonWeight += AIEngine.GetCapacity(wagonType)
		LogWarning("Wagon weight with freight: " + wagonWeight)
	}
	//local power = AIEngine.GetPower(engineType)
	//local force = AIEngine.GetMaxTractiveEffort(engineType)
	local speed = min(AIEngine.GetMaxSpeed(engineType), AIEngine.GetMaxSpeed(wagonType))
	local wagonCount = 1
	local engineCount = 1
	local finished = false
	if (speed == 0)
		speed = AIEngine.GetMaxSpeed(engineType)
	//while (AIVehicle.GetLength(train) + wagonLength <= trainLength) {
	while (!finished && AIVehicle.GetLength(train) + wagonLength < trainLength && wagonCount+engineCount < maxParts) { //Trains must be able to move within a train station
		local position = engineCount -1
		local treinWeight = engineCount*locoWeight + wagonWeight*wagonCount
		if (treinWeight*speed >= engineCount*32*AIEngine.GetPower(engineType) && AIVehicle.GetLength(train) + engineLength < trainLength) {
			trainPart = AIVehicle.BuildVehicle(depot, engineType)
			engineCount++
		} else {
			trainPart = AIVehicle.BuildVehicle(depot, wagonType)
			wagonCount++
		}
		if (!AIVehicle.IsValidVehicle(trainPart)) {
			if (AIVehicle.GetLength(train)*2 > trainLength) {
				LogWarning("The train that I bought is shorter than requested")
				finished = true //we will accept that this train is not the maximum length
			} else {
				//We probably didn't have enough money to buy a proper train. Sell this one+
				LogWarning("Cannot buy additional train parts")
				AIVehicle.SellVehicle(train);
				return null;
			}
		}
		AIVehicle.MoveWagon(trainPart, 0, train, position);	
	}
	AIVehicle.RefitVehicle(train, cargoType)
	//Check if the train indeed carries the correct type of cargo
	//This can fail sometimes, for example, when a passenger train has a small mail department and we want to transport mail
	local cargoCapacity = AIVehicle.GetCapacity(train, cargoType)
	local list = AICargoList();
	for (local cargo = list.Begin(); list.HasNext(); cargo = list.Next()) {
		if (AIVehicle.GetCapacity(train, cargo) > cargoCapacity) {
			LogWarning("The train does not transport the right kind of cargo/passengers. Selling it.")
			AIVehicle.SellVehicle(train);
			return null
		}
	}
	//Log("Succesfully bought a train with id " + train)
	return train
}

function DecreaseTrainLength(vehicle) {

}

function IncreaseTrainLength(vehicle) {
	LogWarning("Adding carriages to train " + AIVehicle.GetUnitNumber(vehicle))
	local maxTrainLength = 16 * GetPlatformLengthOfTrain(vehicle)
	local depot = AIVehicle.GetLocation(vehicle)
	local engineType = AIVehicle.GetWagonEngineType(vehicle, 0)
	local wagonType = AIVehicle.GetWagonEngineType(vehicle, AIVehicle.GetNumWagons(vehicle)-1)
	local cargoType = GetVehicleCargoType(vehicle)
	
	local maxParts = 20
	local trainPart = null
	
	local engineLength = 2
	local wagonLength = 2
	local locoWeight = AIEngine.GetWeight(engineType)
	local wagonWeight = AIEngine.GetWeight(wagonType)//
	LogWarning("Wagon weight: " + wagonWeight)
	if (AICargo.HasCargoClass(cargoType, AICargo.CC_PASSENGERS)) {
		wagonWeight += AIEngine.GetCapacity(wagonType) / 16
		LogWarning("Wagon weight with passengers: " + wagonWeight)
	} else if (AICargo.HasCargoClass(cargoType, AICargo.CC_MAIL)) {
		wagonWeight += AIEngine.GetCapacity(wagonType) / 4
		LogWarning("Wagon weight with mail: " + wagonWeight)
	} else {
		wagonWeight += AIEngine.GetCapacity(wagonType)
		LogWarning("Wagon weight with freight: " + wagonWeight)
	}
	local speed = min(AIEngine.GetMaxSpeed(engineType), AIEngine.GetMaxSpeed(wagonType))
	local wagonCount = 0
	local engineCount = 0
	local finished = false
	if (speed == 0)
		speed = AIEngine.GetMaxSpeed(engineType)
	for (local i = 0; i < AIVehicle.GetNumWagons(vehicle); i++) {
		if (AIVehicle.GetWagonEngineType(vehicle, i) == engineType)
			engineCount++
		else
			wagonCount++
	}
	local oldTrainLength = AIVehicle.GetLength(vehicle)
	local targetLength = oldTrainLength + 16 //make train 1 tile longer
	if (targetLength * 2 >= maxTrainLength) {
		targetLength = maxTrainLength //if train is more than 50% of max length, make it the maximum length
	}
	trainPart = AIVehicle.BuildVehicle(depot, wagonType);
	if (!AIVehicle.IsValidVehicle(trainPart)) {
		LogWarning("Cannot buy a train carriage")
		return false
	}
	if (trainPart == vehicle) {
		AIVehicle.MoveWagon(trainPart, 0, vehicle, 0)
		wagonLength = AIVehicle.GetLength(vehicle) - oldTrainLength;
		wagonCount++
		throw("juww")
	} else {
		wagonLength = AIVehicle.GetLength(trainPart)
		if (wagonLength + AIVehicle.GetLength(vehicle) > maxTrainLength) {
			AIVehicle.SellVehicle(trainPart)
			LogWarning("Train was already at maximum length")
			return false
		}
		AIVehicle.MoveWagon(trainPart, 0, vehicle, AIVehicle.GetNumWagons(vehicle)-1)
		wagonCount++
	}
	engineLength = (AIVehicle.GetLength(vehicle)-wagonLength*wagonCount)/engineCount
	while (!finished && AIVehicle.GetLength(vehicle) + wagonLength < maxTrainLength && AIVehicle.GetLength(vehicle) < targetLength && wagonCount+engineCount < maxParts) { //Trains must be able to move within a train station
		local position = engineCount -1
		local treinWeight = engineCount*locoWeight + wagonWeight*wagonCount
		if (treinWeight*speed >= engineCount*32*AIEngine.GetPower(engineType) && AIVehicle.GetLength(vehicle) + engineLength < maxTrainLength) {
			trainPart = AIVehicle.BuildVehicle(depot, engineType)
			engineCount++
		} else {
			trainPart = AIVehicle.BuildVehicle(depot, wagonType)
			wagonCount++
		}
		if (!AIVehicle.IsValidVehicle(trainPart)) {
			finished = true //we will accept that this train is not the maximum length
		}
		AIVehicle.MoveWagon(trainPart, 0, vehicle, position);	
	}
	AIVehicle.RefitVehicle(vehicle, cargoType)
	
	
	/*for (local i = AIVehicle.GetNumWagons(vehicle); i > 0; i--) {
		local trainPart = AIVehicle.BuildVehicle(depot, AIVehicle.GetWagonEngineType(vehicle, i))
		if (AIVehicle.IsValidVehicle(trainPart)) {
			AIVehicle.MoveWagon(trainPart, AIVehicle.GetNumWagons(trainPart)-1, vehicle, i+1);
			if (AIVehicle.GetLength(vehicle) > maxTrainLength) {
				//adding this wagon did not work out well
				AIVehicle.SellWagon(vehicle, i+1)
				i = -1
			}
		}
	}
	AIVehicle.RefitVehicle(vehicle, GetVehicleCargoType(vehicle))
	*/
}

/* A Train system contains the following information:
 * system[0] -> Rail Type
 * system[1] -> Cargo Type
 * system[2] -> Used platform length
 * system[3] -> * nothing
 */
function SelectTrainSystem(cargo) {
	local l = AIRailTypeList();
	local railspeeds = {}
	local trains = {}
	local maxSpeed = 0
	local maxCost = 0
	local totalTrainCount = 0
	//Filter only the available railtypes
	l.Valuate(AIRail.IsRailTypeAvailable);
	l.KeepValue(1);
	
	foreach (railType, index in l) {
		local result = SelectTrains(cargo, railType)
		trains[railType] <- result
		railspeeds[railType] <- result[3]
		if (result[3] > maxSpeed)
			maxSpeed = result[3];
		totalTrainCount += result[4]
		local cost = AIRail.GetBuildCost(railType, AIRail.BT_TRACK)
		if (cost > maxCost)
			maxCost = cost
	}
	if (maxSpeed == 0) {
		LogError("No trains could be found for " + AICargo.GetCargoLabel(cargo) + "!")
		return false
	}
	//Log("Max speed " + maxSpeed)
	local chances = {}
	local totalChance = 0;
	foreach (railType, index in l) {
		local result = trains[railType]
		if (result[3] < maxSpeed/3) {
			chances[railType] <- 0; //we don't want trains running this slowly (e.g. 40 km/h tracks)
		} else {
			local chance = result[3] * totalTrainCount + result[4] * maxSpeed;
			local cost = AIRail.GetBuildCost(railType, AIRail.BT_TRACK)
			chance += chance *2 * (maxCost - cost) / (maxCost*3)
			chances[railType] <- chance
			totalChance += chance
		}
		//Log("Rail type with speed " + result[3] + " has chance: " + chances[railType]);
	}
	local fortuna = AIBase.RandRange(totalChance)
	local sum = 0;
	foreach (railType, index in l) {
		sum += chances[railType]
		if (sum > fortuna)
			return [railType, cargo, 4 + AIBase.RandRange(4), maxSpeed]
	}
	Log("Something went terribly wrong in selecting a rail type and train set")
}

function GetTrainSystem(train) {
	local location = AIVehicle.GetLocation(train)
	local railType = AIRail.GetRailType(location)
	local cargoType = GetVehicleCargoType(train)
	local maxSpeed = AIRail.GetMaxSpeed(railType)
	if (maxSpeed == 0)
		maxSpeed = 100000
	local maxTrainSpeed = GetMaxVehicleSpeed(train)
	if (maxTrainSpeed < maxSpeed)
		maxSpeed = maxTrainSpeed
	local platformLength = GetPlatformLengthOfTrain(train)
		
	return [railType, cargoType, platformLength, maxSpeed]
}

/* Select all Trains (engines, carriages, trainsets) for a certain cargo and rail type
 * Trains contain the following information:
 * trains[0] -> AIEngine list containing carriages
 * trains[1] -> AIEngine list containing locomotives
 * trains[2] -> AIEngine list containing trainsets
 * trains[3] -> Maximum speed of the trains
 * trains[4] -> Number of different available trains
 */
function SelectTrains(cargo, railType) {
	local maxTrackSpeed = AIRail.GetMaxSpeed(railType)
	if (maxTrackSpeed == 0)
		maxTrackSpeed = 100000
	//Log("speed: "+maxTrackSpeed);
	
	local wgns = AIEngineList(AIVehicle.VT_RAIL);
	local locs = AIEngineList(AIVehicle.VT_RAIL);
	local sets = AIEngineList(AIVehicle.VT_RAIL);
	wgns.Valuate(AIEngine.IsWagon); wgns.KeepValue(1);
	locs.Valuate(AIEngine.IsWagon); locs.KeepValue(0);
	sets.Valuate(AIEngine.IsWagon); sets.KeepValue(0);
	wgns.Valuate(AIEngine.IsBuildable); wgns.KeepValue(1);
	locs.Valuate(AIEngine.IsBuildable); locs.KeepValue(1);
	sets.Valuate(AIEngine.IsBuildable); sets.KeepValue(1);
	wgns.Valuate(AIEngine.CanRunOnRail, railType); wgns.KeepValue(1);
	locs.Valuate(AIEngine.CanRunOnRail, railType); locs.KeepValue(1);
	sets.Valuate(AIEngine.CanRunOnRail, railType); sets.KeepValue(1);
	
	locs.Valuate(AIEngine.CanPullCargo, cargo); locs.KeepValue(1);
	wgns.Valuate(AIEngine.CanRefitCargo, cargo); wgns.KeepValue(1);
	sets.Valuate(AIEngine.CanRefitCargo, cargo); sets.KeepValue(1);

	locs.Valuate(AIEngine.HasPowerOnRail, railType); locs.KeepValue(1);
	sets.Valuate(AIEngine.HasPowerOnRail, railType); sets.KeepValue(1);
	locs.Valuate(AIEngine.GetCapacity); locs.KeepBelowValue(20);
	sets.Valuate(AIEngine.GetCapacity); sets.KeepAboveValue(20);
	
	local maxsetspeed = 0;
	local maxlocspeed = 0;
	local maxwgnspeed = 0;
	
	foreach (wagon, value in wgns) {
		local maxSpeed = AIEngine.GetMaxSpeed(wagon)
		if (maxSpeed > maxwgnspeed) {
			maxwgnspeed = maxSpeed
		} else if (maxSpeed == 0) {
			maxwgnspeed = 100000
		}
	}
	foreach (engine, value in locs) {
		local maxSpeed = AIEngine.GetMaxSpeed(engine);
		if (maxSpeed > maxlocspeed) {
			maxlocspeed = maxSpeed
		}
	}		
	foreach (trainset, value in sets) {
		local maxSpeed = AIEngine.GetMaxSpeed(trainset);
		if (maxSpeed > maxsetspeed) {
			maxsetspeed = maxSpeed
		}
	}
	local maxSpeed = min(maxTrackSpeed, max(maxsetspeed, min(maxlocspeed, maxwgnspeed)))
	local minSpeed = maxSpeed * 8 / 11
	//Log ("Max speeds: " + maxsetspeed +"/" + maxlocspeed + "/" + maxwgnspeed + "->" + maxSpeed + " - " + minSpeed);
	
	if (maxwgnspeed < 100000) {
		wgns.Valuate(AIEngine.GetMaxSpeed); wgns.KeepAboveValue(minSpeed);
	}
	locs.Valuate(AIEngine.GetMaxSpeed); locs.KeepAboveValue(minSpeed);
	sets.Valuate(AIEngine.GetMaxSpeed); sets.KeepAboveValue(minSpeed);

	local traincount = sets.Count() + 2*min(wgns.Count(), locs.Count());
	return [wgns, locs, sets, maxSpeed, traincount]
	
}



class TrainBuyPlan extends Plan {
	trainSystem = null
	depot = null
	train = null
	stationPlan = null
	constructor(trainSystem, stationPlan, data = null) {
		if (data) {
			this.trainSystem = data.trainSystem
			this.stationPlan = stationPlan
			this.depot = null
			this.train = null
		} else {
			this.trainSystem = trainSystem
			this.stationPlan = stationPlan
			this.depot = null
			this.train = null
		}
	}
	
	function getData() {
		return {trainSystem = this.trainSystem}
	}
	
	function Build(testmode = false) {
		this.depot = this.stationPlan.depot
		if (this.depot == null) {
			LogError("No depot to buy the train!")
			return false
		}
		ConnectDepot(depot)
		LogTile("Buying a train at ", depot)
		train = BuyTrain(depot, trainSystem[1], trainSystem[2])
		if (train == null && !testmode)
			train = BuyTrain(depot, trainSystem[1], trainSystem[2])
		if (train == null && !testmode)
			train = BuyTrain(depot, trainSystem[1], trainSystem[2])
		if (train == null) {
			LogError("Couldn't buy a train.")
			return false
		}
		if (testmode) {
			AIVehicle.SellVehicle(train)
			return true
		}
		Log("Built train has ID " + train)
		return train
	}
	
	function EstimateCosts() {
		local accountant = AIAccounting()
		local train = Build(false)
		if (train == false)
			return false
		local costs = accountant.GetCosts()
		Log ("train costs estimation: " + costs + " id " + train)
		if (!AIVehicle.SellVehicle(train))
			LogWarning("Failed selling vehicle")
		return costs
	}
	
	function Undo() {
		AIVehicle.SellVehicle(train)	
	}
}