'''此类为Prop的管理组件类，任何的Prop，其至少需要在此类的最上层的子层添加该组件
	该管理器，在添加入Tree时，会自动的添加一个States节点作为其所有state的root，
	并且添加一个默认的state作为其子节点。
'''

@tool
@icon("res://addons/reedscene/prop/icon/prop_icon.svg")
class_name PropComponent extends Node

## ==============================
## Export
## ==============================

##此物件的描述ID，无法主动修改，由PropManager发信
@export_custom(PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY) var prop_id: int  = -1
##初始的默认state_id
@export var initial_state_id: int = 0
##如果为真，则无论ActManger的配置为任何，在Inital的时候都会使用自己定义的initial state
@export var overwrite_init_state: bool = false
##是否需要输出错误
@export var debug_log := false
##是否等待
@export var should_wait_owner_ready :bool = true
##状态切换的信号
signal state_changed(from_state: int, to_state: int, ctx: Dictionary)

##state的根节点，所有的state都需要连在根节点上
var _states_root: Node
var _state_map: Dictionary = {}
var _current: ReedPropState = null

const STATES_ROOT_NAME := "States"
const STATES_ROOT_SCRIPT := preload("res://addons/reedscene/prop/StateManager.gd")
const DEFAULT_STATE_ID := 0


func _enter_tree() -> void:
	if Engine.is_editor_hint():
		_editor_ensure_states_root()

func _ready() -> void:
	if Engine.is_editor_hint():
		_editor_ready()
	
	_build_state_cache()

## 只在編輯器模式的Ready裏Call的函數
func _editor_ready() -> void:
	child_exiting_tree.connect(_on_child_exiting_tree)

## 用于初始化状态
func init() -> void:
	_init_states()

## 用于检查初始状态复写的Check
func _init_state_check() ->void:
	if _current == null and initial_state_id >= 0 and overwrite_init_state:
		change_state(initial_state_id, false, {
			"reason": "INIT",
			"instant": true
		})

## 在構造時，映射State到stateID
func _build_state_cache() -> void:
	_state_map.clear()

	_states_root = get_node_or_null(STATES_ROOT_NAME)
	if _states_root == null:
		push_warning("[PropComponent:%s] States root not found" % prop_id)
		return
	
	## 判斷子節點是否是StatesRoot節點，此節點的脚本後續會輕量化，所以不設置為一個單獨的類
	if not _states_root.IS_PROP_STATES_ROOT:
		push_error("[PropComponent:%s] States root missing PropStatesRoot script" % prop_id)
		return

	for c in _states_root.get_children():
		if c is ReedPropState:
			var s := c as ReedPropState
			if _state_map.has(s.state_id):
				push_error("[PropComponent:%s] Duplicate state_id: %d"
					% [prop_id, s.state_id])
				continue
			_state_map[s.state_id] = s

	if debug_log:
		print("[PropComponent:%s] States:", _state_map.keys())

##初始化状态
func _init_states() -> void:
	var owner := get_parent()
	if owner == null:
		push_error("[PropComponent:%s] Owner node not found." % prop_id)
		return

	for s in _state_map.values():
		var ok :bool = s.init(owner, self)
		if not ok:
			push_error("[PropComponent:%s] State init failed: %s"
				% [prop_id, s.name])

## 引擎層預知一個工具節點給予到State及其子節點。
func _editor_ensure_states_root() -> void:
	var states := get_node_or_null(STATES_ROOT_NAME)
	if states:
		return

	states = Node.new()
	states.name = STATES_ROOT_NAME
	states.set_script(STATES_ROOT_SCRIPT)
	add_child(states)
	states.owner = get_tree().edited_scene_root

	var default_state := ReedPropState.new()
	default_state.name = "Default"
	states.add_child(default_state)
	default_state.owner = get_tree().edited_scene_root

## 不允许StatesNode被删除，如果删了会自动补一个
func _on_child_exiting_tree(child: Node) -> void:
	if child.name != STATES_ROOT_NAME:
		return
	# 不要立刻报错，先延后一帧再确认
	call_deferred("_editor_verify_states_root_missing")

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

	# 如果下一帧 States 还在，说明只是编辑器重建/移动过程，不算删除
	if get_node_or_null(STATES_ROOT_NAME) != null:
		return

	# 到这里才算真的没了
	push_error("[PropComponent] 'States' node is required and cannot be removed.")
	_editor_ensure_states_root()
## ==============================
## External API
## ==============================
#region 外部接口函数
## 使用PropComp獲取PropName，直接返回ParentName
func get_prop_name() -> String:
	return get_parent().name

## 獲取StatesRoot，返回緩存的StatesRoot
func get_states_root() -> Node:
	return _states_root

## 通過ID獲取PropState
func get_state_by_id(id: int) -> ReedPropState:
	return _state_map.get(id)

## 返回State的Size
func get_state_size() -> int:
	return _state_map.keys().size()

## 返回所有的States
func get_all_states() -> Array[ReedPropState]:
	return _state_map.values()

##检查是否存在Id的State
func has_state(state_id: int) -> bool:
	return _state_map.has(state_id)

##获取当前的状态ID
func get_current_state_id() -> int:
	return _current.state_id if _current else -1

## 切换状态，如果 use_trans 为 true，则会优先检查 next state 下是否存在可用的 Transition
func change_state(state_id: int, use_trans: bool, ctx: Dictionary = {}) -> bool:
	if not _state_map.has(state_id):
		push_warning("[PropComponent:%s] State not found: %d" % [prop_id, state_id])
		return false

	var next: ReedPropState = _state_map[state_id]

	if _current == next:
		return true

	if not next.can_enter(_current, ctx):
		if debug_log:
			print("[PropComponent:%s] can_enter rejected: %d" % [prop_id, state_id])
		return false

	var from_state_id := get_current_state_id()
	var prev := _current

	# ---------- EXIT ----------
	if _current:
		_current.on_exit(next, ctx)

	# ---------- SWITCH CURRENT ----------
	_current = next

	# ---------- TRANSITION CHECK ----------
	var transition_handled := false

	if use_trans:
		for child in _current.get_children():
			if child is ReedTransition:
				if child.can_trigger(prev, ctx):
					if debug_log:
						print("[PropComponent:%s] Transition triggered: %s"
							% [prop_id, child.name])

					transition_handled = child.execute(prev, _current, ctx)
					if transition_handled:
						break

	# ---------- ENTER ----------
	if not transition_handled:
		_current.on_enter(prev, ctx)
	else:
		if debug_log:
			print("[PropComponent:%s] StateEnter skipped by Transition: %d"
				% [prop_id, state_id])

	if debug_log:
		print("[PropComponent:%s] State %d -> %d | ctx=%s"
			% [prop_id, from_state_id, state_id, ctx])

	return true

## 获取ID的State的Name
func get_state_name_by_id(id: int) -> String:
	var s : ReedPropState = get_state_by_id(id)
	if not s:
		return ""
	return get_state_name(s)

## 获取State的Name
func get_state_name(state: ReedPropState) -> String:
	if not state:
		return ""
	
	var n: String = state.name
	var extract_raw_name := func(name: String) -> String:
		return (
			name.substr(name.find("]") + 2)
			if name.begins_with("[ID:")
			and name.find("]") != -1
			and name.find("]") + 2 < name.length()
			else name
		)

	return extract_raw_name.call(n)

#endregion
