extends CanvasLayer

# Bottom-right minimap: green translucent background, gray dots for every
# wall/asteroid tile, white dots for teammates, yellow dots for enemies.
# Drawing itself lives in mini_map_view.gd (a Control, since CanvasLayer
# can't _draw()) — this script only polls game state and feeds it in.

const MARGIN := 16.0
const WIDTH := 260.0
const HEIGHT := 180.0
const REFRESH_INTERVAL := 0.1  # ~10Hz is plenty for a minimap dot

const TEAM_COLOR := Color(1.0, 1.0, 1.0)
const ENEMY_COLOR := Color(0.95, 0.85, 0.25)
const SELF_COLOR := Color(0.3, 0.55, 1.0)

var _view: MiniMapView
var _refresh_timer: float = 0.0


func _ready() -> void:
	layer = 4
	_build_ui()
	# Deferred: MiniMap is a child of World and so (like every other World
	# child, see World.decide_offered_races()'s comment) has its own _ready()
	# fire *before* World._ready() — before the map has even been instanced
	# into MapContainer. Deferring runs this after the whole _ready cascade
	# finishes, once the map (and GameConfig.world_bounds) actually exist.
	call_deferred("_cache_hazard_points")


func _build_ui() -> void:
	_view = MiniMapView.new()
	_view.mouse_filter = Control.MOUSE_FILTER_IGNORE
	# Anchored fully to the bottom-right corner (all anchors 1.0), so offsets
	# are negative pixel distances from that corner that stay correct
	# regardless of window size — see structure.md's Display section on why
	# every anchor here must be set directly rather than via sequential
	# set_anchor_and_offset() calls (push_opposite_anchor footgun).
	_view.anchor_left = 1.0
	_view.anchor_right = 1.0
	_view.anchor_top = 1.0
	_view.anchor_bottom = 1.0
	_view.offset_left = -MARGIN - WIDTH
	_view.offset_right = -MARGIN
	_view.offset_top = -MARGIN - HEIGHT
	_view.offset_bottom = -MARGIN
	add_child(_view)


func _process(delta: float) -> void:
	_refresh_timer -= delta
	if _refresh_timer > 0.0:
		return
	_refresh_timer = REFRESH_INTERVAL
	_update_players()
	_view.queue_redraw()


func _cache_hazard_points() -> void:
	var map_container := get_node_or_null("/root/World/MapContainer")
	if not map_container:
		return
	var bounds := GameConfig.world_bounds
	var points := PackedVector2Array()
	for tile_layer in map_container.find_children("*", "TileMapLayer", true, false):
		for cell in tile_layer.get_used_cells():
			var world_pos: Vector2 = tile_layer.to_global(tile_layer.map_to_local(cell))
			points.append(_normalize(world_pos, bounds))
	_view.hazard_points = points
	_view.queue_redraw()


func _update_players() -> void:
	var players_node := get_node_or_null("/root/World/Players")
	if not players_node:
		return
	var bounds := GameConfig.world_bounds
	var local_id := PlayerRegistry.get_local_id()
	var local_race: int = PlayerRegistry.get_info(local_id).get("race", 0)

	var points: Array = []
	for peer_id in PlayerRegistry.players.keys():
		var ship := players_node.get_node_or_null(str(peer_id))
		if not ship or not ship.visible:
			continue
		var color: Color
		if peer_id == local_id:
			color = SELF_COLOR
		else:
			var info: Dictionary = PlayerRegistry.players[peer_id]
			var is_teammate: bool = info.get("race", 0) == local_race
			color = TEAM_COLOR if is_teammate else ENEMY_COLOR
		points.append({"pos": _normalize(ship.global_position, bounds), "color": color})
	_view.player_points = points


func _normalize(world_pos: Vector2, bounds: Rect2) -> Vector2:
	var frac := (world_pos - bounds.position) / bounds.size
	return frac.clamp(Vector2.ZERO, Vector2.ONE)
