class_name Player extends CharacterBody3D

@export_category("Player Ailments")
@export_range(0, 100, 1.0) var health: int = 100
@export_range(0, 100, 1.0) var toxicity: int = 0
@export_range(0, 100, 1.0) var burns: int = 0
@export_range(0, 100, 1.0) var physical_trauma: int = 0
@export_range(0, 100, 1.0) var hunger: int = 0
@export_range(0, 100, 1.0) var thirst: int = 0
@export_range(0, 100, 1.0) var insanity: int = 0
@export_range(0, 100, 1.0) var drowning: int = 0

@export_category("Player Physics/Attributes")
@export_range(1, 35, 1) var speed: float = 5.0
@export_range(10, 400, 1) var acceleration: float = 100.0

@export_range(0.1, 3.0, 0.1) var jump_height: float = 1.0
@export_range(0.1, 3.0, 0.1, "or_greater") var camera_sens: float = 1.0
@export_range(0.1, 3.0, 0.1, "or_greater") var joystick_camera_sens_multiplier: float = 5.0

@export_category("Node References")
@export var camera: Camera3D
@export var neck: Node3D
@export var body: Node3D
@export var head: Node3D
@export var watch_face: MeshInstance3D
@export var animation_player: AnimationPlayer


var jumping: bool = false
var is_using_joystick: bool = false

var move_dir: Vector2 # Input direction for movement
var look_dir: Vector2 # Input direction for look/aim
var walk_vel: Vector3 # Walking velocity
var grav_vel: Vector3 # Gravity velocity
var jump_vel: Vector3 # Jumping velocity
var gravity: float = 9.8 # Gravity


func _ready():
	if is_multiplayer_authority():
		print("-> [%s] Authority granted. Setting up camera and input." % name)
		camera.make_current()
		Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
		#$Mesh.hide() # commented out; we want self-body view
		$Label3D.hide()
		head.hide()


func set_player_name(peer_id: String, player_name: String):
	name = peer_id
	$Label3D.text = player_name


func _input(event):
	if is_multiplayer_authority():
		if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			if event is InputEventMouseMotion:
				is_using_joystick = false
				look_dir = event.relative * 0.001
				_rotate_camera()
			elif event is InputEventJoypadMotion:
				is_using_joystick = true
				
			if Input.is_action_just_pressed("jump"):
				jumping = true
				animation_player.play("Jump")
		
		if event.is_action_pressed("esc") and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
		elif event.is_action_pressed("esc") and not Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)


func _physics_process(delta: float) -> void:
	if is_multiplayer_authority():
		velocity = walk(delta) + _gravity(delta) + _jump(delta)
			
		global_rotation.x = 0.0
		global_rotation.z = 0.0

		if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
			_handle_joypad_camera_rotation(delta)
		move_and_slide()


func _rotate_camera(sens_mod: float = 1.0) -> void:
	if is_multiplayer_authority():
		var camera_sens_final = camera_sens
		if is_using_joystick:
			camera_sens_final = camera_sens * joystick_camera_sens_multiplier

		rotation.y -= look_dir.x * camera_sens_final * sens_mod
		neck.rotation.x = clamp(neck.rotation.x - look_dir.y * camera_sens_final * sens_mod, -1.5, 1.5)

	
func _handle_joypad_camera_rotation(delta: float, sens_mod: float = 1.0) -> void:
	var joypad_dir: Vector2 = Input.get_vector("look_left","look_right","look_up","look_down")
	if joypad_dir.length() > 0:
		look_dir += joypad_dir * delta
		_rotate_camera(sens_mod)
		look_dir = Vector2.ZERO

		
func walk(delta: float) -> Vector3:
	if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
		move_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_backward")
		var _forward: Vector3 = neck.global_transform.basis * Vector3(move_dir.x, 0, move_dir.y)
		var walk_dir: Vector3 = Vector3(_forward.x, 0, _forward.z).normalized()
		walk_vel = walk_vel.move_toward(walk_dir * speed * move_dir.length(), acceleration * delta)
		
		if walk_vel:
			animation_player.play("Walk")
		else:
			animation_player.play("Idle")
	return walk_vel

	
func _gravity(delta: float) -> Vector3:
	grav_vel = Vector3.ZERO if is_on_floor() else grav_vel.move_toward(Vector3(0, velocity.y - gravity, 0), gravity * delta)
	if not is_on_floor() and animation_player.current_animation.begins_with("Idle"):
		animation_player.play("Fall")
	return grav_vel

	
func _jump(delta: float) -> Vector3:
	if jumping:
		if is_on_floor(): jump_vel = Vector3(0, sqrt(4 * jump_height * gravity), 0)
		jumping = false
		return jump_vel
	jump_vel = Vector3.ZERO if is_on_floor() else jump_vel.move_toward(Vector3.ZERO, gravity * delta)
	return jump_vel
