@tool
extends Node
class_name CameraPointer

const _CONSTANTS := preload("res://addons/reedcamera/_data/CameraSystemConst.gd")

## 相机指针所能持有的工具种类
enum ToolType{
	SHAKER,
	ANCHOR,
	FOLLOWER
}

## 相机工具和脚本的映射
const type_script_map:Dictionary = {
	ToolType.SHAKER : preload("res://addons/reedcamera/scripts/camera_tools/CameraShakeController.gd"),
	ToolType.ANCHOR : preload("res://addons/reedcamera/scripts/camera_tools/CameraAnchorController.gd"),
	ToolType.FOLLOWER : preload("res://addons/reedcamera/scripts/camera_tools/CameraFollowController.gd")
}

var _camera: Camera2D
var _editor_valid := false
var _runtime_registered := false
var _warned := false

var _final_offset := Vector2.ZERO
var _final_position: Vector2
var _final_zoom := Vector2.ONE

var _pos_dirty := false
var _offset_dirty := false
var _zoom_dirty := false


func _enter_tree() -> void:
	if Engine.is_editor_hint():
		call_deferred("_editor_verify")

func _exit_tree() -> void:
	if Engine.is_editor_hint():
		return

	_request_unregister()

func _process(delta: float) -> void:
	if Engine.is_editor_hint():
		return
	
	if not _camera:
		return
	
	_reset_dirty_flags()
	
	_update_final_position()
	_update_camera_offset()
	_update_camera_zoom()
	
	_apply_camera_config()

func _reset_dirty_flags() -> void:
	_pos_dirty = false
	_offset_dirty = false
	_zoom_dirty = false

func _update_final_position():
	for child in get_children():
		if child is CameraToolBasic \
		and child.enabled \
		and child.affect_position \
		and child.has_base_position():
			_final_position = child.get_base_position()
			_pos_dirty = true
			return

	# fallback
	_final_position = _camera.global_position

func _update_camera_offset():
	var offset := Vector2.ZERO
	var used := false

	for child in get_children():
		if child is CameraToolBasic \
		and child.enabled \
		and child.affect_offset \
		and child.has_camera_offset():
			offset += child.get_camera_offset()
			used = true

	_final_offset = offset
	_offset_dirty = used

func _update_camera_zoom():
	for child in get_children():
		if child is CameraToolBasic \
		and child.enabled \
		and child.affect_zoom \
		and child.has_camera_zoom():
			_final_zoom = child.get_camera_zoom()
			_zoom_dirty = true
			return
	
	# fallback
	_final_zoom = _camera.zoom

func _apply_camera_config():
	if _pos_dirty:
		_camera.global_position = _final_position

	if _offset_dirty:
		_camera.offset = _final_offset

	if _zoom_dirty:
		_camera.zoom = _final_zoom


func _notification(what: int) -> void:
	if not Engine.is_editor_hint():
		return
	
	match what:
		NOTIFICATION_PARENTED, NOTIFICATION_UNPARENTED:
			#print("重設parent")
			call_deferred("_editor_verify")

## 編輯器層面的通過性驗證
func _editor_verify() -> void:
	_camera = _find_camera()
	_editor_valid = _camera != null
	#print(_editor_valid)

	if not _editor_valid:
		_emit_config_warning()
	else:
		_warned = false

func _ready() -> void:
	if Engine.is_editor_hint():
		return
	
	if not _camera:
		_camera = _find_camera()

	_request_register()

func _find_camera() -> Camera2D:
	var p := get_parent()
	return p as Camera2D if p is Camera2D else null

func _request_register() -> void:
	var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME)
	if not sys:
		return

	_runtime_registered = sys.request_register_pointer(self)

func _request_unregister() -> void:
	if not _runtime_registered:
		return

	var sys := Engine.get_singleton(_CONSTANTS.CAMERA_SYSTEM_NAME)
	if sys:
		sys.request_unregister_pointer(self)

	_runtime_registered = false

func _emit_config_warning() -> void:
	if not Engine.is_editor_hint():
		return

	if _warned:
		return

	push_warning(
		"[CameraPointer] Invalid configuration: parent node must be Camera2D. "
		+ "Current parent: %s" % (get_parent() if get_parent() else "null")
	)

	_warned = true

## =========================
## Public API
## =========================
##获取相机
func get_camera() -> Camera2D:
	if not _camera:
		_camera = _find_camera()
	return _camera

##获取特定工具
func get_tool(tool_class: Script) -> Node:
	for c in get_children():
		if c.get_script() == tool_class:
			return c
	return null

##通过类型返回工具
func get_tool_by_type(type:ToolType) -> Node:
	return get_tool(type_script_map.get(type))

##获取所有工具
func get_tools() -> Array[CameraToolBasic]:
	var out: Array[CameraToolBasic] = []
	for c in get_children():
		if c is CameraToolBasic:
			out.append(c)
	return out
