@tool
class_name XRFirearmSlide
extends XRToolsInteractableHandleDriven


## XR Firearm Slide script
##
## The firearm slide is a slider transform node controlled by the
## player through [XRToolsInteractableHandle] instances.
##
## The slider translates itelf along its local Z axis, and so should be
## placed as a child of a node to translate and rotate as appropriate.
##
## The interactable slider is not a [RigidBody3D], and as such will not react
## to any collisions.


## Signal for slider moved
signal firearm_slider_moved(position)


## Start position for slide, can be positiv and negativ in values
@export var slider_start : float = 0.0

## End position for slide, can be positiv and negativ in values
@export var slider_end : float = 1.0

## Slider position - move to test the position setup
@export var slider_position : float = 0.0: set = _set_slider_position

## Default position
@export var default_position : float = 0.0

## If true, the slider moves to the default position when released
@export var default_on_release : bool = false


# Add support for is_xr_class on XRTools classes
func is_xr_class(name : String) -> bool:
	return name == "XRFirearmSlide" or super(name)


# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	# In Godot 4 we must now manually call our super class ready function
	super()

	# Set the initial position to match the initial slider position value
	transform = Transform3D(
		Basis.IDENTITY,
		Vector3(0.0, 0.0, slider_position)
	)

	# Connect signals
	if released.connect(_on_slider_released):
		push_error("Cannot connect slider released signal")


# Called every frame when one or more handles are held by the player
func _process(_delta: float) -> void:
	# Get the total handle offsets
	var offset_sum := Vector3.ZERO
	for item in grabbed_handles:
		var handle := item as XRToolsInteractableHandle
		offset_sum += handle.global_transform.origin - handle.handle_origin.global_transform.origin

	# Rotate the offset sum vector from global into local coordinate space
	offset_sum = offset_sum * global_transform.basis

	# Get the average displacement in the Z axis
	var offset := offset_sum.z / grabbed_handles.size()

	# Move the slider by the requested offset
	move_slider(slider_position + offset)


# Move the slider to the specified position
func move_slider(new_position: float) -> void:
	# Do the slider move
	new_position = _do_move_slider(new_position)
	if new_position == slider_position:
		return

	# Update the current position
	slider_position = new_position

	# Emit the moved signal
	emit_signal("firearm_slider_moved", new_position)


# Handle release of slider
func _on_slider_released(_interactable: XRFirearmSlide):
	if default_on_release:
		move_slider(default_position)


# Called when the slider position is set externally
func _set_slider_position(new_position: float) -> void:
	new_position = _do_move_slider(new_position)
	slider_position = new_position


# Do the slider move
func _do_move_slider(new_position: float) -> float:
	# Apply slider limits
	new_position = clamp(new_position, slider_start, slider_end)

	# Move if necessary
	if new_position != slider_position:
		transform.origin.z = new_position

	# Return the updated position
	return new_position
