@tool
extends Path3D
## Guard rail along a curve with nicely spaced posts
##
## Ref: https://www.youtube.com/watch?v=Gfpnxg-jne4

## Distance between railing posts, in meters
@export var post_space := 4.0:
	set(value):
		if value != post_space:
			_dirty = true
		post_space = value

## If enabled, always places a railing post at the end of the curve.
## Post spacing will be adjusted to accomodate.
@export var end_cap := false:
	set(value):
		if value != end_cap:
			_dirty = true
		end_cap = value

var _dirty := false

@onready var post_multi_mesh: MultiMeshInstance3D = $PostMultiMesh

static var multimesh_mesh: Mesh = preload(
	"res://src/props/scenery/highway/guard_rail/guard_rail_post_mesh.tres"
)


func _ready() -> void:
	# Set up multimesh
	# This is a workaround for an engine bug as of Godot 4.3
	# ref: https://github.com/godotengine/godot/issues/68592
	var multimesh := MultiMesh.new()
	multimesh.transform_format = MultiMesh.TRANSFORM_3D
	multimesh.instance_count = 0
	multimesh.mesh = multimesh_mesh

	post_multi_mesh.multimesh = multimesh


func _update_multimesh() -> void:
	if not curve:
		post_multi_mesh.multimesh.instance_count = 0
		return

	var path_length := curve.get_baked_length()
	var count: int = floor(path_length / post_space)
	var spacing := post_space

	if end_cap:
		spacing = path_length / float(count)
		count += 1

	post_multi_mesh.multimesh.instance_count = count

	for i in range(0, count):
		var tx := curve.sample_baked_with_rotation(spacing * i, true, true)
		post_multi_mesh.multimesh.set_instance_transform(i, tx)


func _process(_delta: float) -> void:
	if _dirty:
		_update_multimesh()
		_dirty = false


func _on_curve_changed() -> void:
	_dirty = true
