extends CameraToolBasic
class_name ReedCameraAnchorController

const _CONSTANTS = preload("res://addons/reedcamera/_data/CameraSystemConst.gd")
## =========================
## Private
## =========================
var _anchors: Array[CameraAnchor] = []
var _current_anchor: CameraAnchor
var _current_position := Vector2.ZERO
var _current_zoom := Vector2.ONE
var _tween: Tween

## =========================
## Config
## =========================
@export var move_duration := 0.4
@export var ease_type := Tween.EASE_OUT
@export var trans_type := Tween.TRANS_CUBIC

var _evaluate_requested := false
var _evaluate_scheduled := false

var _sys : Object = null

enum AnchorState {
	IDLE,
	TRANSITIONING,
}

var _state := AnchorState.IDLE

## =========================
## Lifecycle
## =========================

func _ready() -> void:
	_anchors = _get_camera_system().get_all_anchors()

	_get_camera_system().anchors_changed.connect(_on_anchors_changed)

	_request_evaluate()

func _on_anchors_changed() -> void:
	_sync_anchors()
	_request_evaluate()

func _sync_anchors() -> void:
	_anchors = _get_camera_system().get_all_anchors()

func _request_evaluate() -> void:
	_evaluate_requested = true

	if _evaluate_scheduled:
		return

	_evaluate_scheduled = true
	call_deferred("_commit_evaluate")

func _commit_evaluate() -> void:
	_evaluate_scheduled = false

	if not _evaluate_requested:
		return

	_evaluate_requested = false
	_evaluate_impl()

func _evaluate_impl() -> void:
	var winner := _pick_best_anchor()
	if winner == _current_anchor:
		return

	_switch_to(winner)

func _pick_best_anchor() -> CameraAnchor:
	var best: CameraAnchor
	var best_p := -INF

	for a in _anchors:
		if not a.enabled:
			continue
		if a.priority > best_p:
			best = a
			best_p = a.priority

	return best

func _switch_to(anchor: CameraAnchor) -> void:
	if not anchor:
		return

	_current_anchor = anchor
	_state = AnchorState.TRANSITIONING

	var target := anchor.global_position
	var target_zoom := anchor.zoom if (anchor.zoom.x > 0 and anchor.zoom.y > 0) else Vector2.ONE

	if _tween and _tween.is_running():
		_tween.kill()

	if not anchor.use_blend or anchor.blend_time <= 0.0:
		_current_position = target
		_current_zoom = target_zoom
		_state = AnchorState.IDLE
		return

	_tween = get_tree().create_tween()
	_tween.set_ignore_time_scale(true)
	_tween.set_trans(trans_type)
	_tween.set_ease(ease_type)

	_tween.tween_property(self, "_current_position", target, anchor.blend_time)
	_tween.parallel().tween_property(self, "_current_zoom", target_zoom, anchor.blend_time)

	_tween.finished.connect(func():
		_state = AnchorState.IDLE
	)

func _get_camera_system() -> Object:
	if _sys:
		return _sys
	
	if Engine.has_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME):
		_sys = Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME)
	
	return _sys

## =========================
## CameraPointer Interface
## =========================
func get_base_position() -> Vector2:
	if _state != AnchorState.TRANSITIONING:
		return Vector2.ZERO
	return _current_position

func has_base_position() -> bool:
	return _state == AnchorState.TRANSITIONING

func get_camera_zoom() -> Vector2:
	if _state != AnchorState.TRANSITIONING:
		return Vector2.ONE
	return _current_zoom

func has_camera_zoom() -> bool:
	return _state == AnchorState.TRANSITIONING
