extends Control

# Off-screen player indicators: show other players at the edge of the screen when outside camera.
# Uses the same colors as chat (player_colors by peer_id).

# Match chat_ui.gd player colors exactly
var _player_colors: Array = [
	Color.RED,
	Color.GREEN,
	Color.BLUE,
	Color.ORANGE,
	Color(0.5, 0.0, 0.5)  # Purple
]

const INDICATOR_RADIUS: float = 8.0
const EDGE_MARGIN: float = 24.0  # Inset from viewport edge so circles stay visible
const NAME_FONT_SIZE: int = 12
const NAME_OFFSET: float = 14.0  # Gap between circle and name

var _game_world: Node = null
var _camera: Camera2D = null
var _local_player_set: Dictionary = {}  # player instance id -> true
var _indicators: Array = []  # { position: Vector2, color: Color, name: String, direction: Vector2 }
var _font: Font = null

func _ready() -> void:
	call_deferred("_update_size")
	if get_viewport():
		get_viewport().size_changed.connect(_update_size)
	_game_world = get_tree().get_first_node_in_group("game_world")
	if _game_world and _game_world.has_node("Camera2D"):
		_camera = _game_world.get_node("Camera2D")
	_font = ThemeDB.fallback_font
	mouse_filter = Control.MOUSE_FILTER_IGNORE

func _update_size() -> void:
	var vp = get_viewport()
	if not vp:
		return
	var rect = vp.get_visible_rect()
	set_anchors_preset(Control.PRESET_FULL_RECT)
	# Use set_deferred for size-related properties to avoid anchor override warnings
	set_deferred("position", rect.position)
	set_deferred("size", rect.size)
	set_deferred("custom_minimum_size", rect.size)

func _process(_delta: float) -> void:
	_indicators.clear()
	if not _game_world or not _camera or not multiplayer or not multiplayer.has_multiplayer_peer():
		queue_redraw()
		return

	var vp = get_viewport()
	if not vp:
		return
	var vp_rect = vp.get_visible_rect()
	var vp_size = vp_rect.size
	var center = vp_rect.get_center()

	# Local players (don't show indicators for them)
	_local_player_set.clear()
	var local_players: Array = []
	if _game_world.has_node("PlayerManager"):
		var pm = _game_world.get_node("PlayerManager")
		if pm.has_method("get_local_players"):
			local_players = pm.get_local_players()
	for p in local_players:
		if is_instance_valid(p):
			_local_player_set[p.get_instance_id()] = true

	var zoom_x: float = _camera.zoom.x if _camera.zoom.x > 0.0 else 1.0

	for player in get_tree().get_nodes_in_group("player"):
		if not is_instance_valid(player):
			continue
		if _local_player_set.has(player.get_instance_id()):
			continue
		if "is_dead" in player and player.is_dead:
			continue
		if not player.has_method("get_multiplayer_authority"):
			continue

		var world_pos: Vector2 = player.global_position
		var screen_pos: Vector2 = (world_pos - _camera.global_position) * zoom_x + center

		# Check if on screen (with small margin)
		var m = EDGE_MARGIN
		if screen_pos.x >= m and screen_pos.x <= vp_size.x - m and screen_pos.y >= m and screen_pos.y <= vp_size.y - m:
			continue

		var edge_pos: Vector2 = _clamp_to_viewport_edge(center, screen_pos, vp_rect)
		var peer_id: int = player.get_multiplayer_authority()
		var col: Color = _player_colors[peer_id % _player_colors.size()]
		var dir: Vector2 = (edge_pos - center).normalized()
		var pname: String = player.name
		if player.get("character_stats") and player.character_stats:
			var cn: String = player.character_stats.character_name
			if not cn.is_empty():
				pname = cn
		_indicators.append({ "position": edge_pos, "color": col, "name": pname, "direction": dir })

	queue_redraw()

func _clamp_to_viewport_edge(center: Vector2, point: Vector2, vp_rect: Rect2) -> Vector2:
	var sz = vp_rect.size
	var mx = EDGE_MARGIN
	var cx = center.x
	var cy = center.y
	var px = point.x
	var py = point.y
	var dx = px - cx
	var dy = py - cy
	var len_sq = dx * dx + dy * dy
	if len_sq < 0.0001:
		return center
	var inv = 1.0 / sqrt(len_sq)
	var ux = dx * inv
	var uy = dy * inv

	# Intersect ray center + t * (ux, uy) with inset rect [mx, my] to [w-mx, h-mx]
	var w = sz.x
	var h = sz.y
	var t_min = INF
	var hit = center

	# Left edge x = mx
	if abs(ux) > 0.0001:
		var t = (mx - cx) / ux
		if t > 0:
			var y = cy + t * uy
			if y >= mx and y <= h - mx:
				if t < t_min:
					t_min = t
					hit = Vector2(mx, y)
	# Right edge x = w - mx
	if abs(ux) > 0.0001:
		var t = (w - mx - cx) / ux
		if t > 0:
			var y = cy + t * uy
			if y >= mx and y <= h - mx:
				if t < t_min:
					t_min = t
					hit = Vector2(w - mx, y)
	# Top edge y = mx
	if abs(uy) > 0.0001:
		var t = (mx - cy) / uy
		if t > 0:
			var x = cx + t * ux
			if x >= mx and x <= w - mx:
				if t < t_min:
					t_min = t
					hit = Vector2(x, mx)
	# Bottom edge y = h - mx
	if abs(uy) > 0.0001:
		var t = (h - mx - cy) / uy
		if t > 0:
			var x = cx + t * ux
			if x >= mx and x <= w - mx:
				if t < t_min:
					t_min = t
					hit = Vector2(x, h - mx)

	return hit

func _draw() -> void:
	for d in _indicators:
		var pos: Vector2 = d["position"]
		var col: Color = d["color"]
		var pname: String = d["name"]
		var dir: Vector2 = d["direction"]
		# Slight dark outline so visible on any background
		draw_circle(pos, INDICATOR_RADIUS + 1.0, Color(0.15, 0.15, 0.15, 0.7))
		draw_circle(pos, INDICATOR_RADIUS, col)
		# Player name inward from circle, same color as indicator
		if _font and not pname.is_empty():
			var text_anchor: Vector2 = pos - dir * (INDICATOR_RADIUS + NAME_OFFSET)
			var ts: Vector2 = _font.get_string_size(pname, HORIZONTAL_ALIGNMENT_LEFT, -1, NAME_FONT_SIZE)
			var base_y: float = text_anchor.y
			var text_pos: Vector2
			if dir.x >= 0:
				text_pos = Vector2(text_anchor.x - ts.x, base_y)
			else:
				text_pos = Vector2(text_anchor.x, base_y)
			draw_string_outline(_font, text_pos, pname, HORIZONTAL_ALIGNMENT_LEFT, -1, NAME_FONT_SIZE, 1, Color(0.1, 0.1, 0.1, 0.9))
			draw_string(_font, text_pos, pname, HORIZONTAL_ALIGNMENT_LEFT, -1, NAME_FONT_SIZE, col)
