extends SceneTree

## Converts the heavy baked ADT cache into a lightweight runtime streaming cache.
##
## Usage:
##   godot --headless --path <project> --script res://src/tools/build_adt_streaming_cache.gd -- \
##     --map Azeroth --input res://data/cache/baked_terrain_v2 --output res://data/cache/baked_terrain_stream_v1 --force

const BAKED_TILE_SCRIPT := preload("res://src/resources/baked_adt_tile.gd")
const STREAMING_TILE_SCRIPT := preload("res://src/resources/streaming_adt_tile.gd")


func _initialize() -> void:
	var args := OS.get_cmdline_user_args()
	var map_name := _arg(args, "--map", "Azeroth")
	var input_dir := _res(_arg(args, "--input", "res://data/cache/baked_terrain_v2"))
	var output_dir := _res(_arg(args, "--output", "res://data/cache/baked_terrain_stream_v1"))
	var force := args.has("--force")

	var in_map_dir := input_dir.path_join(map_name)
	var out_map_dir := output_dir.path_join(map_name)
	var in_map_abs := ProjectSettings.globalize_path(in_map_dir)
	var out_map_abs := ProjectSettings.globalize_path(out_map_dir)
	if not DirAccess.dir_exists_absolute(in_map_abs):
		push_error("Input baked terrain cache not found: %s" % in_map_dir)
		quit(1)
		return
	DirAccess.make_dir_recursive_absolute(out_map_abs)

	var dir := DirAccess.open(in_map_dir)
	if dir == null:
		push_error("Cannot open input cache dir: %s" % in_map_dir)
		quit(1)
		return

	var files := dir.get_files()
	files.sort()
	var converted := 0
	var skipped := 0
	var failed := 0
	var started_ms := Time.get_ticks_msec()

	for file_name in files:
		if not file_name.ends_with(".res"):
			continue
		var in_path := in_map_dir.path_join(file_name)
		var out_path := out_map_dir.path_join(file_name)
		if not force and ResourceLoader.exists(out_path):
			skipped += 1
			continue

		var baked: Resource = load(in_path)
		if baked == null or baked.get_script() != BAKED_TILE_SCRIPT:
			push_warning("Invalid baked tile: %s" % in_path)
			failed += 1
			continue

		var stream_tile: Resource = STREAMING_TILE_SCRIPT.new()
		stream_tile.set("format_version", STREAMING_TILE_SCRIPT.FORMAT_VERSION)
		stream_tile.set("map_name", str(baked.get("map_name")))
		stream_tile.set("tile_x", int(baked.get("tile_x")))
		stream_tile.set("tile_y", int(baked.get("tile_y")))
		stream_tile.set("tile_origin", baked.get("tile_origin"))
		stream_tile.set("texture_size", int(baked.get("coarse_texture_size")))
		stream_tile.set("wmo_names", baked.get("wmo_names"))
		stream_tile.set("wmo_placements", baked.get("wmo_placements"))
		stream_tile.set("m2_names", baked.get("m2_names"))
		stream_tile.set("m2_placements", baked.get("m2_placements"))
		stream_tile.set("terrain_mesh", baked.get("coarse_mesh"))

		var err := ResourceSaver.save(stream_tile, out_path)
		if err != OK:
			push_warning("Failed to save streaming tile %s (err=%d)" % [out_path, err])
			failed += 1
			continue

		converted += 1
		if converted % 25 == 0:
			print("Converted %d tiles..." % converted)

	var elapsed_ms := Time.get_ticks_msec() - started_ms
	print("Streaming cache finished. converted=%d skipped=%d failed=%d elapsed=%.2fs" % [
		converted, skipped, failed, float(elapsed_ms) / 1000.0
	])
	quit(0 if failed == 0 else 2)


func _arg(args: PackedStringArray, name: String, default: String) -> String:
	var idx := args.find(name)
	if idx >= 0 and idx + 1 < args.size():
		return args[idx + 1]
	return default


func _res(path: String) -> String:
	if path.begins_with("res://") or path.begins_with("user://"):
		return path
	return "res://" + path.trim_prefix("./").trim_prefix("/")
