class_name M2PlacementGrouper
extends RefCounted

## Stateless ADT M2 placement validation, normalization and transform grouping.

const M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT := preload(
	"res://src/render/m2/m2_placement_transform_resolver.gd"
)


## Groups valid placements by normalized relative M2 path while preserving
## placement order inside each group. Returned containers are caller-owned.
func group_placements(
		tile_origin: Vector3,
		m2_names: PackedStringArray,
		m2_placements: Array
) -> Dictionary:
	var groups: Dictionary = {}
	var placement_transform_resolver := (
		M2_PLACEMENT_TRANSFORM_RESOLVER_SCRIPT.new()
	)
	for placement_variant in m2_placements:
		if not (placement_variant is Dictionary):
			continue
		var placement: Dictionary = placement_variant
		var name_id: int = int(placement.get("name_id", -1))
		if name_id < 0 or name_id >= m2_names.size():
			continue

		var normalized_relative_path := _normalize_relative_path(m2_names[name_id])
		if normalized_relative_path.is_empty():
			continue

		var world_position: Vector3 = placement.get("pos", Vector3.ZERO)
		var local_position := world_position - tile_origin
		var rotation_radians: Vector3 = placement.get("rot", Vector3.ZERO)
		var scale_value := float(placement.get("scale", 1.0))
		var placement_basis: Basis = placement_transform_resolver.resolve_basis(
			rotation_radians,
			normalized_relative_path
		)
		var origin_offset: Vector3 = placement_transform_resolver.resolve_origin_offset(
			rotation_radians,
			normalized_relative_path,
			scale_value
		)
		var local_transform := Transform3D(
			placement_basis.scaled(Vector3.ONE * maxf(scale_value, 0.0001)),
			local_position + origin_offset
		)

		if not groups.has(normalized_relative_path):
			groups[normalized_relative_path] = []
		(groups[normalized_relative_path] as Array).append(local_transform)
	return groups


func _normalize_relative_path(relative_path: String) -> String:
	var normalized_relative_path := relative_path.replace("\\", "/")
	if (
		normalized_relative_path.ends_with(".mdx")
		or normalized_relative_path.ends_with(".mdl")
	):
		normalized_relative_path = normalized_relative_path.get_basename() + ".m2"
	return normalized_relative_path
