//A plan to build a bus or truck station in a town
class GaragePlan extends Plan {
	location = null
	frontTile = null
	value = 0
	name = "Garage"
	isFirstGarage = true
	
	constructor(data, place, roadSystem) {
		if (data) {
			this.location = data.location,
			this.frontTile = data.frontTile,
			this.isFirstGarage = data.isFirstGarage
		} else {
			local list = AIDepotList(AITile.TRANSPORT_ROAD)
			for (local depot = list.Begin(); list.HasNext(); depot = list.Next()) {
				if (AIRoad.HasRoadType(depot, AIRoad.GetCurrentRoadType())) {
					this.isFirstGarage = false
				}
			}
			LogTile("Finding a garage location near", place)
			local area = AITileList()
			SafeAddRectangle(area, place, 12)
			//Select locations somewhat near, but not too near
			area.Valuate(AITile.GetDistanceManhattanToTile, place)
			area.KeepAboveValue(3)
			//The garage tiles must be flat
			area.Valuate(IsFlatTile)
			area.KeepValue(1)
			//Find an empty tile to build a garage
			area.Valuate(AITile.IsBuildable)
			area.KeepValue(1)
			area.Valuate(AIRoad.GetNeighbourRoadCount)
			area.KeepBelowValue(3)
			
			//The station should be somewhere near the requested location
			area.Valuate(AITile.GetDistanceManhattanToTile, place)
			area.KeepBottom(40+AIBase.RandRange(9)*40)
			
			local loc = area.Begin()
			local distance = 100
			while (!area.IsEnd()) { //we want to minimize the distance
				local value = AIMap.DistanceManhattan(loc, place)
				if (value < distance) {
					local ft = null
					if (HasAnyRoad(loc + dXY(0, 1))) {
						ft = loc + dXY(0, 1)
					} else if (HasAnyRoad(loc + dXY(0, -1))) {
						ft = loc + dXY(0, -1)
					} else if (HasAnyRoad(loc + dXY(1, 0))) {
						ft = loc + dXY(1, 0)
					} else if (HasAnyRoad(loc + dXY(-1, 0))) {
						ft = loc + dXY(-1, 0)
					} else { // no connected road tiles, dislike this by indicating it is further away
						ft = loc + dXY(-1, 0)
						value = value + 10
					} 
					if (value < distance && AITile.IsBuildable(ft) && AITile.GetSlope(ft) == AITile.SLOPE_FLAT) {
						this.location = loc
						this.frontTile = ft
						distance = value
					}
				}
				loc = area.Next()
			}
		}
		if (this.location == null)
			LogWarning("No location found for " + name)
		else {
			//LogTile("location of " + name + ": ", this.location)
			if (this.isFirstGarage && !BuildGarage()) {
				this.location = null
				LogWarning("Could not build " + name)
			}
		}
	}
	
	function Build(testmode = false) {
		if (this.isFirstGarage) return this.getData()
		if (!testmode && !BuildGarage()) return false
		return this.getData()
	}
	
	function BuildGarage() {
		local execute = false
		if (this.isFirstGarage)
			execute = AIExecMode() // always build the first garage!
		LogTile("Building garage " + name + " at ", this.location)
		if (AIRoad.BuildRoadDepot(this.location, this.frontTile) == false && !IsAlreadyBuilt()) {
			LogTile("Failed building a garage at ", this.location)
			LogWarning(AIError.GetLastErrorString())
			return false
		}
		if (AIRoad.BuildRoad(this.location, this.frontTile) == false && !IsAlreadyBuilt()) {
			LogTile("Failed building a garage connecting road at ", this.location)
			LogWarning(AIError.GetLastErrorString())
			AIRoad.RemoveRoadDepot(this.location)
			return false
		}
		//Log("Succesfully built the garage")
		return true
	}
	
	function getData() {
		return {location = this.location,
			frontTile = this.frontTile,
			isFirstGarage = this.isFirstGarage}
	}
	
	function Cancel() {
		if (!this.isFirstGarage) {
			AIRoad.RemoveRoadDepot(this.location)
			AIRoad.RemoveRoad(this.location, this.frontTile)
		}
	}
	
	function Undo() {
		if (!this.isFirstGarage) {
			AIRoad.RemoveRoadDepot(this.location)
			AIRoad.RemoveRoad(this.location, this.frontTile)
		}
	}
	
}


//A plan to build a bus or truck station in a town
class WaterGaragePlan extends Plan {
	location = null
	secondTile = null
	frontTile = null
	backTile = null
	areaTiles = []
	value = 0
	name = "Water depot"
	isFirstGarage = true
	
	constructor(data, place, roadSystem) {
		if (data) {
			this.location = data.location,
			this.secondTile = data.secondTile
			this.frontTile = data.frontTile
			this.backTile = data.backTile
			this.areaTiles = data.areaTiles
			this.isFirstGarage = data.isFirstGarage
		} else {
			local list = AIDepotList(AITile.TRANSPORT_WATER)
			this.isFirstGarage = true
			if (list.Count() > 0)
				this.isFirstGarage = false
			LogTile("Finding a water depot location near", place, "waterdepot")
			local area = AITileList()
			SafeAddRectangle(area, place, 10)
			//Select locations somewhat near, but not too near
			area.Valuate(AITile.GetDistanceManhattanToTile, place)
			area.KeepAboveValue(3)
			//The garage tiles must be flat
			area.Valuate(IsFlatTile)
			area.KeepValue(1)
			//Find an empty tile to build a garage
			area.Valuate(AIMarine.IsCanalTile)
			area.KeepValue(0)
			
			//The station should be somewhere near the requested location
			area.Valuate(AITile.GetDistanceManhattanToTile, place)
			area.KeepBottom(40+AIBase.RandRange(9)*40)
			
			local loc = area.Begin()
			local value = 0
			while (!area.IsEnd() && (this.location == null || value == 0 )) { //cargo acceptance, we want to maximize this
				foreach (delta in [dXY(0,1), dXY(1,0)]) {
					value = 1
					local st = loc + delta
					local ft = loc - delta
					local bt = st + delta
					if ((AITile.IsBuildable(loc) || AITile.IsWaterTile(loc)) &&
						AITile.GetSlope(ft+dXY(0,1)) == AITile.SLOPE_FLAT && AITile.GetSlope(ft+dXY(0,-1)) == AITile.SLOPE_FLAT &&
						AITile.GetSlope(ft+dXY(1,0)) == AITile.SLOPE_FLAT && AITile.GetSlope(ft+dXY(-1,0)) == AITile.SLOPE_FLAT &&
						(AITile.IsBuildable(st) || (AITile.IsWaterTile(st) && !AIMarine.IsCanalTile(st))) && AITile.GetSlope(st) == AITile.SLOPE_FLAT &&
						(AITile.IsBuildable(ft) || (AITile.IsWaterTile(ft) && !AIMarine.IsCanalTile(ft))) && AITile.GetSlope(ft) == AITile.SLOPE_FLAT &&
						(AITile.IsBuildable(bt) || (AITile.IsWaterTile(bt) && !AIMarine.IsCanalTile(bt))) && AITile.GetSlope(bt) == AITile.SLOPE_FLAT) {
						this.location = loc
						this.secondTile = st
						this.areaTiles = [st]
						this.frontTile = ft
						this.backTile = bt
					}
				}
				loc = area.Next()
			}
		}
		if (this.location == null)
			LogWarning("No location found for " + name, "waterdepot")
		else {
			LogTile("location of " + name + ": ", this.location, "waterdepot")
			if (this.isFirstGarage && !BuildWaterDepot(false)) {
				this.location = null
				LogWarning("Could not build " + name, "waterdepot")
			}
		}
	}
	
	function Build(testmode = false) {
		if (this.isFirstGarage) return this.getData()
		if (!BuildWaterDepot(testmode)) return false
		return this.getData()
	}
	
	function BuildWaterDepot(testmode) {
		local execute = false
		//if (this.isFirstGarage) {
		if (!testmode) {
			execute = AIExecMode() // always build the first garage!
			testmode = false
		}
		LogTile("Building water garage " + name + " at ", this.location, "waterdepot")
		if (!AITile.IsWaterTile(this.location) && !AIMarine.BuildCanal(this.location)) {
			LogTile("Failed building a canal to place a water depot at ", this.location, "waterdepot")
			LogWarning(AIError.GetLastErrorString(), "waterdepot")
			return false
		}
		if (!AITile.IsWaterTile(this.secondTile) && !AIMarine.BuildCanal(this.secondTile)) {
			LogTile("Failed building a canal to place a water depot at ", this.secondTile, "waterdepot")
			LogWarning(AIError.GetLastErrorString(), "waterdepot")
			return false
		}
		if (!AITile.IsWaterTile(this.frontTile) && !AIMarine.BuildCanal(this.frontTile)) {
			LogTile("Failed building a canal to place a water depot at ", this.location, "waterdepot")
			LogWarning(AIError.GetLastErrorString(), "waterdepot")
			return false
		}
		if (!AITile.IsWaterTile(this.backTile) && !AIMarine.BuildCanal(this.backTile)) {
			LogTile("Failed building a canal to place a water depot at ", this.backTile, "waterdepot")
			LogWarning(AIError.GetLastErrorString(), "waterdepot")
			return false
		}
		if (AIMarine.BuildWaterDepot(this.location, this.frontTile) == false && !testmode && !IsAlreadyBuilt()) {
			LogTile("Failed building a water depot at ", this.location, "waterdepot")
			LogWarning(AIError.GetLastErrorString(), "waterdepot")
			return false
		}
		return true
	}
	
	function getData() {
		return {location = this.location,
			secondTile = this.secondTile,
			areaTiles = this.areaTiles,
			frontTile = this.frontTile,
			backTile = this.backTile,
			isFirstGarage = this.isFirstGarage}
	}
	
	function Cancel() {
		if (!this.isFirstGarage)
			AIMarine.RemoveWaterDepot(this.location)
	}
	
	function Undo() {
		if (!this.isFirstGarage)
			AIMarine.RemoveWaterDepot(this.location)
	}
	
}