class_name TerrainChunkGeometryQueuePlanner
extends RefCounted

## Pure desired-operation planner for terrain chunk geometry queues.

const GODOT_WORLD_POSITION_SCRIPT := preload(
	"res://src/domain/coordinates/godot_world_position.gd"
)


## Returns fresh nearest-first create and stable-order remove request arrays.
## The caller retains queue storage, execution order and every render side effect.
func plan(tile_states: Dictionary, focus_position: RefCounted) -> Dictionary:
	var chunk_create_requests: Array = []
	var chunk_remove_requests: Array = []
	if (
		focus_position == null
		or focus_position.get_script() != GODOT_WORLD_POSITION_SCRIPT
	):
		return _create_plan(chunk_create_requests, chunk_remove_requests)

	var focus_x_units := float(focus_position.get("x_units"))
	var focus_z_units := float(focus_position.get("z_units"))
	for tile_key_variant in tile_states.keys():
		var tile_key := String(tile_key_variant)
		var tile_state: Dictionary = tile_states[tile_key_variant]
		var current_chunks: Dictionary = tile_state["chunks"]
		var desired_lods: Dictionary = tile_state["desired_lods"]
		var desired_tile_lod := int(tile_state.get("desired_tile_lod", -1))

		if desired_tile_lod >= 0:
			for chunk_id in current_chunks.keys():
				chunk_remove_requests.append({"tile": tile_key, "chunk": chunk_id})
			continue

		for chunk_id in current_chunks.keys():
			if not desired_lods.has(chunk_id):
				chunk_remove_requests.append({"tile": tile_key, "chunk": chunk_id})
				continue
			if int(current_chunks[chunk_id]["lod"]) != int(desired_lods[chunk_id]):
				chunk_create_requests.append(_create_chunk_request(
					tile_key,
					chunk_id,
					int(desired_lods[chunk_id]),
					_resolve_chunk_origin(tile_state, int(chunk_id)),
					focus_x_units,
					focus_z_units
				))

		for chunk_id in desired_lods.keys():
			if current_chunks.has(chunk_id):
				continue
			chunk_create_requests.append(_create_chunk_request(
				tile_key,
				chunk_id,
				int(desired_lods[chunk_id]),
				_resolve_chunk_origin(tile_state, int(chunk_id)),
				focus_x_units,
				focus_z_units
			))

	chunk_create_requests.sort_custom(func(
			left_request: Dictionary,
			right_request: Dictionary
	) -> bool:
		return float(left_request["dist_sq"]) < float(right_request["dist_sq"])
	)
	return _create_plan(chunk_create_requests, chunk_remove_requests)


func _resolve_chunk_origin(tile_state: Dictionary, chunk_id: int) -> Vector3:
	var chunks: Array = tile_state["data"].get("chunks", [])
	if chunk_id >= 0 and chunk_id < chunks.size():
		var chunk: Dictionary = chunks[chunk_id]
		if not chunk.is_empty():
			return chunk.get("origin", Vector3.ZERO)
	return tile_state.get("origin", Vector3.ZERO)


func _create_chunk_request(
		tile_key: String,
		chunk_id: Variant,
		desired_lod: int,
		chunk_origin: Vector3,
		focus_x_units: float,
		focus_z_units: float
) -> Dictionary:
	var delta_x_units := chunk_origin.x - focus_x_units
	var delta_z_units := chunk_origin.z - focus_z_units
	return {
		"tile": tile_key,
		"chunk": chunk_id,
		"lod": desired_lod,
		"dist_sq": delta_x_units * delta_x_units + delta_z_units * delta_z_units,
	}


func _create_plan(chunk_create_requests: Array, chunk_remove_requests: Array) -> Dictionary:
	return {
		"create_requests": chunk_create_requests,
		"remove_requests": chunk_remove_requests,
	}
