@tool
extends CharacterBody3D
class_name Mecha

const STEP_MOVE_DISPLACEMENT = 3
const RECOIL_STRENGHT = 0.5
const MOUSE_SENSIVITY = 0.01
const ZOOM_SENSIVITY = 1.

const MAX_ZOOM = 60.
const MIN_ZOOM = 15.

const MAX_HP : float = 50.

signal died

@export var hp : float = MAX_HP

@export var max_speed : float = 15.
@export var turn_speed : float = 5.

var destination := Vector2()

@onready var legs : Array[MechaLeg] = [
    %Leg1,
    %Leg2,
    %Leg3,
    %Leg4,
]

var turn_normal : Vector2 = Vector2.UP

var selected_item = 0

var current_recoil = 0.

func _ready():
    for l in legs:
        l.step.connect(next_leg_can_step)


func next_leg_can_step():
    for l in legs:
        l.can_step = false
    await get_tree().create_timer(MechaLeg.STEP_DURATION / len(legs)).timeout
    for l in legs:
        l.can_step = true


func _process(delta):
    if not Engine.is_editor_hint():
        var target = calculate_target_position()
        %Target.global_position = target

        var target_turn = (Vector2(position.x,position.z) - Vector2(target.x,target.z)).normalized()

        turn_normal = lerp(
            turn_normal,
            target_turn,
            min(turn_speed * delta, 1.)
        )

        var speed = 0.
        var item_used = false

        for i in range(len(%Inventory.get_children())):
            var item = %Inventory.get_children()[i]
            if item is Weapon:
                var used = item.process_fire(
                    delta,
                    self,
                    selected_item == i and Input.is_action_pressed("fire")
                )
                if used:
                    item_used = true
        
        if not item_used and Input.is_action_pressed("move_to"):
            speed = max_speed

        var front_factor = 1 - (turn_normal - target_turn).length() / 2.

        velocity = position.direction_to(target) * speed * front_factor

        %Model.rotation.y = - turn_normal.angle()

        # Recoil
        %Model.position = Vector3(turn_normal.x, 0, turn_normal.y) * current_recoil * RECOIL_STRENGHT
        current_recoil = lerp(current_recoil, 0., 0.5)

        %StepTargets.position.x = lerp(
            0,
            -STEP_MOVE_DISPLACEMENT,
            speed/max_speed
        )

        %Camera3D.look_at(self.position)

        move_and_slide()
        
func calculate_target_position() -> Vector3:
    var camera : Camera3D = get_viewport().get_camera_3d()

    if camera:
        var ray_normal := camera.project_ray_normal(get_viewport().get_mouse_position())
        var ray_origin := camera.project_ray_origin(get_viewport().get_mouse_position())

        var plane = Plane(Vector3.UP, 0)
        var intersection = plane.intersects_ray(ray_origin, ray_normal)
        if intersection:
            return intersection
    return global_position

func get_target_angle() -> float:
    return - turn_normal.angle() + PI / 2

func get_projectile_spwan_point() -> Vector3:
    return global_position + Vector3.UP * 2.

func add_recoil(recoil : = 1.):
    current_recoil += recoil

func hit(damage = 1.):
    hp = max(0, hp - damage)
    if hp == 0:
        die()

func die():
    died.emit()

func _unhandled_input(event):
    if Input.is_action_just_pressed("zoom_in"):
        %Camera3D.position.z = max(MIN_ZOOM, %Camera3D.position.z - ZOOM_SENSIVITY)
    if Input.is_action_just_pressed("zoom_out"):
        %Camera3D.position.z = min(MAX_ZOOM,  %Camera3D.position.z + ZOOM_SENSIVITY)
    if event is InputEventMouseMotion and Input.is_action_pressed("turn_camera") and Input.mouse_mode == Input.MOUSE_MODE_VISIBLE:
        Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
        %CameraPivot.rotation.y -= event.relative.x * MOUSE_SENSIVITY
        %CameraPivot.rotation.x -= event.relative.y * MOUSE_SENSIVITY
        if %CameraPivot.rotation.x > deg_to_rad(-20):
            %CameraPivot.rotation.x = deg_to_rad(-20)
        if %CameraPivot.rotation.x < deg_to_rad(-80):
            %CameraPivot.rotation.x = deg_to_rad(-80)
