@tool
extends Node3D
class_name Slider3D

@export var min: float = 0.0:
	set(new_value):
		min = new_value
		if value < min: value = min
		if !is_node_ready(): await ready
		_update_slider()

@export var max: float = 1.0:
	set(new_value):
		max = new_value
		if value > max: value = max
		if !is_node_ready(): await ready
		_update_slider()
@export var value: float = 0.2:
	set(new_value):
		value = roundi(clamp(new_value, min, max) / step) * step

		if !is_node_ready(): await ready
		label.text = str(value) + " " + label_unit
		on_value_changed.emit(value)
		_update_slider()

@export var step: float = 0.01

@export var show_label: bool = false:
	set(value):
		show_label = value
		if !is_node_ready(): await ready
		label.visible = show_label

@export var label_unit: String = "":
	set(new_value):
		label_unit = new_value
		if !is_node_ready(): await ready
		label.text = str(value) + " " + label_unit

@export var size: Vector3 = Vector3(0.2, 0.01, 0.02): # Warning, units are in cm
	set(value):
		size = value
		if !is_node_ready(): await ready
		_update_shape()
@export var cutout_border: float = 0.02:
	set(value):
		cutout_border = value
		if !is_node_ready(): await ready
		_update_shape()
@export var cutout_depth: float = 0.05:
	set(value):
		cutout_depth = value
		if !is_node_ready(): await ready
		_update_shape()

@onready var outside_rod: CSGBox3D = $Rod/Outside
@onready var cutout: CSGCombiner3D = $Rod/Cutout
@onready var cutout_box: CSGBox3D = $Rod/Cutout/Length
@onready var cutout_end_left: CSGCylinder3D = $Rod/Cutout/EndLeft
@onready var cutout_end_right: CSGCylinder3D = $Rod/Cutout/EndRight
@onready var label: Label3D = $Label

@onready var slider_knob: MeshInstance3D = $Knob

signal on_value_changed(value: float)

var move_plane: Plane

func _ready():
	_update_shape()
	move_plane = Plane(Vector3.UP, Vector3(0, size.y / 200, 0))

func _on_press_down(event: EventPointer):
	_handle_press(event)

func _on_press_move(event: EventPointer):
	_handle_press(event)

func _get_slider_min_max():
	var cutout_radius = (size.z - cutout_border * 2) / 2

	return Vector2(-size.x / 2 + cutout_border + cutout_radius, size.x / 2 - cutout_border - cutout_radius) / 100

func _handle_press(event: EventPointer):
	var ray_pos = event.ray.global_position
	var ray_dir = -event.ray.global_transform.basis.z

	var local_pos = to_local(ray_pos)
	var local_dir = global_transform.basis.inverse() * ray_dir

	var click_pos = move_plane.intersects_ray(local_pos, local_dir)
	
	if click_pos == null:
		return

	var min_max = _get_slider_min_max()

	var pos_x = clamp(click_pos.x, min_max.x, min_max.y)

	var click_percent = inverse_lerp(min_max.x, min_max.y, pos_x)

	value = lerp(min, max, click_percent)
	_update_slider()

func _update_slider():
	var min_max = _get_slider_min_max()

	var click_percent = inverse_lerp(min, max, value)

	slider_knob.position.x = lerp(min_max.x, min_max.y, click_percent)

func _update_shape():
	outside_rod.size = size

	var cutout_width = size.z - cutout_border * 2

	cutout_box.size = Vector3(
		size.x - cutout_border * 2 - (cutout_width),
		cutout_depth,
		cutout_width
	)

	cutout.position = Vector3(
		0,
		size.y / 2 - cutout_depth / 2 + 0.001,
		0
	)

	cutout_end_left.radius = cutout_box.size.z / 2
	cutout_end_right.radius = cutout_box.size.z / 2
	cutout_end_left.height = cutout_depth
	cutout_end_right.height = cutout_depth

	cutout_end_left.position = Vector3(
		-cutout_box.size.x / 2,
		0,
		0
	)

	cutout_end_right.position = Vector3(
		cutout_box.size.x / 2,
		0,
		0
	)

	label.position = Vector3(
		size.x / 200 + 0.005,
		0,
		0
	)
