extends CharacterBody2D
class_name Plant
signal shoot(ufo: UFO)

@export var y_offset: float = 0.0
@export var height: float = 100.0
@export_range(1.0, 30.0) var grow_time: float = 1.0 
var growing: bool = true
var grow_value: float = 0.0
@export_range(0.0, 1.0) var base_scale: float = 0.5
@onready var target_scale: float = get_scale().x
@export_range(0.0, 1000.0) var max_hp = 100.0
@onready var hp: float = max_hp
var planted_position: Vector2 = Vector2(0, 0)
var dead: bool = false
var attackers: Array[UFO] = []
var target_attacker: UFO = null # Used when the plant is dying
@export var cost: int = 10
@export var bullet_color: Color = Color(1, 1, 1, 1)
@export var bullet_base_speed_u: int = 2000
@export var bullet_base_speed_l: int = 1000
@export var constant_sound: bool = false
@onready var body = $Body

enum TargetMode {
    NEAREST, HIGHEST, LOWEST
}

var ufo_search_timer: Timer = Timer.new()
var attack_timer: Timer = Timer.new()
@onready var ufos: Node2D = get_tree().get_root().find_child("UFOs", true, false)
var victim: UFO = null
@export var target_mode: TargetMode = TargetMode.NEAREST
@export var attack_rate: float = 0.1
@export var attack_range: float = 300.0
@export var attack_damage: float = 10.0

func _ready():
    move_local_y(y_offset)
    planted_position = get_position()
    ufo_search_timer.set_wait_time(0.1)
    ufo_search_timer.set_autostart(false)
    ufo_search_timer.set_one_shot(true)
    ufo_search_timer.connect("timeout", _on_attack)
    attack_timer.set_wait_time(attack_rate)
    attack_timer.set_autostart(false)
    attack_timer.set_one_shot(true)
    attack_timer.connect("timeout", _on_attack)
    add_child(ufo_search_timer)
    add_child(attack_timer)
    attack_timer.start()
    # Workaround crash run out of packets
    if constant_sound:
        $Coin.set_volume_db(-100)
        $Coin.play()

func get_class():
    return "Plant"

func _on_attack():
    find_ufo()
    if victim == null or victim.dead:
        ufo_search_timer.start()
    else:
        emit_signal("shoot", victim)
        attack_timer.start()
        if !constant_sound:
            $Coin.play()

func find_ufo():
    victim = null
    var all_ufos = ufos.get_children()
    var plant_pos = get_global_position()
    if all_ufos.size() > 0:
        var best_ufo = all_ufos[0]
        var best_value = 0.0
        if target_mode == TargetMode.NEAREST: 
            best_value = 10000000.0
        for ufo in ufos.get_children():
            if ufo.dead:
                continue
            var tested_value: float
            if target_mode == TargetMode.NEAREST: 
                tested_value = ufo.get_global_position().distance_to(plant_pos)
                if tested_value <= attack_range:
                    if tested_value < best_value:
                        best_value = tested_value
                        best_ufo = ufo
                        victim = best_ufo
            else:
                tested_value = ufo.hp
                if tested_value > best_value:
                    best_value = tested_value
                    best_ufo = ufo
                    victim = best_ufo
            

func _process(delta):
    if growing:
        grow_value += delta / grow_time
        if grow_value > 1.0:
            grow_value = 1.0
            growing = false
            set_scale(Vector2(target_scale, target_scale))
        else:
            var new_scale = target_scale * (base_scale + grow_value * (1.0 - base_scale))
            set_scale(Vector2(new_scale, new_scale))
    elif dead:
        if target_attacker == null or target_attacker.dead:
            select_attacker()
        if target_attacker != null:
            velocity = target_attacker.get_global_position() - get_global_position()
            move_and_slide()
        var scale = get_scale().x
        scale *= (1.0 - delta)
        set_scale(Vector2(scale, scale))
        set_z_index(-30)
        rotate(delta * PI)
        if scale < 0.1:
            get_parent().remove_child(self)
            queue_free()
    else:
        if target_attacker == null or target_attacker.dead:
            alter_hp(delta * max_hp / 10.0)
            if constant_sound:
                $Coin.set_volume_db(-100)
        if victim != null and not victim.dead and constant_sound:
            $Coin.set_volume_db(-12)

func select_attacker():
    for attacker in attackers:
        #if !attacker.dead:
         target_attacker = attacker

func alter_hp(delta: float):
    hp += delta
    hp = min(hp, max_hp)
    set_position(Vector2(planted_position.x, planted_position.y - height * (1.0 - hp / max_hp)))

func deal_damage(damage: float, attacker: UFO):
    if dead:
        return
    alter_hp(-damage)
    if attacker != null:
        attackers.push_back(attacker)
    if hp < 0:
        dead = true

func is_ready():
    return !growing
