## Procedural snow cap geometry for flat roofs and vehicles.
extends Node3D

enum PlacementMode {
	AUTO_TARGET_AABB,
	MANUAL_SURFACE,
}

const SNOW_CAP_SHADER: Shader = preload("res://core/daynight/snow_cap.gdshader")

@export var placement_mode: PlacementMode = PlacementMode.AUTO_TARGET_AABB
@export var target_path: NodePath
@export var size_inset: Vector2 = Vector2(0.25, 1.0)
@export var position_offset: Vector3 = Vector3(0.0, -0.08, 0.0)
@export var max_snow_height: float = 0.2
@export_range(1, 24) var subdivide_width: int = 6
@export_range(1, 24) var subdivide_depth: int = 18
@export var manual_surface_center: Vector3 = Vector3.ZERO
@export var manual_surface_size: Vector2 = Vector2(2.0, 6.0)

var _mesh_instance: MeshInstance3D

func _ready() -> void:
	add_to_group("weather_overlay_ignore")
	_ensure_mesh_instance()
	_rebuild()

func rebuild() -> void:
	_rebuild()

func _ensure_mesh_instance() -> void:
	_mesh_instance = get_node_or_null("SnowCapMesh") as MeshInstance3D
	if _mesh_instance != null:
		return

	_mesh_instance = MeshInstance3D.new()
	_mesh_instance.name = "SnowCapMesh"
	_mesh_instance.add_to_group("weather_overlay_ignore")
	add_child(_mesh_instance)

func _rebuild() -> void:
	var cap_width: float = 0.0
	var cap_depth: float = 0.0
	var cap_surface_center := Vector3.ZERO

	if placement_mode == PlacementMode.MANUAL_SURFACE:
		cap_width = manual_surface_size.x
		cap_depth = manual_surface_size.y
		cap_surface_center = manual_surface_center
	else:
		var target_mesh: MeshInstance3D = get_node_or_null(target_path) as MeshInstance3D
		if target_mesh == null:
			if _mesh_instance:
				_mesh_instance.visible = false
			return

		var target_aabb: AABB = _get_target_aabb(target_mesh)
		cap_width = max(target_aabb.size.x - size_inset.x, 0.1)
		cap_depth = max(target_aabb.size.z - size_inset.y, 0.1)
		cap_surface_center = Vector3(
			target_aabb.position.x + target_aabb.size.x * 0.5,
			target_aabb.position.y + target_aabb.size.y,
			target_aabb.position.z + target_aabb.size.z * 0.5
		)

	var cap_center := cap_surface_center + Vector3(0.0, max_snow_height * 0.5, 0.0)

	var box_mesh := BoxMesh.new()
	box_mesh.size = Vector3(cap_width, max_snow_height, cap_depth)
	box_mesh.subdivide_width = subdivide_width
	box_mesh.subdivide_depth = subdivide_depth
	_mesh_instance.mesh = box_mesh
	_mesh_instance.position = cap_center + position_offset
	_mesh_instance.material_override = _build_material(cap_width, cap_depth)
	_mesh_instance.visible = true

func _build_material(cap_width: float, cap_depth: float) -> ShaderMaterial:
	var material := ShaderMaterial.new()
	material.shader = SNOW_CAP_SHADER
	material.set_shader_parameter("max_height", max_snow_height)
	material.set_shader_parameter("half_width", cap_width * 0.5)
	material.set_shader_parameter("half_depth", cap_depth * 0.5)
	return material

func _get_target_aabb(target_mesh: MeshInstance3D) -> AABB:
	var points: Array[Vector3] = []
	for point in _get_aabb_points(target_mesh.get_aabb()):
		points.append(target_mesh.transform * point)

	var merged := AABB(points[0], Vector3.ZERO)
	for point in points:
		merged = merged.expand(point)
	return merged

func _get_aabb_points(aabb: AABB) -> Array[Vector3]:
	var p: Vector3 = aabb.position
	var s: Vector3 = aabb.size
	return [
		p,
		p + Vector3(s.x, 0.0, 0.0),
		p + Vector3(0.0, s.y, 0.0),
		p + Vector3(0.0, 0.0, s.z),
		p + Vector3(s.x, s.y, 0.0),
		p + Vector3(s.x, 0.0, s.z),
		p + Vector3(0.0, s.y, s.z),
		p + s,
	]
