extends CanvasLayer

# Ingame HUD - Displays player health, level, time, and boss health

var label_life: Label = null
var progress_bar_hp: ProgressBar = null
var label_hp_value: Label = null
var vbox_mp: VBoxContainer = null
var progress_bar_mp: ProgressBar = null
var label_mp_value: Label = null
var label_keys: Label = null
var label_keys_value: Label = null
var label_level: Label = null
var label_level_value: Label = null
var label_time: Label = null
var label_time_value: Label = null
var label_boss: Label = null
var texture_progress_bar_boss_hp: TextureProgressBar = null
var label_host: Label = null
var label_player_count: Label = null
var label_room_code: Label = null
var label_disconnected: Label = null
var label_matchbox_status: Label = null
var label_ice_status: Label = null
var label_data_channels_status: Label = null

var game_world: Node = null
var network_manager: Node = null
var local_player: Node = null
var level_start_time: float = 0.0
var player_search_attempts: int = 0
var max_player_search_attempts: int = 100  # Limit retries to prevent infinite recursion
var timer_running: bool = true  # Flag to stop/start timer
const HUD_BASE_SIZE: Vector2 = Vector2(1280, 720)

func _ready():
	print("IngameHUD: _ready() called")
	
	# Find nodes safely (using get_node_or_null to avoid crashes)
	label_life = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerLIFE/LabelLife")
	label_keys = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerKeys/LabelKeys")
	label_keys_value = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerKeys/HBoxContainer/LabelKeysValue")
	label_level = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerLevel/LabelLevel")
	label_level_value = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerLevel/LabelLevelValue")
	label_time = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerTime/LabelTime")
	label_time_value = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerTime/LabelTimeValue")
	label_boss = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerBoss/LabelBoss")
	texture_progress_bar_boss_hp = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerBoss/TextureProgressBarBossHP")
	label_host = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerHost/LabelHost")
	label_player_count = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerHost/LabelPlayerCount")
	label_room_code = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerHost/LabelRoomCode")
	label_disconnected = get_node_or_null("CenterTop/LabelDisconnected")
	label_matchbox_status = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerConnectionStatus/LabelMatchboxStatus")
	label_ice_status = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerConnectionStatus/LabelICEStatus")
	label_data_channels_status = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerConnectionStatus/LabelDataChannelsStatus")
	
	# Debug: Log if connection status labels weren't found
	if not label_matchbox_status:
		print("IngameHUD: WARNING - label_matchbox_status not found!")
	if not label_ice_status:
		print("IngameHUD: WARNING - label_ice_status not found!")
	if not label_data_channels_status:
		print("IngameHUD: WARNING - label_data_channels_status not found!")
	
	# Find network manager
	network_manager = get_node_or_null("/root/NetworkManager")
	if network_manager:
		# Connect to player connection signals to update player count
		network_manager.player_connected.connect(_on_player_connected)
		network_manager.player_disconnected.connect(_on_player_disconnected)
		network_manager.connection_failed.connect(_on_connection_failed)
		network_manager.connection_succeeded.connect(_on_connection_succeeded)
	
	# Debug: Check if nodes were found
	if not label_time_value:
		print("IngameHUD: ERROR - label_time_value not found!")
	else:
		print("IngameHUD: Nodes found successfully")
	
	# Ensure visibility
	visible = true
	layer = 100  # High layer to ensure HUD is on top
	
	# Find game world
	game_world = get_tree().get_first_node_in_group("game_world")
	if not game_world:
		print("IngameHUD: WARNING - game_world not found in group")
	
	# Initially hide boss health bar
	if texture_progress_bar_boss_hp:
		texture_progress_bar_boss_hp.visible = false
	if label_boss:
		label_boss.visible = false
	
	# Update host info display
	_update_host_info()
	
	# Initially hide connection status (will be shown if WebRTC mode)
	var connection_status_container = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerConnectionStatus")
	if connection_status_container:
		connection_status_container.visible = false
	
	# Start level timer
	level_start_time = Time.get_ticks_msec() / 1000.0
	
	# Keep HUD text crisp with integer scaling
	_update_hud_scale()
	get_viewport().size_changed.connect(_update_hud_scale)
	
	# Find local player (with retry limit)
	player_search_attempts = 0
	_find_local_player()
	
	# Add HP value label (curr/max, like inventory) and MP bar
	_setup_hp_mp_ui()

func _on_player_connected(_peer_id: int, _player_info: Dictionary):
	_update_host_info()

func _on_player_disconnected(_peer_id: int, _player_info: Dictionary):
	_update_host_info()

func _on_connection_failed():
	# Show disconnection message
	if label_disconnected:
		label_disconnected.visible = true
		# Show different message for host vs joiner
		if network_manager and network_manager.is_hosting:
			label_disconnected.text = "Lost connection to Matchbox server - Retrying..."
		else:
			label_disconnected.text = "Disconnected - Reconnecting..."

func _on_connection_succeeded():
	# Hide disconnection message
	if label_disconnected:
		label_disconnected.visible = false

func _update_host_info():
	if not network_manager:
		return
	
	# Update HOST label visibility
	if label_host:
		label_host.visible = network_manager.is_hosting
	
	# Update player count
	if label_player_count and network_manager.players_info:
		var total_players = 0
		for peer_id in network_manager.players_info.keys():
			var info = network_manager.players_info[peer_id]
			total_players += info.get("local_player_count", 1)
		label_player_count.text = "Players: " + str(total_players)
	
	# Update room code (only show if WebRTC/WebSocket and hosting)
	if label_room_code:
		var mode = network_manager.network_mode
		if (mode == 1 or mode == 2) and network_manager.is_hosting:  # WebRTC or WebSocket
			var room_id = network_manager.get_room_id()
			if not room_id.is_empty():
				label_room_code.text = "Room: " + room_id
				label_room_code.visible = true
			else:
				label_room_code.visible = false
		else:
			label_room_code.visible = false

func _find_local_player():
	# Prevent infinite recursion
	player_search_attempts += 1
	if player_search_attempts > max_player_search_attempts:
		print("IngameHUD: Warning - Could not find local player after ", max_player_search_attempts, " attempts. HUD will continue without player data.")
		return
	
	# Find the local player (first player with authority)
	var players = get_tree().get_nodes_in_group("player")
	
	# Check if we're in multiplayer mode
	var is_multiplayer = multiplayer and multiplayer.has_multiplayer_peer()
	
	if is_multiplayer:
		# In multiplayer mode, find player with authority
		for player in players:
			if player.has_method("is_multiplayer_authority") and player.is_multiplayer_authority():
				local_player = player
				print("IngameHUD: Found local player")
				return
	else:
		# In single-player mode, use first player
		if players.size() > 0:
			local_player = players[0]
			print("IngameHUD: Found player (single-player mode)")
			return
	
	# If not found, try again later (but only if we haven't exceeded attempts)
	if not local_player and player_search_attempts <= max_player_search_attempts:
		call_deferred("_find_local_player")

func _process(_delta):
	# Only update if nodes exist
	if not label_time_value:
		return  # Nodes not initialized yet, skip updates
	
	# Update player health and MP
	if local_player and is_instance_valid(local_player):
		_update_player_health()
		_update_player_mp()
		_update_keys_display()
	
	# Update level display
	_update_level_display()
	
	# Update time display
	_update_time_display()
	
	# Update boss health (if boss exists)
	_update_boss_health()
	
	# Update connection status (only for WebRTC)
	_update_connection_status()

func _update_hud_scale():
	# Scale HUD to an integer factor to keep pixel text crisp
	var viewport_size = get_viewport().get_visible_rect().size
	var scale_factor = int(floor(min(viewport_size.x / HUD_BASE_SIZE.x, viewport_size.y / HUD_BASE_SIZE.y)))
	if scale_factor < 1:
		scale_factor = 1
	scale = Vector2.ONE * scale_factor

func _setup_hp_mp_ui() -> void:
	var life_vbox = get_node_or_null("UpperLeft/HBoxContainer/VBoxContainerLIFE")
	var hbox = get_node_or_null("UpperLeft/HBoxContainer")
	if not life_vbox or not hbox:
		return
	var old_heart_bar = life_vbox.get_node_or_null("TextureProgressBarHP")
	if old_heart_bar:
		life_vbox.remove_child(old_heart_bar)
		old_heart_bar.queue_free()
	# HP bar (inventory-style ProgressBar, same as inventory - no hearts)
	progress_bar_hp = ProgressBar.new()
	progress_bar_hp.custom_minimum_size = Vector2(100, 12)
	progress_bar_hp.show_percentage = false
	var bg_hp = StyleBoxFlat.new()
	bg_hp.bg_color = Color(0.2, 0.2, 0.2, 0.8)
	bg_hp.set_border_width_all(1)
	bg_hp.border_color = Color(0.4, 0.4, 0.4)
	progress_bar_hp.add_theme_stylebox_override("background", bg_hp)
	var fill_hp = StyleBoxFlat.new()
	fill_hp.bg_color = Color(0.85, 0.2, 0.2)
	progress_bar_hp.add_theme_stylebox_override("fill", fill_hp)
	life_vbox.add_child(progress_bar_hp)
	# HP value label (curr/max, like inventory)
	label_hp_value = Label.new()
	label_hp_value.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
		var fr = load("res://assets/fonts/standard_font.png")
		if fr:
			label_hp_value.add_theme_font_override("font", fr)
	label_hp_value.add_theme_font_size_override("font_size", 10)
	label_hp_value.text = "0/0"
	life_vbox.add_child(label_hp_value)
	# MP VBox (label + bar + value)
	vbox_mp = VBoxContainer.new()
	vbox_mp.name = "VBoxContainerMP"
	vbox_mp.size_flags_horizontal = Control.SIZE_EXPAND_FILL
	var mp_title = Label.new()
	mp_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
		var fr = load("res://assets/fonts/standard_font.png")
		if fr:
			mp_title.add_theme_font_override("font", fr)
	mp_title.add_theme_font_size_override("font_size", 10)
	mp_title.text = "MP"
	vbox_mp.add_child(mp_title)
	progress_bar_mp = ProgressBar.new()
	progress_bar_mp.custom_minimum_size = Vector2(100, 12)
	progress_bar_mp.show_percentage = false
	var bg = StyleBoxFlat.new()
	bg.bg_color = Color(0.2, 0.2, 0.2, 0.8)
	bg.set_border_width_all(1)
	bg.border_color = Color(0.4, 0.4, 0.4)
	progress_bar_mp.add_theme_stylebox_override("background", bg)
	var fill = StyleBoxFlat.new()
	fill.bg_color = Color(0.25, 0.45, 0.9)
	progress_bar_mp.add_theme_stylebox_override("fill", fill)
	vbox_mp.add_child(progress_bar_mp)
	label_mp_value = Label.new()
	label_mp_value.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
	if ResourceLoader.exists("res://assets/fonts/standard_font.png"):
		var fr = load("res://assets/fonts/standard_font.png")
		if fr:
			label_mp_value.add_theme_font_override("font", fr)
	label_mp_value.add_theme_font_size_override("font_size", 10)
	label_mp_value.text = "0/0"
	vbox_mp.add_child(label_mp_value)
	hbox.add_child(vbox_mp)
	hbox.move_child(vbox_mp, 1)

func _update_player_health():
	if not local_player or not progress_bar_hp:
		return
	
	var health = 0.0
	var max_health = 100.0
	
	if local_player.character_stats:
		health = local_player.character_stats.hp
		max_health = local_player.character_stats.maxhp
	else:
		health = local_player.current_health
		max_health = local_player.max_health
	
	progress_bar_hp.max_value = max(1.0, max_health)
	progress_bar_hp.value = health
	if label_hp_value:
		label_hp_value.text = str(int(health)) + "/" + str(int(max_health))
	
	# Race label: "- DWARF -" / "- ELF -" / "- WIZARD -" (Human -> Wizard)
	if label_life and local_player.character_stats:
		var r = local_player.character_stats.race
		var t = "- "
		if r == "Dwarf":
			t += "DWARF"
		elif r == "Elf":
			t += "ELF"
		elif r == "Human":
			t += "WIZARD"
		else:
			t += "LIFE"
		label_life.text = t + " -"

func _update_player_mp() -> void:
	if not local_player or not progress_bar_mp or not label_mp_value:
		return
	if not local_player.character_stats:
		return
	var mp = local_player.character_stats.mp
	var max_mp = local_player.character_stats.maxmp
	progress_bar_mp.max_value = max(1.0, max_mp)
	progress_bar_mp.value = mp
	label_mp_value.text = str(int(mp)) + "/" + str(int(max_mp))

func _update_keys_display():
	if not local_player or not label_keys_value:
		return
	
	# Get key count from player (keys property always exists in player.gd)
	var key_count = local_player.keys
	label_keys_value.text = str(key_count)

func _update_level_display():
	if not label_level_value:
		return
	
	# current_level is always defined in game_world.gd
	if game_world:
		var level = game_world.current_level
		label_level_value.text = str(level)

func _update_time_display():
	if not label_time_value:
		return
	
	# Only update if timer is running
	if not timer_running:
		return
	
	# Calculate elapsed time since level start
	var current_time = Time.get_ticks_msec() / 1000.0
	var elapsed_time = current_time - level_start_time
	
	# Format as MM:SS
	var minutes = int(elapsed_time / 60)
	var seconds = int(elapsed_time) % 60
	label_time_value.text = "%02d:%02d" % [minutes, seconds]

func get_level_time() -> float:
	# Get the current level time (even if timer is stopped)
	var current_time = Time.get_ticks_msec() / 1000.0
	return current_time - level_start_time

func stop_timer():
	# Stop the timer when level completes
	timer_running = false

func start_timer():
	# Start/reset the timer for new level
	timer_running = true
	level_start_time = Time.get_ticks_msec() / 1000.0

func _update_boss_health():
	# Find boss enemy (if any)
	var boss_enemy = null
	var enemies = get_tree().get_nodes_in_group("enemy")
	for enemy in enemies:
		# Check if enemy is a boss (could check metadata or name)
		if enemy.has_meta("is_boss") and enemy.get_meta("is_boss"):
			boss_enemy = enemy
			break
	
	if boss_enemy and is_instance_valid(boss_enemy):
		# Show boss health bar
		if texture_progress_bar_boss_hp:
			texture_progress_bar_boss_hp.visible = true
		if label_boss:
			label_boss.visible = true
		
		# Update boss health (properties are always defined in enemy_base.gd)
		var health = boss_enemy.current_health
		var max_health = boss_enemy.max_health
		
		if texture_progress_bar_boss_hp:
			texture_progress_bar_boss_hp.max_value = max_health
			texture_progress_bar_boss_hp.value = health
	else:
		# Hide boss health bar if no boss
		if texture_progress_bar_boss_hp:
			texture_progress_bar_boss_hp.visible = false
		if label_boss:
			label_boss.visible = false

func reset_level_timer():
	# Reset timer when starting a new level
	start_timer()

func _update_connection_status():
	# Only show connection status for WebRTC mode
	var connection_status_container = get_node_or_null("UpperRight/HBoxContainer/VBoxContainerConnectionStatus")
	if not network_manager:
		if connection_status_container:
			connection_status_container.visible = false
		return
	
	if network_manager.network_mode != 1:  # Not WebRTC
		if connection_status_container:
			connection_status_container.visible = false
		return
	
	# Show connection status container and labels (for both host and joiner)
	if connection_status_container:
		connection_status_container.visible = true
	else:
		# Debug: Log if container not found
		if not has_meta("connection_status_container_warned"):
			set_meta("connection_status_container_warned", true)
			print("IngameHUD: WARNING - connection_status_container not found!")
	
	if label_matchbox_status:
		label_matchbox_status.visible = true
	else:
		if not has_meta("label_matchbox_status_warned"):
			set_meta("label_matchbox_status_warned", true)
			print("IngameHUD: WARNING - label_matchbox_status not found!")
	
	if label_ice_status:
		label_ice_status.visible = true
	else:
		if not has_meta("label_ice_status_warned"):
			set_meta("label_ice_status_warned", true)
			print("IngameHUD: WARNING - label_ice_status not found!")
	
	if label_data_channels_status:
		label_data_channels_status.visible = true
	else:
		if not has_meta("label_data_channels_status_warned"):
			set_meta("label_data_channels_status_warned", true)
			print("IngameHUD: WARNING - label_data_channels_status not found!")
	
	# Update Matchbox connection status
	if network_manager.matchbox_client and label_matchbox_status:
		var is_net_connected = network_manager.matchbox_client.is_matchbox_connected()
		if is_net_connected:
			label_matchbox_status.text = "Matchbox: Connected"
			label_matchbox_status.modulate = Color.GREEN
		else:
			label_matchbox_status.text = "Matchbox: Disconnected"
			label_matchbox_status.modulate = Color.RED
	
	# Update ICE connection status
	if network_manager.matchbox_client and label_ice_status:
		var ice_status = network_manager.matchbox_client.get_ice_connection_status()
		if ice_status.is_empty():
			label_ice_status.text = "ICE: No peers"
			label_ice_status.modulate = Color.YELLOW
		else:
			# Check if any peer has ICE connected
			# For WebRTC, we consider it "connected" if:
			# 1. Connection state is CONNECTED (2), OR
			# 2. Signaling is STABLE (0) - offer/answer exchange complete (even if ICE still finalizing)
			# This is more lenient because WebRTC in browsers can have different timing than native builds
			var any_connected = false
			var any_establishing = false
			for peer_id in ice_status.keys():
				var peer_status = ice_status[peer_id]
				var conn_state = peer_status.get("connection_state", -1)
				var sig_state = peer_status.get("signaling_state", -1)
				
				# Connection is fully connected
				if conn_state == 2:  # CONNECTED
					any_connected = true
					break
				# Signaling is STABLE (0) - offer/answer exchange is complete
				# Even if ICE is still CONNECTING, the connection is essentially established
				# This matches native behavior where everything works fine
				elif sig_state == 0:  # STABLE signaling means connection is ready
					any_establishing = true
			
			if any_connected or any_establishing:
				label_ice_status.text = "ICE: Connected"
				label_ice_status.modulate = Color.GREEN
			else:
				# Check connection state and ICE candidate exchange
				var connecting = false
				var candidate_info = ""
				for peer_id in ice_status.keys():
					var conn_state = ice_status[peer_id].get("connection_state", -1)
					if conn_state == 1:  # CONNECTING
						connecting = true
						# Show ICE candidate counts to help debug
						var sent = ice_status[peer_id].get("candidates_sent", 0)
						var received = ice_status[peer_id].get("candidates_received", 0)
						if sent > 0 or received > 0:
							candidate_info = " (sent: " + str(sent) + ", recv: " + str(received) + ")"
						break
				
				if connecting:
					label_ice_status.text = "ICE: Connecting..." + candidate_info
					label_ice_status.modulate = Color.YELLOW
				else:
					label_ice_status.text = "ICE: Disconnected"
					label_ice_status.modulate = Color.RED
	
	# Update data channels status
	if network_manager.matchbox_client and label_data_channels_status:
		var channels_connected = network_manager.matchbox_client.are_data_channels_connected()
		if channels_connected:
			label_data_channels_status.text = "Data: Connected"
			label_data_channels_status.modulate = Color.GREEN
		else:
			label_data_channels_status.text = "Data: Disconnected"
			label_data_channels_status.modulate = Color.RED
