extends RigidBody3D

var has_stage: bool = false:
	set(on_stage):
		if has_stage != on_stage: 
			if on_stage:
				has_stage = true
				if is_inside_tree():
					camera.make_current()
					get_viewport().gui_release_focus()
					Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
					var jitter_tween: Tween = create_tween()
					jitter_tween.tween_property(self, "jitter_strength", 1, 1)
					if has_entered: emit_signal("ui_exited")
				elif has_stage:
					camera.current = true
					jitter_strength = 1
			else:
				if is_inside_tree() and has_stage:
					Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
					var jitter_tween: Tween = create_tween()
					jitter_tween.tween_property(self, "jitter_strength", 0, 0.5)
					if has_entered: emit_signal("ui_exited")
				else:
					jitter_strength = 0
				has_stage = false
				
		sleeping = has_stage
				
@export_range (0, 10) var max_speed: float = 3
@export_range (0, 10) var max_acceleration: float = 5
@export_range (0, 20) var damp: float = 10
@export_range (0.1, 1) var mouse_jerk:float = 0.5
@export_range (10, 100) var gamepad_response = 50
@export_range (50, 500) var mouse_jerk_rejection = 200
@export var max_angle:float = 75

@export var camera_jitter_speed:float = 3
@export var angular_jitter:Vector3 = Vector3(0.1, 0, 0.05)
@export var angular_jitter_speed:Vector3 = Vector3(2, 1, 1)
@export var location_jitter:Vector3 = Vector3(0.05, 0.05, 0.05)
@export var location_jitter_speed:Vector3 = Vector3(2, 0.3, 1)
var jitter_strength: float = 0

var loc_noise_spot: Vector3 = Vector3.ZERO
var rot_noise_spot: Vector3 = Vector3.ZERO

var rotation_speed: Vector2 = Vector2.ZERO
var current_mouse_rotation: Vector2 = Vector2.ZERO
var mouse_sensitivity: Vector2 = Vector2(3, 3)
var noise: Noise = FastNoiseLite.new()
var crouched:bool = false
var on_crouch_cooldown:bool = false

@onready var yaw:Node3D = $Yaw
@onready var pitch:Node3D = $Yaw/Pitch
@onready var mount:Node3D = $Yaw/Pitch/Mount
@onready var camera:Camera3D = $Yaw/Pitch/Mount/Camera3D
@onready var focus_ray: RayCast3D = $Yaw/Pitch/Mount/Camera3D/RayCast3D

var zoomed:
	set(zoom):
		if zoomed != zoom:
			if zoom:
				var zoom_tween = create_tween()
				zoom_tween.tween_property(camera, "fov", camera.fov*0.5, 0.5)
			else:
				var zoom_tween = create_tween()
				zoom_tween.tween_property(camera, "fov", camera.fov*2, 0.5)
		zoomed = zoom    
signal ui_entered
var has_entered:bool = false
signal ui_exited

func _ready():
	_handle_jitter(0)
	
func _on_ini_room():
	State.take_stage(self)
	get_tree().call_group("scene_actors", "play_scene", Scenes.id.YOUTH_DRAEVEN, false)
	State.queue_for_stage(self)
	
func _process(_delta):
	
	if focus_ray.get_collider() != null and not has_entered:
		emit_signal("ui_entered")
		has_entered = true
		focus_ray.get_collider().reveal()
	
	if has_stage:
		if has_entered:
			if focus_ray.get_collider() == null:
				emit_signal("ui_exited")
				has_entered = false    
			if Input.is_action_just_pressed("ui_accept"):
				State.pass_stage_to(focus_ray.get_collider())
		else:
			if Input.is_action_just_pressed("zoom_in_controller"):
				zoomed = true
			elif Input.is_action_just_released("zoom_in_controller"):
				zoomed = false
			
func _physics_process(delta:float):
	if has_stage:
		_handle_movement(delta)
		_handle_rotation(delta)
	if jitter_strength > 0 and not State.reduce_motion: _handle_jitter(delta)

func _handle_movement(delta:float):
	var input:Vector2 = Vector2(Input.get_action_strength("player_right") - Input.get_action_strength("player_left"),
						Input.get_action_strength("player_backwards")*0.8 - Input.get_action_strength("player_forwards"))
	
	if input.length()>1:
		input = input.normalized()
	
	var direction: Vector3 = Vector3(input.x, 0, input.y)
	
	direction = yaw.global_transform.basis.x * direction.x + transform.basis.y * direction.y + yaw.global_transform.basis.z * direction.z        
	
	if linear_velocity.length() > (linear_velocity + (direction*max_speed - linear_velocity)).length():
		direction = Vector3.ZERO
	else:
		direction *= (direction*max_speed - linear_velocity).length()*max_acceleration
	
	linear_damp = damp * max(0.5, 1 - input.length())
	
	apply_central_impulse(direction*delta)

func _handle_rotation(delta:float):
	var smoothness = min(3, 60.0/Engine.get_frames_per_second())
	
	var input_speed = Vector2( Input.get_action_strength("look_right")-Input.get_action_strength("look_left"), Input.get_action_strength("look_up")-Input.get_action_strength("look_down")) * gamepad_response
	
	if current_mouse_rotation.length()>0:
		input_speed = current_mouse_rotation
		current_mouse_rotation = Vector2.ZERO
		
	rotation_speed = rotation_speed * (1-mouse_jerk*smoothness) + input_speed * mouse_jerk * smoothness
	
	if rotation_speed.y > 0 and pitch.rotation_degrees.x < 0:
		rotation_speed.y *= 1-pow(pitch.rotation_degrees.x/-max_angle, 4)
	elif rotation_speed.y < 0 and pitch.rotation_degrees.x > 0 :
		rotation_speed.y *= 1-pow(pitch.rotation_degrees.x/max_angle, 4)
	
	yaw.rotate_y(deg_to_rad(-rotation_speed.x * delta * mouse_sensitivity.x))
	pitch.rotate_x(deg_to_rad(-rotation_speed.y * delta * mouse_sensitivity.y))

func _handle_jitter(delta):
	loc_noise_spot += Vector3(delta * camera_jitter_speed * location_jitter_speed)
	rot_noise_spot += Vector3(delta * camera_jitter_speed * angular_jitter_speed)
	pitch.position = Vector3(
						noise.get_noise_1d(loc_noise_spot.x),
						noise.get_noise_1d(loc_noise_spot.y),
						noise.get_noise_1d(loc_noise_spot.z)
						) * location_jitter * jitter_strength
	
	if not State.reduce_motion: mount.rotation = Vector3(
						noise.get_noise_1d(rot_noise_spot.x),
						noise.get_noise_1d(rot_noise_spot.y),
						noise.get_noise_1d(rot_noise_spot.z)
						) * angular_jitter * jitter_strength
	

func _handle_mouse_input(event:InputEventMouseMotion):
	if event.relative.length() < mouse_jerk_rejection:
		current_mouse_rotation = event.relative

func _input(event:InputEvent):
	if has_stage:
		if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			_handle_mouse_input(event)
			get_viewport().set_input_as_handled()
		if event is InputEventMouseButton and event.pressed:
			if Input.is_action_just_pressed("zoom_in_mouse"):
				zoomed = true
			elif Input.is_action_just_pressed("zoom_out_mouse"):
				zoomed = false
			else:
				State.free_focus()
			get_tree().call_group("interactables", "reveal")
		#if event.is_action_pressed("ui_accept"):
		#    State.pass_stage_to(focus_ray.get_collider())
		#    get_viewport().set_input_as_handled()
	else:
		if event is InputEventMouseButton:
			if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
				if !Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
					State.take_stage(self, true)
					get_tree().call_group("interactables", "collapse")

func play_scene(id: int, _repeat):
	if id == Scenes.id.YOUTH_DRAEVEN:
		var rotation_tween = create_tween()

func scene_finished(_id, repeat: bool):
	if repeat:
		State.take_stage(self, true)

func _on_bed_enter(_body):
	if not (crouched or on_crouch_cooldown):
		$PlayerAnimationPlayer.queue("crouch")
		var tween = create_tween()
		tween.tween_property(self, "max_speed", max_speed/2, 0.3)
		crouched = true

func _on_bed_exit(_body):
	if crouched:
		crouched = false
		$PlayerAnimationPlayer.queue("stand_up")
		var tween = create_tween()
		tween.tween_property(self, "max_speed", max_speed*2, 1)
		
	on_crouch_cooldown = true
	await get_tree().create_timer(1.0).timeout
	on_crouch_cooldown = false
