## Client Main — Client entry point for testing.
##
## Connects to the server. For the local player, spawns a full FPS
## character (from the ChaffGames FPS template) with mouse look,
## weapons, HUD, and movement. For remote players, creates simple
## box-mesh representations for visibility.
##
## The local FPS character moves independently (single-player style).
## Position is replicated to the server each tick so other clients
## see where we are.

extends Node3D

# ---------------------------------------------------------------------------
# Exports
# ---------------------------------------------------------------------------
@export var server_host: String = "68.202.6.107"
@export var server_port: int = 34197
@export var fps_scene: PackedScene = preload("res://client/template/player_character.tscn")

# ---------------------------------------------------------------------------
# State
# ---------------------------------------------------------------------------
# remote_players[peer_id] = Node3D — visual representation of other players
var remote_players: Dictionary = {}
var connected: bool = false

# Our local FPS character
var _local_player: Node = null

# Position sync timer
var _sync_timer: float = 0.0
const SYNC_RATE: float = 20.0  # Hz — how often to send position to server

# ---------------------------------------------------------------------------
# Lifecycle
# ---------------------------------------------------------------------------
func _ready() -> void:
	# Connect to server after a short delay
	await get_tree().create_timer(0.5).timeout
	_connect_to_server()

func _connect_to_server() -> void:
	if OS.has_environment("SERVER_HOST"):
		server_host = OS.get_environment("SERVER_HOST")
	if OS.has_environment("SERVER_PORT"):
		server_port = int(OS.get_environment("SERVER_PORT"))
	
	print("[ClientMain] Connecting to %s:%d ..." % [server_host, server_port])
	
	var err: Error = NetworkManager.join_server(server_host, server_port)
	if err != OK:
		push_error("[ClientMain] Connection failed: %s" % error_string(err))
		await get_tree().create_timer(2.0).timeout
		_connect_to_server()
		return
	
	connected = true
	
	# Connect replication signals from NetworkManager
	NetworkManager.remote_player_spawned.connect(_on_remote_player_spawned)
	NetworkManager.remote_player_despawned.connect(_on_remote_player_despawned)
	
	print("[ClientMain] Connected to server. Peer ID: %d" % multiplayer.get_unique_id())

# ---------------------------------------------------------------------------
# Local FPS character + position sync
# ---------------------------------------------------------------------------
func _process(delta: float) -> void:
	# Sync our position to the server at a fixed rate
	if _local_player:
		_sync_timer += delta
		if _sync_timer >= 1.0 / SYNC_RATE:
			_sync_timer = 0.0
			var pos: Vector3 = _local_player.global_position
			rpc_id(1, "_send_position", pos)

@rpc("unreliable")
func _send_position(pos: Vector3) -> void:
	# Server receives our position and broadcasts to other clients
	pass

# Server broadcasts other players' positions to us
@rpc("unreliable", "authority")
func _replicate_position(pos: Vector3, moving_peer_id: int) -> void:
	# Update the position of a remote player on our screen
	if moving_peer_id in remote_players:
		remote_players[moving_peer_id].position = pos

# ---------------------------------------------------------------------------
# Player replication handlers (called when server broadcasts via RPC)
# ---------------------------------------------------------------------------
func _on_remote_player_spawned(peer_id: int, pos: Vector3) -> void:
	if peer_id == multiplayer.get_unique_id():
		# THIS IS OUR PLAYER — spawn the full FPS character
		if _local_player:
			push_warning("[ClientMain] Local player already exists, despawning old")
			_local_player.queue_free()
		
		_local_player = fps_scene.instantiate()
		_local_player.name = "LocalPlayer"
		_local_player.global_position = pos
		add_child(_local_player, true)
		
		print("[ClientMain] Spawned LOCAL FPS player at (%.1f, %.1f)" % [pos.x, pos.z])
		return
	
	if peer_id in remote_players:
		push_warning("[ClientMain] Remote player %d already exists, skipping" % peer_id)
		return
	
	# Create a remote player node for visualization (simple box mesh)
	var player := Node3D.new()
	player.name = "RemotePlayer_%d" % peer_id
	player.set_multiplayer_authority(peer_id)
	player.position = pos
	
	# Add a simple box mesh so we can see where other players are
	var mesh := MeshInstance3D.new()
	mesh.mesh = BoxMesh.new()
	mesh.mesh.size = Vector3(1, 1, 1)
	mesh.position.y = 0.5
	player.add_child(mesh)
	
	add_child(player, true)
	remote_players[peer_id] = player
	
	print("[ClientMain] Spawned remote player %d at (%.1f, %.1f)" % [peer_id, pos.x, pos.z])

func _on_remote_player_despawned(peer_id: int) -> void:
	if peer_id == multiplayer.get_unique_id():
		if _local_player:
			_local_player.queue_free()
			_local_player = null
			print("[ClientMain] Local player despawned")
		return
	
	if peer_id in remote_players:
		remote_players[peer_id].queue_free()
		remote_players.erase(peer_id)
		print("[ClientMain] Despawned remote player %d" % peer_id)

func _exit_tree() -> void:
	if connected:
		NetworkManager.stop()
	
	if _local_player:
		_local_player.queue_free()
	for p in remote_players.values():
		p.queue_free()
	remote_players.clear()
