/**
 * Helper static functions to find places for stations, depots, etc.
 */

class finder {

	static function get_tiles_near_factory(factory)
	{
		local cov = settings.get_station_coverage()
		local area = []

		// generate a list of tiles that will reach the factory
		local ftiles = factory.get_tile_list()
		foreach (c in ftiles) {
			for(local dx = -cov; dx <= cov; dx++) {
				for(local dy = -cov; dy <= cov; dy++) {
					if (dx==0 && dy==0) continue;

					local x = c.x+dx
					local y = c.y+dy

					if (x>=0 && y>=0) area.append( (x << 16) + y );
				}
			}
		}
		// sort
		sleep()
		area.sort()
		return area
	}

	static function find_empty_place(area, target)
	{
		// find place closest to target
		local tx = target.x
		local ty = target.y

		local best = null
		local dist = 10000
		// check for flat and empty ground
		for(local i = 0; i<area.len(); i++) {

			local h = area[i]
			if (i>0  &&  h == area[i-1]) continue;

			local x = h >> 16
			local y = h & 0xffff

			if (world.is_coord_valid({x=x,y=y})) {
				local tile = square_x(x, y).get_ground_tile()

				if (tile.is_empty()  &&  tile.get_slope()==0) {
					local d = abs(x - tx) + abs(y - ty)
					if (d < dist) {
						dist = d
						best = tile
					}
				}
			}
		}
		return best
	}


	static function _find_places(area, test /* function */)
	{
		local list = []
		// check for flat and empty ground
		for(local i = 0; i<area.len(); i++) {

			local h = area[i]
			if (i>0  &&  h == area[i-1]) continue;

			local x = h >> 16
			local y = h & 0xffff

			if (world.is_coord_valid({x=x,y=y})) {
				local tile = square_x(x, y).get_ground_tile()

				if (test(tile)) {
					list.append(tile)
				}
			}
		}
		return list.len() > 0 ?  list : []
	}

	static function find_empty_places(area)
	{
		return _find_places(area, _tile_empty)
	}

	static function _tile_empty(tile)
	{
		return tile.is_empty()  &&  tile.get_slope()==0
	}

	static function _tile_empty_or_field(tile)
	{
		return tile.get_slope()==0  &&  (tile.is_empty()  ||  tile.find_object(mo_field))
	}

	static function find_water_places(area)
	{
		return _find_places(area, _tile_water)
	}

	static function _tile_water(tile)
	{
		return tile.is_water()  &&  (tile.find_object(mo_building)==null)  &&  (tile.find_object(mo_depot_water)==null)
	}

	static function _tile_water_way(tile)
	{
		if (tile.is_water()) {
			return true // (tile.find_object(mo_building)==null)  &&  (tile.find_object(mo_depot_water)==null)
		}
		else {
			foreach(obj in tile.get_objects()) {
				try {
					if (obj.get_type() != mo_way) continue;

					if (obj.get_waytype() == wt_water) {
						return obj.get_desc().get_topspeed() > 5
					}
				}
				catch(ev) {
					// object gone, most likely vehicle that moved on
				}
			}
		}
		return false
	}

	static function find_station_place(factory, target, unload = false)
	{
		if (unload) {
			// try unload station from station manager
			local res = ::station_manager.access_freight_station(factory).road_unload
			if (res) {
				return [res]
			}
		}
		local can_delete_fields = factory.get_field_count() > factory.get_min_field_count()

		local area = get_tiles_near_factory(factory)

		if (can_delete_fields) {
			return _find_places(area, _tile_empty_or_field);
		}
		else {
			return find_empty_places(area)
		}
	}

	/**
	 * Can harbour of length @p len placed at @p pos (land tile!) in direction @p d.
	 */
	static function check_harbour_place(pos, len, d /* direction */)
	{
		local from = pos
		for(local i = 0; i<len; i++) {
			local to = from.get_neighbour(i>0 ? wt_water : wt_all, d)
			if (to  &&  _tile_water(to)  &&  to.can_remove_all_objects(our_player)==null) {
				from = to
			}
			else {
				return false
			}
		}
		return true
	}

}
