extends RefCounted

# Perception and hostility are separate from the attack timeline.
var state := "undetected"
var visible := false
var last_known_position := Vector2.ZERO
var visible_time := 0.0
var unseen_time := 0.0
var state_cooldown_remaining := 0.0
var reentry_remaining := 0.0
var settings: Dictionary = {}

func configure(definition: Dictionary) -> void:
	settings = definition.duplicate(true)
	state = "undetected"
	visible = false
	visible_time = 0.0
	unseen_time = 0.0
	state_cooldown_remaining = 0.0
	reentry_remaining = 0.0
	last_known_position = Vector2.ZERO

func update(delta: float, perceived: bool, position: Vector2, busy: bool = false, target_alive: bool = true) -> String:
	delta = maxf(0.0, delta)
	state_cooldown_remaining = maxf(0.0, state_cooldown_remaining - delta)
	reentry_remaining = maxf(0.0, reentry_remaining - delta)
	visible = perceived and target_alive
	if not target_alive:
		visible_time = 0.0
		unseen_time = 0.0
		_change("undetected")
		return state
	if visible:
		last_known_position = position
		visible_time += delta
		unseen_time = 0.0
	else:
		visible_time = 0.0
		unseen_time += delta
	if state == "aggroed":
		# Finish a committed telegraph before forgetting its target.
		if not visible and not busy and state_cooldown_remaining <= 0.0 and unseen_time >= float(settings.get("aggro_memory_duration", 3.0)):
			_change("undetected")
			reentry_remaining = float(settings.get("aggro_reentry_cooldown", 1.0))
	elif visible and bool(settings.get("aggro_on_detection", true)) and reentry_remaining <= 0.0 and visible_time >= float(settings.get("aggro_detection_delay", 0.0)):
		_change("aggroed")
	elif state_cooldown_remaining <= 0.0:
		_change("detected" if visible else "undetected")
	return state

func provoke(position: Vector2, force: bool = false) -> bool:
	if not force and not bool(settings.get("aggro_on_damage", true)):
		return false
	last_known_position = position
	unseen_time = 0.0
	reentry_remaining = 0.0
	_change("aggroed") # Damage is an urgent stimulus, not an ordinary transition.
	return true

func _change(next: String) -> void:
	if next == state:
		return
	state = next
	state_cooldown_remaining = float(settings.get("state_cooldown", 0.25))
