@tool
@icon("uid://p0oxphym6oqg")
'''
	此类为一个level的最小单元，其可以承载任意多的prop和act
	你可以这样理解，一个level（大关卡），带有n个小关卡，n = scene的数量
	
	Scene 必须带有：
		1.SceneManager
		2.Act Manager
		3.复数个Prop
	
	其中： ActManager下，带有复数个Act，其主要用于记录各个Prop的不同state，当Act切换时，Prop对应的状态也会切换。
		ActManager的主要作用就是管理这些Act的切换。
		Prop可以简单的理解为场景中的非地形碰撞的，与玩家可交互的道具，AI，等。
		所有的Prop会自带一个PropComponent，其核心功能是负责管理Prop自身的state和state的切换
		
		举例：一个门，从State1 -> State2，state1是关闭状态，state2是打开状态，那么：
			1. 我们在State1中定义，门是关的，State2中定义，门是开的。简单的实现比如我们去设置门的坐标
			2. 我们会做一个State1 -> State2的流转函数，其定义了，State1->State2的具体行为，比如，播放一个门打开的动画。
			
	
	对于任意的添加到Scene的Prop，如果其自身的最上层子节点集中不含有PropComponent，Scene会默认的给他发一个PropComponent
	注意，我并不推荐在Prop上直接挂载PropComponent，通过Scene来添加更为合适。
	
	对于Scene，Scene通过PropComponent上的ID，通过ID获取到PropComponent所挂载的组件，这也是为什么我强制要求
	PropComponent必须是处于Prop的最上层子节点集。
	
	Scene自身也有SceneID，SceneID是实现跨Scene修改的核心，SceneID是插件内置的系统主动生成的，且理论上不允许修改，
	对于每个Scene来说，SceneID是唯一且绝对的，这个涉及到后续的异步加载问题，虽然2D游戏可能没有这些问题。
	
	比如说，我们可以让玩家在Scene1的时候，触发Scene9的一个ActChange，发射一个火焰流星雨，但是实际上这个东西是可以被在Scene的玩家所
	观察的。
'''

class_name ReedScene extends Node2D

## ==============================
## Const Config
## ==============================
##Act管理器的命名
const ACT_MANAGER_NAME := "ActManager"
##Prop的根节点命名
const PROPS_ROOT_NAME := "Props"
##场景管理器的根节点命名
const SCENE_MANAGER_NAME := "SceneManager"
##Prop的路徑
const PROPS_ROOT_PATH: NodePath = ^"Props"
##PropManager脚本的路徑
const PROPS_MANAGER_SCRIPT: Script = preload("res://addons/reedscene/prop/PropManager.gd")

## ==============================
## Export Config
## ==============================

##是否要自動的為Props下的Node添加PropComponent
@export var auto_attach_prop_component := true
##是否要打印DebugLog
@export var debug_log := false

## ==============================
## Internal State
## ==============================
var _scene_id_comp: ReedSceneID
var _act_manager: ActManager
var _props_root: Node
var _prop_map: Dictionary = {}   # prop_id -> PropComponent
var _scene_manager: SceneManager

## ==============================
## Build In
## ==============================
func _enter_tree() -> void:
	if Engine.is_editor_hint():
		_editor_ensure_scene_nodes() ##进入场景树，自动添加ActManager,PropsRoot,和SceneManager
		_editor_ensure_scene_id_comp() ##進入場景樹，則自動添加一個IDComp

func _notification(what):
	if what == NOTIFICATION_PARENTED:
		if get_parent() != null and not (get_parent() is ReedScene):
			push_warning("ReedSceneID must stay under ReedScene.")

func _ready() -> void:
	_resolve_scene_id_comp()
	_resolve_scene_manager()
	_resolve_act_manager()
	_resolve_props_root()
	_collect_props()
	_bind_act_events()
	
	if Engine.is_editor_hint():
		return
	
	## 初始化Prop
	var pcs : Array = _prop_map.values()
	for pc in pcs:
		pc.init()
	
	## act manger 切换一次
	_act_manager.switch_act_with_id(_act_manager.DEFAULT_ACT_ID if _act_manager.init_act_id < 0 else _act_manager.init_act_id)
	
	## 如果prop有复写init state的选项，则overwrite
	for pc in pcs:
		pc._init_state_check()
	ReedSceneRegistry.register_scene(self)

## ==============================
## Resolve References
## ==============================
func _resolve_scene_id_comp() -> void:
	_scene_id_comp = null
	for child in get_children():
		if child is ReedSceneID:
			_scene_id_comp = child
			break

	if _scene_id_comp == null:
		push_error("[ReedScene] ReedSceneID not found.")

func _resolve_act_manager() -> void:
	_act_manager = get_node_or_null("ActManager")
	if _act_manager == null:
		push_error("[ReedScene] ActManager not found.")
		return

func _resolve_props_root() -> void:
	_props_root = get_node_or_null(PROPS_ROOT_PATH)
	if _props_root == null:
		push_error("[ReedScene] Props root not found: %s" % PROPS_ROOT_PATH)

func _resolve_scene_manager() -> void:
	_scene_manager = get_node_or_null(SCENE_MANAGER_NAME)
	if _scene_manager == null:
		push_error("[ReedScene] SceneManager not found.")
## ==============================
## Prop Collection
## ==============================
func _collect_props() -> void:
	_prop_map.clear()

	if _props_root == null:
		push_warning("[ReedScene]:Scene[ID:%s] dont have a prop root" % _scene_id_comp.get_scene_id())
		return

	for prop in _props_root.get_children():
		if not prop is Node:
			continue

		var prop_comp := _find_prop_component(prop)
		if prop_comp == null:
			continue

		var prop_id := prop_comp.prop_id
		if prop_id < 0:
			push_warning("[ReedScene] Prop has invaild ID: %s" % prop.name)
			continue

		if _prop_map.has(prop_id):
			push_error("[ReedScene] Duplicate Prop ID: %s" % prop_id)
			continue

		_prop_map[prop_id] = prop_comp

		if debug_log:
			print("[ReedScene] Registered Prop:", prop_id)

##獲取到一個Prop的PropComp
func _find_prop_component(prop: Node) -> PropComponent:
	for child in prop.get_children():
		if child is PropComponent:
			return child
	return null

## ==============================
## Act Binding
## ==============================
func _bind_act_events() -> void:
	if _act_manager == null:
		return
	_act_manager.act_changed.connect(_on_act_changed)


func _on_act_changed(from_act: int, to_act: int) -> void:
	if debug_log:
		print("[ReedScene] Act changed:", from_act, "->", to_act)

##进入场景树，自动添加ActManager,PropsRoot,和SceneManager
func _editor_ensure_scene_nodes() -> void:
	_editor_ensure_node(SCENE_MANAGER_NAME, SceneManager)
	_editor_ensure_node(ACT_MANAGER_NAME, ActManager)
	_editor_ensure_node_from_script(PROPS_ROOT_NAME,PROPS_MANAGER_SCRIPT)

##添加命名和节点
func _editor_ensure_node(name: String, type: Variant) -> Node:
	var node := get_node_or_null(name)
	if node != null:
		return node

	node = type.new()
	node.name = name
	add_child(node)

	node.owner = get_tree().edited_scene_root

	if debug_log:
		print("[ReedScene][Editor] Created node:", name)

	return node

##通過脚本添加節點
func _editor_ensure_node_from_script(
	name: String,
	script: Script
) -> Node:
	assert(script != null)

	var node := get_node_or_null(name)
	if node != null:
		return node

	node = script.new()
	node.name = name
	add_child(node)

	node.owner = get_tree().edited_scene_root

	if debug_log:
		print("[ReedScene][Editor] Created node from script:", name)

	return node

##保證存在ID節點
func _editor_ensure_scene_id_comp() -> ReedSceneID:
	# 1️. 查找已有的 ReedSceneID（只找直接子节点）
	var id_comp: ReedSceneID = null
	for child in get_children():
		if child is ReedSceneID:
			id_comp = child
			break

	# 2️. 不存在就创建
	if id_comp == null:
		id_comp = ReedSceneID.new()
		id_comp.name = "SceneID"
		add_child(id_comp)

		# 关键：设 owner，才能被保存到场景
		id_comp.owner = get_tree().edited_scene_root

		if debug_log:
			print("[ReedScene][Editor] Created ReedSceneID")

	# 3️. 确保在子节点最上层（index = 0）
	if get_child(0) != id_comp:
		move_child(id_comp, 0)

	return id_comp

## ==============================
## Externel API
## ==============================

## 外部取得Prop映射對
func get_prop_map() -> Dictionary:
	return _prop_map

## 切換Act
func switch_act_by_id(act_id: int) -> void:
	if not _act_manager:
		push_warning("[ReedScene] ActManager requested before ready.")
	
	_act_manager.switch_act_with_id(act_id)

## 獲取關卡管理器
func get_scene_manager() -> SceneManager:
	if _scene_manager == null:
		push_warning("[ReedScene] SceneManager requested before ready.")
	return _scene_manager

## 獲取SceneID Comp
func get_scene_id_comp() -> ReedSceneID:
	if _scene_id_comp == null:
		push_warning("[ReedScene] Scene not has a ID Comp.")
	return _scene_id_comp

## 獲取Prop的根節點
func get_props_root() -> Node2D:
	return _props_root

## 獲取SceneID
func get_scene_id() -> int:
	if get_scene_id_comp().has_id():
		return get_scene_id_comp().scene_id
	
	return -1

## Prop的数量
func get_prop_count() -> int:
	return get_prop_map().values().size()
