extends SceneTree

# Usage:
#   godot --headless --path demo --script scripts/bake_dungeon.gd -- SEED DEPTH OUT_DIR
#
# Shells out to bin/genesis --emit=json and bakes the result into two sibling
# assets under OUT_DIR:
#   - mesh_library.tres  (tile catalog, same content MLB.build()
#                         produces at runtime — shareable across bakes)
#   - dungeon_seed<S>_depth<D>.tscn  (PackedScene with a GridMap referencing
#                                     the MeshLibrary, populated per the
#                                     generated dungeon)
#
# Override the genesis binary path with the GENESIS_BIN env var; default is
# "../bin/genesis" relative to the Godot project.

const MESH_LIB_NAME := "mesh_library.tres"
const MLB = preload("res://scripts/mesh_library_builder.gd")

func _init() -> void:
	var args := OS.get_cmdline_user_args()
	if args.size() != 3:
		push_error("usage: -- SEED DEPTH OUT_DIR")
		quit(1)
		return

	var seed := int(args[0])
	var depth := int(args[1])
	var out_dir := args[2]

	var bin_path := OS.get_environment("GENESIS_BIN")
	if bin_path == "":
		bin_path = ProjectSettings.globalize_path("res://") + "../bin/genesis"
	bin_path = bin_path.simplify_path()

	if not DirAccess.dir_exists_absolute(out_dir):
		var err := DirAccess.make_dir_recursive_absolute(out_dir)
		if err != OK:
			push_error("cannot create %s (err %d)" % [out_dir, err])
			quit(1)
			return

	var stdout: Array = []
	var exit_code := OS.execute(bin_path, [
		"--seed", str(seed), "--depth", str(depth), "--emit=json",
	], stdout, true)
	if exit_code != 0:
		push_error("genesis exited %d (bin=%s)" % [exit_code, bin_path])
		quit(1)
		return

	var grid: Dictionary = JSON.parse_string(stdout[0])
	if grid.is_empty():
		push_error("genesis produced invalid JSON")
		quit(1)
		return

	# Step 1 — MeshLibrary.tres.
	var lib_path := "%s/%s" % [out_dir, MESH_LIB_NAME]
	var save_err := MLB.save_resource(lib_path, true)
	if save_err != OK:
		push_error("failed to save %s (err %d)" % [lib_path, save_err])
		quit(1)
		return

	# Step 2 — PackedScene with populated GridMap.
	var lib: MeshLibrary = load(lib_path)
	var root := Node3D.new()
	root.name = "Dungeon_seed%d_depth%d" % [seed, depth]
	var gm := GridMap.new()
	gm.name = "GridMap"
	gm.mesh_library = lib
	gm.cell_size = Vector3(1, 1, 1)
	root.add_child(gm)
	gm.owner = root

	var cells_set := _populate(gm, grid)

	var packed := PackedScene.new()
	var pack_err := packed.pack(root)
	if pack_err != OK:
		push_error("failed to pack scene (err %d)" % pack_err)
		quit(1)
		return

	var scn_path := "%s/dungeon_seed%d_depth%d.tscn" % [out_dir, seed, depth]
	var scn_err := ResourceSaver.save(packed, scn_path)
	if scn_err != OK:
		push_error("failed to save %s (err %d)" % [scn_path, scn_err])
		quit(1)
		return

	print("wrote %s + %s — %d cells" % [scn_path, lib_path, cells_set])
	quit(0)

# Decode the base64 layers and stamp tile IDs into the GridMap. Mirrors
# arcade_scene.gd _populate_grid_map so the CLI produces the same geometry
# as the in-engine path for the same seed/depth. Chasms stay as empty cells.
func _populate(gm: GridMap, grid: Dictionary) -> int:
	var w := int(grid["width"])
	var h := int(grid["height"])
	var terrain := Marshalls.base64_to_raw(grid["terrain"])
	var liquid := Marshalls.base64_to_raw(grid["liquid"])

	var count := 0
	for y in range(h):
		for x in range(w):
			var idx := y * w + x
			var t := terrain[idx]
			var liq := liquid[idx]
			if t == 5 and liq == 3:  # T_LIQUID + L_CHASM
				continue
			var tile := _tile_for(t, liq)
			if tile < 0:
				continue
			gm.set_cell_item(Vector3i(x, 0, y), tile)
			count += 1
	return count

func _tile_for(terrain: int, liquid: int) -> int:
	match terrain:
		1: return MLB.TILE_FLOOR
		4: return MLB.TILE_CORRIDOR
		3: return MLB.TILE_DOOR
		2: return MLB.TILE_WALL
		6: return MLB.TILE_BRIDGE
		7: return MLB.TILE_STAIRS_UP
		8: return MLB.TILE_STAIRS_DOWN
		5:
			match liquid:
				1: return MLB.TILE_WATER
				2: return MLB.TILE_LAVA
				4: return MLB.TILE_BRIMSTONE
				_: return MLB.TILE_WATER
		_: return -1
