class_name Server

extends Node


#region Constants

const DEFAULT_PORT = 10567
const MAX_CLIENTS  = 3

#endregion


#region Onready Variables

# @onready var microphone : AudioStreamPlayer3D = $Microphone

#endregion


#region Variables

var players_ready : Array = []

var peer : MultiplayerPeer = null

#var mic_capture : AudioEffectOpusChunked

var lobby_id : int = -1

#endregion


#region RPC

# @rpc("any_peer", "unreliable")
# func voice_packet_recieved(packet):
#     var sender_id = multiplayer.get_remote_sender_id()
	# push_opus_packet_to_player.emit(sender_id, packet)

#endregion


#region Signals

signal game_log(message : String, color : Color)

signal server_close()

signal client_connected_to_server()

signal client_disconnected_from_server()

signal remove_client_from_server(id : int)

signal steam_lobby_created()

# signal push_opus_packet_to_player(id : int, packet)

#endregion


#region Godot Functions

func _ready() -> void:
	for argument in OS.get_cmdline_args():
		if argument == "-steam":
			ready_steam()

	# ready_voip()

	#set multiplayer signals
	multiplayer.peer_connected.connect(_multiplayer_peer_connected)
	multiplayer.peer_disconnected.connect(_multiplayer_peer_disconnected)
	multiplayer.connected_to_server.connect(_multiplayer_connected_to_server)
	multiplayer.connection_failed.connect(_multiplayer_connection_failed)
	multiplayer.server_disconnected.connect(_multiplayer_server_disconnected)
	

	#set steam signals
	if Global.steam_connected == true:
		Steam.lobby_joined.connect(_steam_lobby_joined)
		Steam.lobby_created.connect(_steam_lobby_created)

func _process(_delta : float):
	# process_voip()
	Steam.run_callbacks()

#endregion


#region Ready Functions

func ready_steam() -> void:
	var steam_response : Dictionary = Steam.steamInitEx()
	Global.steam_connected = steam_response["status"] == 0

	if Global.steam_connected == false:
		get_tree().create_timer(3.0).timeout.connect(
			func():
				game_log.emit("Could Not Connect To Steam", Color(1, 0, 0, 1))
		)

# func ready_voip() -> void:
#     assert(microphone.bus == "MicrophoneBus")
#     var mic_bus = AudioServer.get_bus_index("MicrophoneBus")
#     mic_capture = AudioServer.get_bus_effect(mic_bus, 0)

#endregion


#region Process Functions

# func process_voip() -> void:
#     if mic_capture == null:
#         return

#     while mic_capture.chunk_available():
#         var packet = mic_capture.read_opus_packet(PackedByteArray())
#         mic_capture.drop_chunk()

#         if Global.voip_on == false:
#             continue

#         if Global.is_multiplayer and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED:
#             voice_packet_recieved.rpc(packet)

#endregion


#region Default Multiplayer Functions

func destroy_lobby() -> void:
	Global.is_multiplayer = false

	multiplayer.multiplayer_peer.close()
	peer = OfflineMultiplayerPeer.new()
	multiplayer.multiplayer_peer = peer
	
	server_close.emit()

func disconnect_client() -> void:
	Global.is_multiplayer = false

	multiplayer.multiplayer_peer.close()
	peer = OfflineMultiplayerPeer.new()
	multiplayer.multiplayer_peer = peer

	client_disconnected_from_server.emit()

#endregion


#region Enet Functions

func enet_create_host() -> void:
	#set multiplayer to true
	Global.is_multiplayer = true

	#init multiplayer host
	peer = ENetMultiplayerPeer.new()
	(peer as ENetMultiplayerPeer).create_server(DEFAULT_PORT, MAX_CLIENTS)
	multiplayer.multiplayer_peer = peer

func enet_create_client(address : String) -> void:
	#set multiplayer to true
	Global.is_multiplayer = true

	#init client and wait for connection
	peer = ENetMultiplayerPeer.new()
	(peer as ENetMultiplayerPeer).create_client(address, DEFAULT_PORT)
	multiplayer.set_multiplayer_peer(peer)
	await multiplayer.connected_to_server

#endregion


#region Steam Functions

func steam_create_host() -> void:
	#players[1] = PlayerData.new(1, 0, new_username)
	Steam.createLobby(Steam.LOBBY_TYPE_FRIENDS_ONLY, MAX_CLIENTS)

func steam_join_lobby(new_lobby_id : int) -> void:
	Steam.joinLobby(new_lobby_id)

func steam_create_socket():
	Global.is_multiplayer = true

	peer = SteamMultiplayerPeer.new()
	peer.create_host(0)
	multiplayer.set_multiplayer_peer(peer)

func steam_connect_socket(steam_id : int) -> void:
	Global.is_multiplayer = true

	peer = SteamMultiplayerPeer.new()
	peer.create_client(steam_id, 0)
	multiplayer.set_multiplayer_peer(peer)

#endregion


#region Signal Functions

func _multiplayer_peer_connected(_id : int):
	pass

func _multiplayer_peer_disconnected(id : int):
	if multiplayer.is_server():
		game_log.emit("Player \"" + str(id) + "\" disconnected", Color(1, 0, 0, 1))
		remove_client_from_server.emit(id)

func _multiplayer_connected_to_server() -> void:
	client_connected_to_server.emit()

func _multiplayer_connection_failed() -> void:
	Global.is_multiplayer = false
	peer = OfflineMultiplayerPeer.new()
	multiplayer.multiplayer_peer = peer
	client_disconnected_from_server.emit()
	game_log.emit("Server connection failed", Color(1, 0, 0, 1))

func _multiplayer_server_disconnected() -> void:
	Global.is_multiplayer = false
	peer = OfflineMultiplayerPeer.new()
	multiplayer.multiplayer_peer = peer
	client_disconnected_from_server.emit()
	game_log.emit("Server disconnected", Color(1, 0, 0, 1))

func _steam_lobby_joined(new_lobby_id : int, _permissions : int, _locked : bool, response : int) -> void:
	if response == Steam.CHAT_ROOM_ENTER_RESPONSE_SUCCESS:
		lobby_id = new_lobby_id
		var id : int = Steam.getLobbyOwner(new_lobby_id)
		if id != Steam.getSteamID():
			steam_connect_socket(id)
			#player_register.rpc(username)
			#players[multiplayer.get_unique_id()] = username
			return
		var message : String
		match response:
			Steam.CHAT_ROOM_ENTER_RESPONSE_DOESNT_EXIST:
				message = "This lobby no longer exists."
			Steam.CHAT_ROOM_ENTER_RESPONSE_NOT_ALLOWED:
				message = "You don't have permission to join this lobby."
			Steam.CHAT_ROOM_ENTER_RESPONSE_FULL:
				message = "The lobby is now full."
			Steam.CHAT_ROOM_ENTER_RESPONSE_ERROR:
				message = "Response errored."
			Steam.CHAT_ROOM_ENTER_RESPONSE_BANNED:
				message = "You are banned from this lobby."
			Steam.CHAT_ROOM_ENTER_RESPONSE_LIMITED:
				message = "You cannot join due to having a limited account."
			Steam.CHAT_ROOM_ENTER_RESPONSE_CLAN_DISABLED:
				message = "This lobby is locked or disabled."
			Steam.CHAT_ROOM_ENTER_RESPONSE_COMMUNITY_BAN:
				message = "This lobby is community locked."
			Steam.CHAT_ROOM_ENTER_RESPONSE_MEMBER_BLOCKED_YOU:
				message = "A user in the lobby has blocked you from joining."
			Steam.CHAT_ROOM_ENTER_RESPONSE_YOU_BLOCKED_MEMBER:
				message = "A user you have blocked is in the lobby."
		game_log.emit(message, Color(1, 0, 0, 1))

func _steam_lobby_created(connected: int, this_lobby_id: int) -> void:
	if connected == 1:
		Global.steam_lobby_id = str(this_lobby_id)
		Steam.setLobbyData(this_lobby_id, "name", str(Steam.getPersonaName(), "'s test server"))
		steam_create_socket()
		steam_lobby_created.emit()
		return
	game_log.emit("Error on create lobby!", Color(1, 0, 0, 1))

#endregion
