extends Node


# Signals to let lobby GUI know what's going on.
signal player_list_changed()
signal connection_failed()
signal connection_succeeded()
signal game_ended()
signal game_started()
signal game_error(what)

# Default game server port. Can be any number between 1024 and 49151.
# Not on the list of registered or common ports as of November 2020:
# https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
const DEFAULT_PORT = 10567

# Max number of players.
const MAX_PEERS = 4

var peer = null

# Name for my player.
var player_name = "The Warrior"

# Names for remote players in id:name format.
var players = {}
var players_ready = []


func _ready():
	get_tree().connect("network_peer_connected", self, "_player_connected")
	get_tree().connect("network_peer_disconnected", self,"_player_disconnected")
	get_tree().connect("connected_to_server", self, "_connected_ok")
	get_tree().connect("connection_failed", self, "_connected_fail")
	get_tree().connect("server_disconnected", self, "_server_disconnected")


# Callback from SceneTree.
func _player_connected(id):
	# Registration of a client beings here, tell the connected player that we are here.
	rpc_id(id, "register_player", player_name)


# Callback from SceneTree.
func _player_disconnected(id):
	##TODO: Need better way to determine a game is running
	if has_node("/root/Main/ViewportContainer/Viewport/1"): # Game is in progress.
		if get_tree().is_network_server():
			emit_signal("game_error", "Player " + players[id] + " disconnected")
			end_game()
	else: # Game is not in progress.
		# Unregister this player.
		unregister_player(id)


# Callback from SceneTree, only for clients (not server).
func _connected_ok():
	# We just connected to a server
	emit_signal("connection_succeeded")


# Callback from SceneTree, only for clients (not server).
func _server_disconnected():
	emit_signal("game_error", "Server disconnected")
	end_game()



# Callback from SceneTree, only for clients (not server).
func _connected_fail():
	get_tree().set_network_peer(null) # Remove peer
	emit_signal("connection_failed")

# Lobby management functions.

remote func register_player(new_player_name):
	var id = get_tree().get_rpc_sender_id()
	print(id)
	players[id] = new_player_name
	emit_signal("player_list_changed")


func unregister_player(id):
	players.erase(id)
	emit_signal("player_list_changed")

func instance_on_peer_players(_scene :Resource, _node_path :NodePath, _global_position :Vector2 , _as_toplevel = true):
#	print ("Scene Path: ", _scene.resource_path,
#		" Instance At: ", _node_path,
#		" Position : ", _global_position)
	
	## was going to create local instance here but, lets see if I don't have to.
	## makes me thing that node name and path must be very important for multiplayer
	rpc("load_scene_on_all_clients", _scene.resource_path, _node_path, _global_position, _as_toplevel)

## remote means only runs on peers, remotesync goes both here and in peers
## because I want the node I load in two steps
remote func load_scene_on_all_clients(_scene :String, _node_path :NodePath, _global_position :Vector2 , _as_toplevel = true):
	print ( "-- Remote Scene Call -- \n",
		" Scene Path: ", _scene,
		" Instance At: ", _node_path,
		" Position : ", _global_position)

	var new_scene = load(_scene).instance()
	var _caller_id = get_tree().get_rpc_sender_id()
	
	new_scene.set_network_master(_caller_id)
	new_scene.set_as_toplevel(_as_toplevel)
	new_scene.global_position = _global_position
	
	get_node(_node_path).add_child(new_scene)
	
#	for _player in players:
#		rpc_id(_player, "load_scene_on_all_clients", _scene, _node_path, _global_position, _as_toplevel )
	
#	if new_scene.get_network_master() == _caller_id:
#		return new_scene
#	else:
#		return null

remote func pre_start_game(spawn_points):
	# Change scene.
	var _game_starting_level_scene = get_tree().get_root().get_node("/root/Main").starting_level
	var _in_game_player_hud = get_tree().get_root().get_node("/root/Main").player_hud
	#var world :Node = load("res://src/levels/TestBox.tscn").instance()
	var world :Node = _game_starting_level_scene.instance()
	#var ui :Node = load("res://src/ui/HUD.tscn").instance()
	var ui :Node = _in_game_player_hud.instance()

	## No need to name world anymore
	#world.set_name("World")
	world.add_to_group('world')
	ui.add_to_group('world')

	get_tree().get_root().get_node("/root/Main/ViewportContainer/Viewport").add_child(world)
	get_tree().get_root().get_node("/root/Main/ViewportContainer/Viewport").add_child(ui)
	
	get_tree().get_root().get_node("/root/Main/ViewportContainer/Viewport/Lobby").hide()

	## Clear the PlayerInfo table in case this is a restart. 
	PlayerInfo.reset()
	var player_scene = load("res://src/actors/players/playerE/PlayerE.tscn")


	for p_id in spawn_points:
		#var spawn_pos = world.get_node("SpawnPoints/" + str(spawn_points[p_id])).position
		var player = player_scene.instance()

		player.set_name(str(p_id)) # Use unique ID as node name.
		#player.position=spawn_pos
		player.set_network_master(p_id) #set unique id as master.

		# We don't yet use a player name like a node yet
#		if p_id == get_tree().get_network_unique_id():
#			# If node for this peer id, set name.
#			player.set_player_name(player_name)
#		else:
#			# Otherwise set name from peer.
#			player.set_player_name(players[p_id])

		get_tree().get_root().get_node("/root/Main/ViewportContainer/Viewport").add_child(player)
		#world.get_node("Players").add_child(player)

#	# Set up score.
#	world.get_node("Score").add_player(get_tree().get_network_unique_id(), player_name)
#	for pn in players:
#		world.get_node("Score").add_player(pn, players[pn])

	if not get_tree().is_network_server():
		# Tell server we are ready to start.
		rpc_id(1, "ready_to_start", get_tree().get_network_unique_id())
	elif players.size() == 0:
		post_start_game()


remote func post_start_game():
	get_tree().set_pause(false) # Unpause and unleash the game!
	emit_signal("game_started")

remote func ready_to_start(id):
	assert(get_tree().is_network_server())

	if not id in players_ready:
		players_ready.append(id)

	if players_ready.size() == players.size():
		for p in players:
			rpc_id(p, "post_start_game")
		post_start_game()


func host_game(new_player_name):
	player_name = new_player_name
	peer = NetworkedMultiplayerENet.new()
	peer.create_server(DEFAULT_PORT, MAX_PEERS)
	get_tree().set_network_peer(peer)


func join_game(ip, new_player_name):
	player_name = new_player_name
	peer = NetworkedMultiplayerENet.new()
	peer.create_client(ip, DEFAULT_PORT)
	get_tree().set_network_peer(peer)


func get_player_list():
	return players.values()

func get_player_ids():
	return players.keys()

func get_player_name():
	return player_name


func begin_game():
	assert(get_tree().is_network_server())

	# Create a dictionary with peer id and respective spawn points, could be improved by randomizing.
	var spawn_points = {}
	spawn_points[1] = 0 # Server in spawn point 0.
	var spawn_point_idx = 1
	for p in players:
		spawn_points[p] = spawn_point_idx
		spawn_point_idx += 1
	# Call to pre-start game with the spawn points.
	for p in players:
		rpc_id(p, "pre_start_game", spawn_points)

	pre_start_game(spawn_points)

func end_game():
	rpc("end_clients_game")

remotesync func end_clients_game():
	get_tree().call_group("world","queue_free")
#	if has_node("/root/Main/ViewportContainer/Viewport/World"): # Game is in progress.
#		# End it
#		get_node("/root/Main/ViewportContainer/Viewport/World").queue_free()

	emit_signal("game_ended")
	players.clear()
