extends Control

## RTS 유닛 선택 및 이동을 통제하는 컨트롤러 (UnitController)
## 화면 전체를 덮는 Control 노드로 작동하여 드래그 박스를 렌더링하고 마우스 입력을 처리합니다.

# 마우스 및 드래그 상태 관리
var _is_dragging: bool = false
var _drag_start: Vector2 = Vector2.ZERO
var _drag_end: Vector2 = Vector2.ZERO

# 현재 선택된 유닛 리스트
var selected_units: Array[Node] = []

# 화면 드래그 박스 렌더링 색상
@export var box_color: Color = Color(0.0, 1.0, 0.0, 0.2) # 연녹색 반투명
@export var border_color: Color = Color(0.0, 1.0, 0.0, 0.8) # 녹색 테두리
@export var border_width: float = 1.5

func _ready() -> void:
	# 화면 전체를 덮도록 설정
	anchor_right = 1.0
	anchor_bottom = 1.0
	mouse_filter = MOUSE_FILTER_IGNORE # UI가 아닌 unhandled input으로 동작하게 통과 처리
	
	# 런타임에 NavigationRegion3D를 찾아 맵을 베이크시킴 (4.2 구현 보완)
	var nav_region: NavigationRegion3D = get_node_or_null("../NavigationRegion3D") as NavigationRegion3D
	if nav_region:
		nav_region.bake_navigation_mesh()
		print("UnitController: Navigation mesh baked successfully on start.")

func _unhandled_input(event: InputEvent) -> void:
	if GameManager.is_game_over:
		return
		
	var camera: Camera3D = get_viewport().get_camera_3d()
	if not camera:
		return
		
	# 마우스 좌클릭 (선택/드래그 시작)
	if event is InputEventMouseButton:
		var mouse_event: InputEventMouseButton = event as InputEventMouseButton
		
		# 마우스 왼쪽 버튼 클릭
		if mouse_event.button_index == MOUSE_BUTTON_LEFT:
			if mouse_event.is_pressed():
				_is_dragging = true
				_drag_start = mouse_event.position
				_drag_end = _drag_start
			else:
				# 클릭 해제 시 선택 처리
				_is_dragging = false
				_perform_selection(_drag_start, mouse_event.position)
				queue_redraw()
				
		# 마우스 오른쪽 버튼 클릭 (이동 및 공격 지시)
		elif mouse_event.button_index == MOUSE_BUTTON_RIGHT and mouse_event.is_pressed():
			_perform_action(mouse_event.position)

	# 마우스 드래그 중인 상태
	elif event is InputEventMouseMotion and _is_dragging:
		var motion_event: InputEventMouseMotion = event as InputEventMouseMotion
		_drag_end = motion_event.position
		queue_redraw()

func _draw() -> void:
	# 드래그 영역 상자 그리기
	if _is_dragging and _drag_start.distance_to(_drag_end) > 5.0:
		var rect: Rect2 = Rect2(_drag_start, _drag_end - _drag_start).abs()
		draw_rect(rect, box_color, true)
		draw_rect(rect, border_color, false, border_width)

## 다중 및 단일 선택 수행
func _perform_selection(start: Vector2, end: Vector2) -> void:
	var camera: Camera3D = get_viewport().get_camera_3d()
	if not camera:
		return
		
	# Shift 키 입력 체크
	var is_shift: bool = Input.is_key_pressed(KEY_SHIFT)
	
	# Shift가 눌려있지 않은 경우에만 기존 선택 유닛들 초기화
	if not is_shift:
		for unit: Node in selected_units:
			if is_instance_valid(unit) and unit.has_method("deselect"):
				unit.call("deselect")
		selected_units.clear()
	
	var drag_dist: float = start.distance_to(end)
	
	# 1. 단일 선택 처리 (Raycast 이용)
	if drag_dist <= 5.0:
		var ray_result: Dictionary = _raycast_from_mouse(start)
		if ray_result.has("collider"):
			var hit_collider: Node = ray_result["collider"] as Node
			var unit_node: Node = _get_unit_node(hit_collider)
			if unit_node and unit_node.is_in_group("units"):
				# Shift 추가 선택 모드 시 이미 선택 리스트에 없는 경우에만 누적
				if not selected_units.has(unit_node):
					selected_units.append(unit_node)
					if unit_node.has_method("select"):
						unit_node.call("select")
					print("UnitController: Selected single unit: ", unit_node.name)
				
	# 2. 드래그 다중 선택 처리 (Unproject 투영 이용)
	else:
		var selection_rect: Rect2 = Rect2(start, end - start).abs()
		var all_units: Array[Node] = get_tree().get_nodes_in_group("units")
		var newly_selected_count: int = 0
		
		for unit: Node in all_units:
			if is_instance_valid(unit) and unit is Node3D:
				var unit_3d: Node3D = unit as Node3D
				var screen_pos: Vector2 = camera.unproject_position(unit_3d.global_position)
				
				# 화면 뷰포트 영역 내부 및 드래그 사각형 안에 포함되는지 판정
				if selection_rect.has_point(screen_pos):
					# 중복 선택 추가 방지
					if not selected_units.has(unit):
						selected_units.append(unit)
						if unit.has_method("select"):
							unit.call("select")
						newly_selected_count += 1
		print("UnitController: Selected ", newly_selected_count, " new units via drag. Total: ", selected_units.size())

## 우클릭 액션 (이동 목적지 지정 또는 적 공격 타겟팅)
func _perform_action(mouse_pos: Vector2) -> void:
	if selected_units.is_empty():
		return
		
	# Raycast를 쏘아 바닥 또는 몬스터 감지
	var ray_result: Dictionary = _raycast_from_mouse(mouse_pos)
	if ray_result.is_empty():
		return
		
	var hit_pos: Vector3 = ray_result["position"]
	var hit_collider: Node = ray_result["collider"] as Node
	
	# 충돌 대상이 몬스터인지 검사
	var monster_node: Node = _get_monster_node(hit_collider)
	
	if monster_node and monster_node.is_in_group("monsters"):
		# 선택된 유닛들에게 공격 타겟 명령 하달
		for unit: Node in selected_units:
			if is_instance_valid(unit) and unit.has_method("attack_target"):
				unit.call("attack_target", monster_node)
		print("UnitController: Ordered units to attack monster: ", monster_node.name)
	else:
		# 바닥 또는 일반 지형인 경우 해당 좌표로 이동 명령 하달
		# 다중 유닛 이동 시 겹침 방지를 위해 약간의 분산(Offset)을 주면 좋습니다.
		var unit_count: int = selected_units.size()
		var spacing: float = 1.5
		var rows: int = int(ceil(sqrt(unit_count)))
		
		for i: int in range(unit_count):
			var unit: Node = selected_units[i]
			if is_instance_valid(unit) and unit.has_method("move_to"):
				# 격자 정렬 오프셋 계산
				var row: int = i / rows
				var col: int = i % rows
				var offset: Vector3 = Vector3(
					(col - (rows - 1) / 2.0) * spacing,
					0.0,
					(row - (rows - 1) / 2.0) * spacing
				)
				var target_destination: Vector3 = hit_pos + offset
				unit.call("move_to", target_destination)
				
		print("UnitController: Ordered ", unit_count, " units to move to ", hit_pos)

## 마우스 포인터 방향으로 3D Raycast 실행
func _raycast_from_mouse(mouse_pos: Vector2) -> Dictionary:
	var camera: Camera3D = get_viewport().get_camera_3d()
	if not camera:
		return {}
		
	var from: Vector3 = camera.project_ray_origin(mouse_pos)
	var to: Vector3 = from + camera.project_ray_normal(mouse_pos) * 1000.0
	
	var space_state: PhysicsDirectSpaceState3D = camera.get_world_3d().direct_space_state
	var query: PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(from, to)
	query.collide_with_areas = true
	query.collide_with_bodies = true
	
	return space_state.intersect_ray(query)

## 충돌체로부터 유닛 노드를 식별하여 반환
func _get_unit_node(collider: Node) -> Node:
	if not collider:
		return null
	if collider is BaseUnit:
		return collider
	var parent: Node = collider.get_parent()
	if parent and parent is BaseUnit:
		return parent
	var grandparent: Node = parent.get_parent() if parent else null
	if grandparent and grandparent is BaseUnit:
		return grandparent
	if collider.is_in_group("units"):
		return collider
	if parent and parent.is_in_group("units"):
		return parent
	if grandparent and grandparent.is_in_group("units"):
		return grandparent
	return null

## 충돌체로부터 몬스터 노드를 식별하여 반환
func _get_monster_node(collider: Node) -> Node:
	if not collider:
		return null
	if collider is BaseMonster:
		return collider
	var parent: Node = collider.get_parent()
	if parent and parent is BaseMonster:
		return parent
	var grandparent: Node = parent.get_parent() if parent else null
	if grandparent and grandparent is BaseMonster:
		return grandparent
	if collider.is_in_group("monsters"):
		return collider
	if parent and parent.is_in_group("monsters"):
		return parent
	if grandparent and grandparent.is_in_group("monsters"):
		return grandparent
	return null
