using Godot;

using Sfs3.Client.Core;
using Sfs3.Client.Entities;
using Sfs3.Client.Entities.Data;
using Sfs3.Client.Entities.Invitation;
using Sfs3.Client.Entities.Variables;
using Sfs3.Client.Requests;
using Sfs3.Client.Requests.Buddylist;
using Sfs3.Client.Requests.Invitations;

using System;
using System.Collections.Generic;

/**
 * Script attached to the main node in the Lobby scene.
 */
public partial class LobbyController : BaseController
{
	public static readonly string USERVAR_EXPERIENCE = "xp";

	private const string ZONE_EXT_REQ_INIT = "init";
	private const string ZONE_EXT_RESP_INIT_ERR = "initErr";

	//----------------------------------------------------------
	// UI elements
	//----------------------------------------------------------

	[ExportCategory("UI Settings")]

	[Export]
	public Label userNameLabel;
	[Export]
	public Label userStateLabel;
	[Export]
	public Control startButtonsContainer;
	[Export]
	public ProfileConfiguratorPanel profileConfigurator;
	[Export]
	public WarningPanel warningPanel;
	[Export]
	public Control buddyListContainer;
	[Export]
	public BuddyList buddyList;
	[Export]
	public LineEdit buddyNameInput;
	[Export]
	public BuddyChatPanel buddyChatPanel;
	[Export]
	public InvitationPanel invitationPanel;

	private Queue<InvitationWrapper> invitationQueue;
	private float inviteWaitTimerSecs = 0f;

	//----------------------------------------------------------
	// Callback methods
	//----------------------------------------------------------
	#region
	public override void _Ready()
	{
		base._Ready();

		// Retrieve the SmartFox client instance created in the Login scene
		sfs = Global.GetSfsClient();

		// Hide modal panels
		HideModals();

		// Add SFS event listeners
		AddSmartFoxListeners();

		// Add listeners to events fired by BuddyList class
		buddyList.StartChatAction = OnStartBuddyChatPressed;
		buddyList.BlockAction = OnBlockBuddyPressed;
		buddyList.RemoveAction = OnRemoveBuddyPressed;
		buddyList.AddAction = OnAddBuddyPressed;

		// Add listeners to events fired by ProfileConfiguratorPanel class
		profileConfigurator.OnlineChangeAction = OnOnlineToggleChanged;
		profileConfigurator.BuddySettingChangeAction = OnBuddySettingChanged;
		profileConfigurator.PlayerSettingChangeAction = OnPlayerSettingChanged;

		// Add listeners to events fired by BuddyChatPanel class
		buddyChatPanel.SendMessageAction = OnBuddyChatMessageSubmit;

		// Add listener to event fired by InvitationPanel class
		invitationPanel.InvitationReplyAction = OnInvitationReply;

		// Display username in footer and user profile panel
		userNameLabel.Text = sfs.MySelf.Name;
		profileConfigurator.DisplayUserName(sfs.MySelf.Name);

		// Init or display player profile details
		InitializePlayer();

		// Initialize Buddy List system
		// We have to check if it was already initialized, in case this scene is reloaded after leaving the Game scene
		if (!sfs.BuddyManager.IsInited)
			sfs.Send(new InitBuddyListRequest());
		else
			InitializeBuddyClient();
	}

	public override void _Process(double delta)
	{
		base._Process(delta);

		if (inviteWaitTimerSecs > 0)
		{
			inviteWaitTimerSecs -= (float)delta;

			if ((int)Math.Round(inviteWaitTimerSecs, 0) == 0)
			{
				inviteWaitTimerSecs = 0f;

				ProcessInvitations();
			}
		}
	}
	#endregion

	//----------------------------------------------------------
	// UI event listeners
	//----------------------------------------------------------
	#region
	/**
	 * On Logout button click, disconnect from SmartFoxServer.
	 * This causes a CONNECTION_LOST event, which is handled by the Global Manager to load the Login scene.
	 */
	public void OnLogoutButtonClick()
	{
		BuddyChatHistoryManager.ResetAllHistory();

		sfs.Disconnect();
	}

	/**
	 * On level button click, send a request to the server side Zone Extension to start a new game.
	 * The Zone Extension searches for an existing Room to join the user in; if it can't be found, a new Room is created.
	 */
	public void OnStartGameClick(string levelName)
	{
		// Disable start buttons
		EnableStartButtons(false);

		// Add level name to request parameters
		ISFSObject reqParams = new SFSObject();
		reqParams.PutString("name", levelName);

		// Send request to Zone Extension
		sfs.Send(new ExtensionRequest(ZONE_EXT_REQ_INIT, reqParams));
	}

	/**
	 * On User icon click, show the User Profile panel.
	 */
	public void OnProfileButtonClick()
	{
		profileConfigurator.Visible = true;
	}

	/**
	 * On Add buddy button click, send request to add buddy to user's Buddy List.
	 */
	public void OnAddBuddyButtonClick()
	{
		OnBuddyNameInputSubmit(buddyNameInput.Text);
	}

	/**
	 * On buddy name input submit, send request to add buddy to user's Buddy List.
	 */
	public void OnBuddyNameInputSubmit(string userName)
	{
		if (userName != "")
		{
			// Request buddy adding to buddy list
			sfs.Send(new AddBuddyRequest(userName));

			// Reset buddy name input
			buddyNameInput.Text = "";
		}

		buddyNameInput.GrabFocus();
	}

	/**
	 * On Chat button press in Buddy List item, initialize and show chat panel.
	 */
	public void OnStartBuddyChatPressed(string buddyName)
	{
		// Skip if chat is already active with the same buddy
		if (buddyChatPanel.UserName != buddyName)
		{
			// Get chat history (if any) and reset new messages counter
			var buddyChatMessages = BuddyChatHistoryManager.GetBuddyChatMessages(buddyName);
			BuddyChatHistoryManager.ResetUnreadMessagesCount(buddyName);

			// Initialize and show panel
			InitBuddyChatPanel(sfs.BuddyManager.GetBuddyByName(buddyName), buddyChatMessages);

			// Move buddy list item to top of the list and reset new messages counter
			buddyList.MoveListItemToTop(buddyName);
			buddyList.SetItemChatMsgCounter(buddyName, 0);
		}
	}

	/**
	 * On Block/Unblock button press in Buddy List item, send request to block/unblock buddy in user's Buddy List.
	 */
	public void OnBlockBuddyPressed(string buddyName)
	{
		bool isBlocked = sfs.BuddyManager.GetBuddyByName(buddyName).IsBlocked;

		// Request buddy block/unblock
		sfs.Send(new BlockBuddyRequest(buddyName, !isBlocked));
	}

	/**
	 * On Remove button press in Buddy List item, send request to remove buddy from user's Buddy List.
	 */
	public void OnRemoveBuddyPressed(string buddyName)
	{
		// Request buddy removal from buddy list
		sfs.Send(new RemoveBuddyRequest(buddyName));
	}

	/**
	 * On Add button press in Buddy List item, send request to add a temporary buddy to user's Buddy List.
	 */
	public void OnAddBuddyPressed(string buddyName)
	{
		// Request buddy adding to buddy list
		sfs.Send(new AddBuddyRequest(buddyName));
	}

	/**
	 * On custom event fired by Profile Configurator panel, toggle user online state in Buddy List system.
	 */
	public void OnOnlineToggleChanged(bool goOnline)
	{
		// Request online/offline state toggle
		sfs.Send(new GoOnlineRequest(goOnline));
	}

	/**
	 * On custom event fired by Profile Configurator panel, set user's Buddy Variables.
	 */
	public void OnBuddySettingChanged(string varName, object value)
	{
		var type = value == null ? VariableType.NULL : (value is int ? VariableType.INT : VariableType.STRING);

		List<BuddyVariable> buddyVars = [new SFSBuddyVariable(varName, value, type)];

		// Set Buddy Variables
		sfs.Send(new SetBuddyVariablesRequest(buddyVars));
	}

	/**
	 * On custom event fired by Profile Configurator panel, set user's User Variables.
	 */
	public void OnPlayerSettingChanged(string varName, object value)
	{
		var type = value == null ? VariableType.NULL : (value is int ? VariableType.INT : VariableType.STRING);

		List<UserVariable> userVars = [new SFSUserVariable(varName, value, type)];

		// Set User Variables
		sfs.Send(new SetUserVariablesRequest(userVars));
	}

	/**
	 * On custom event fired by Buddy Chat panel, send a message to the buddy the user is chatting with.
	 */
	public void OnBuddyChatMessageSubmit(string msg)
	{
		string buddyName = buddyChatPanel.UserName;

		// Add a custom parameter containing the recipient name
		ISFSObject paramsObj = new SFSObject();
		paramsObj.PutString("recipient", buddyName);

		// Retrieve buddy
		Buddy buddy = sfs.BuddyManager.GetBuddyByName(buddyName);

		// Send message to buddy
		sfs.Send(new BuddyMessageRequest(msg, buddy, paramsObj));
	}

	/**
	 * On custom event fired by Invitation panel, reply to the invitation.
	 */
	public void OnInvitationReply(Invitation invitation, bool accept)
	{
		// Accept/refuse invitation
		sfs.Send(new InvitationReplyRequest(invitation, accept ? InvitationReply.ACCEPT : InvitationReply.REFUSE));

		// If invitation was accepted, refuse all remaining invitations in the queue
		if (accept)
		{
			// Refuse other invitations
			foreach (var iw in invitationQueue)
				sfs.Send(new InvitationReplyRequest(iw.Invitation, InvitationReply.REFUSE));

			// Reset queue
			invitationQueue = null;
		}

		// If invitation was refused, run timer and then process next invitation in the queue (if any)
		// We wait a second to make it clear that the previous invitation was refused before the next one (if any) is displayed
		else
		{
			inviteWaitTimerSecs = 1f;
		}
	}
	#endregion

	//----------------------------------------------------------
	// Helper methods
	//----------------------------------------------------------
	#region
	/**
	 * Add all SmartFoxServer-related event listeners required by the scene.
	 */
	private void AddSmartFoxListeners()
	{
		sfs.AddEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
		sfs.AddEventListener(SFSEvent.ROOM_JOIN, OnRoomJoin);
		sfs.AddEventListener(SFSEvent.ROOM_JOIN_ERROR, OnRoomJoinError);
		sfs.AddEventListener(SFSEvent.ROOM_CREATION_ERROR, OnRoomCreationError);

		sfs.AddEventListener(SFSEvent.USER_VARIABLES_UPDATE, OnUserVariablesUpdate);

		sfs.AddEventListener(SFSBuddyEvent.BUDDY_LIST_INIT, OnBuddyListInit);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_ERROR, OnBuddyError);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_ONLINE_STATE_CHANGE, OnBuddyOnlineStateChange);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_VARIABLES_UPDATE, OnBuddyVariablesUpdate);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_BLOCK, OnBuddyBlock);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_REMOVE, OnBuddyRemove);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_ADD, OnBuddyAdd);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);

		sfs.AddEventListener(SFSEvent.INVITATION, OnInvitation);
	}

	/**
	 * Remove all SmartFoxServer-related event listeners added by the scene.
	 * This method is called by the parent BaseController when the scene is destroyed.
	 */
	public override void RemoveSmartFoxListeners()
	{
		if (sfs != null)
		{
			sfs.RemoveEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
			sfs.RemoveEventListener(SFSEvent.ROOM_JOIN, OnRoomJoin);
			sfs.RemoveEventListener(SFSEvent.ROOM_JOIN_ERROR, OnRoomJoinError);
			sfs.RemoveEventListener(SFSEvent.ROOM_CREATION_ERROR, OnRoomCreationError);

			sfs.RemoveEventListener(SFSEvent.USER_VARIABLES_UPDATE, OnUserVariablesUpdate);

			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_LIST_INIT, OnBuddyListInit);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_ERROR, OnBuddyError);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_ONLINE_STATE_CHANGE, OnBuddyOnlineStateChange);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_VARIABLES_UPDATE, OnBuddyVariablesUpdate);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_BLOCK, OnBuddyBlock);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_REMOVE, OnBuddyRemove);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_ADD, OnBuddyAdd);
			sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);

			sfs.RemoveEventListener(SFSEvent.INVITATION, OnInvitation);
		}
	}

	/**
	 * Hide all modal panels.
	 */
	protected override void HideModals()
	{
		profileConfigurator.Visible = false;
		warningPanel.Visible = false;
		invitationPanel.HideAndRefuse();
	}

	private void ShowBuddyList(bool show)
	{
		buddyListContainer.Visible = show;
	}

	/**
	 * Enable/disable the buttons to start a new game.
	 */
	private void EnableStartButtons(bool enable)
	{
		foreach (var child in startButtonsContainer.GetChildren())
		{
			if (child is BaseButton button)
			{
				button.Disabled = !enable;
				button.Modulate = new Color(1f, 1f, 1f, enable ? 1f : 0.5f);
			}
		}
	}

	/**
	 * Initialize player profile.
	 */
	private void InitializePlayer()
	{
		// IMPORTANT NOTE
		// The Experience is a custom parameter used in this example to show how to filter players through SmartFoxServer's matchmaking system
		// when sending invitations to play a game. Of course it (or any other parameter with a similar purpose) shouldn't be set manually, but it should be
		// determined by the game logic, saved in a database and set in User Variables by a server-side Extension on user login.
		// In this example, for sake of simplicity, such information is set to a default value when the Lobby scene is loaded and it is lost on user logout.

		// Check if player experience is set in User Variables
		if (sfs.MySelf.GetVariable(USERVAR_EXPERIENCE) == null)
		{
			// If not, set player profile default value; this in turn causes a USER_VARIABLES_UPDATE event to be fired,
			// upon which we will display the player experience in the profile configurator panel
			var expVar = new SFSUserVariable(USERVAR_EXPERIENCE, profileConfigurator.PlayerExperience, VariableType.INT);

			sfs.Send(new SetUserVariablesRequest([expVar]));
		}
		else
		{
			// If yes, display player details in profile configurator panel
			profileConfigurator.InitPlayerProfile(sfs.MySelf);
		}
	}

	/**
	 * Initialize buddy-related entities.
	 */
	private void InitializeBuddyClient()
	{
		// Display buddy list items
		foreach (Buddy buddy in sfs.BuddyManager.BuddyList)
		{
			// All blocked buddies are displayed at the bottom of the list
			buddyList.AddListItem(buddy, !buddy.IsBlocked);
		}

		// Set current user details in Buddy List system
		profileConfigurator.InitBuddyProfile(sfs.BuddyManager);

		// Display user state in Buddy List system
		DisplayUserStateAsBuddy();

		// Show/hide buddy list
		ShowBuddyList(sfs.BuddyManager.MyOnlineState);
	}

	/**
	 * Show current user state in Buddy List system.
	 */
	private void DisplayUserStateAsBuddy()
	{
		string state = sfs.BuddyManager.MyOnlineState ? sfs.BuddyManager.MyState : "Offline";

		userStateLabel.Text = "[" + state + "]";
	}

	/**
	 * Update Buddy List Item instance when buddy state changes.
	 */
	private void UpdateBuddyListEntry(Buddy buddy)
	{
		buddyList.UpdateListItem(buddy);

		// Also update buddy chat panel
		if (buddyChatPanel.UserName == buddy.Name)
		{
			// Update nickname if changed
			string newBuddyDisplayName = buddy.DisplayName;

			// Get current name displayed in chat
			string currBuddyDisplayName = buddyChatPanel.DisplayName;

			if (newBuddyDisplayName != currBuddyDisplayName)
			{
				buddyChatPanel.PrintSystemMessage(currBuddyDisplayName + " is now known as " + newBuddyDisplayName);
				buddyChatPanel.SetUser(buddy.Name, buddy.Nickname);
			}

			// Enable/disable message input
			buddyChatPanel.Disabled = !buddy.IsOnline || buddy.IsBlocked;
		}
	}

	private void InitBuddyChatPanel(Buddy buddy, List<BuddyChatMessage> msgHistory)
	{
		// Reset panel (it is reused for different buddies)
		buddyChatPanel.Reset();

		// Set buddy name
		buddyChatPanel.SetUser(buddy.Name, buddy.Nickname);

		// Print chat history
		if (msgHistory != null)
			foreach (var messageObj in msgHistory)
				buddyChatPanel.PrintChatMessage(messageObj.Message, messageObj.Sender, messageObj.SentByMe);

		// Show chat panel
		buddyChatPanel.SetVisible();

		// Focus on message input
		buddyChatPanel.SetFocus();
	}

	/**
	 * Process the invitations in queue, displaying the invitation accept/refuse panel.
	 */
	private void ProcessInvitations()
	{
		// If the invitation panel is visible, then the user is already dealing with an invitation
		// Otherwise we can go on with the processing
		if (!invitationPanel.Visible)
		{
			while (invitationQueue.Count > 0)
			{
				// Get next invitation wrapper object in queue
				var iw = invitationQueue.Dequeue();

				// Evaluate remaining time for replying
				var now = DateTime.Now;
				int secondsDifference = (int)Math.Floor((now - iw.Date).TotalSeconds);

				// Update expiration time
				iw.ExpiresInSeconds -= secondsDifference;

				// Display invitation only if expiration will occur in 3 seconds or more, otherwise discard it
				if (iw.ExpiresInSeconds >= 3)
				{
					invitationPanel.ShowInvitation(iw);
					break;
				}
			}
		}
	}
	#endregion

	//----------------------------------------------------------
	// SmartFoxServer event listeners
	//----------------------------------------------------------
	#region
	private void OnExtensionResponse(ApiEvent evt)
	{
		string cmd = (string)evt.Params[EventParam.Cmd];

		if (cmd == ZONE_EXT_RESP_INIT_ERR)
		{
			// Show warning
			warningPanel.ShowWarning("Invalid level identifier");

			// Re-enable start buttons
			EnableStartButtons(true);
		}
	}

	private void OnRoomJoin(ApiEvent evt)
	{
		// Set user as "Away" in Buddy List system
		if (sfs.BuddyManager.MyOnlineState)
		{
			List<BuddyVariable> buddyVars = [new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, "Away", VariableType.STRING)];
			sfs.Send(new SetBuddyVariablesRequest(buddyVars));
		}

		// Load Game scene
		GetTree().ChangeSceneToFile("res://scenes/Game.tscn");
	}

	private void OnRoomJoinError(ApiEvent evt)
	{
		// Show warning
		warningPanel.ShowWarning(String.Format("Room join failed.\n{0}.", evt.Params[EventParam.ErrorMessage]));

		// Re-enable start buttons
		EnableStartButtons(true);
	}

	private void OnRoomCreationError(ApiEvent evt)
	{
		// Show warning
		warningPanel.ShowWarning(String.Format("Room creation failed.\n{0}.", evt.Params[EventParam.ErrorMessage]));

		// Re-enable start buttons
		EnableStartButtons(true);
	}

	private void OnUserVariablesUpdate(ApiEvent evt)
	{
		User user = (User)evt.Params[EventParam.User];

		// Display player details in user profile panel
		if (user.IsItMe)
			profileConfigurator.InitPlayerProfile(user);
	}

	private void OnBuddyListInit(ApiEvent evt)
	{
		// Initialize buddy-related entities
		InitializeBuddyClient();
	}

	private void OnBuddyError(ApiEvent evt)
	{
		// Show warning
		warningPanel.ShowWarning(String.Format("Buddy list system error.\n{0}.", evt.Params[EventParam.ErrorMessage]));
	}

	private void OnBuddyOnlineStateChange(ApiEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];
		bool isItMe = (bool)evt.Params[EventParam.IsItMe];

		// As this event is fired in case of online state update for both the current user and their buddies,
		// we have to check who the event refers to

		if (!isItMe)
			UpdateBuddyListEntry(buddy);
		else
		{
			DisplayUserStateAsBuddy();

			bool isOnline = sfs.BuddyManager.MyOnlineState;

			// Show/hide buddy list
			ShowBuddyList(isOnline);

			if (!isOnline)
			{
				// Hide chat if current user went offline
				buddyChatPanel.ResetAndHide();
			}
			else
			{
				// Update all buddy items if current user came online
				foreach (Buddy b in sfs.BuddyManager.BuddyList)
					UpdateBuddyListEntry(b);
			}
		}
	}

	private void OnBuddyVariablesUpdate(ApiEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];
		bool isItMe = (bool)evt.Params[EventParam.IsItMe];

		// As this event is fired in case of Buddy Variables update for both the current user and their buddies,
		// we have to check who the event refers to

		if (!isItMe)
			UpdateBuddyListEntry(buddy);
		else
			DisplayUserStateAsBuddy();
	}

	private void OnBuddyBlock(ApiEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];

		UpdateBuddyListEntry(buddy);

		// Reset chat history and unread messages counter
		BuddyChatHistoryManager.ResetHistory(buddy.Name);
	}

	private void OnBuddyRemove(ApiEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];

		buddyList.RemoveListItem(buddy.Name);

		// Hide buddy chat
		if (buddyChatPanel.UserName == buddy.Name)
			buddyChatPanel.ResetAndHide();

		// Reset chat history and unread messages counter
		BuddyChatHistoryManager.ResetHistory(buddy.Name);
	}

	private void OnBuddyAdd(ApiEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];

		// Add buddy list item at the top of the list
		buddyList.AddListItem(buddy, true);
	}

	private void OnBuddyMessage(ApiEvent evt)
	{
		Buddy sender = (Buddy)evt.Params[EventParam.Buddy];
		bool isItMe = (bool)evt.Params[EventParam.IsItMe];
		string message = (string)evt.Params[EventParam.Message];
		string recipient = ((SFSObject)evt.Params[EventParam.Data]).GetString("recipient");

		string targetBuddyName = isItMe ? recipient : sender.Name;
		string senderDisplayName = isItMe ? sfs.BuddyManager.MyDisplayName : sender.DisplayName;

		// Add message to queue
		var chatMsg = BuddyChatHistoryManager.AddMessage(targetBuddyName, senderDisplayName, isItMe, message);

		// Show message or increase message counter
		if (buddyChatPanel.UserName == chatMsg.Target)
		{
			// Display message in chat panel
			buddyChatPanel.PrintChatMessage(chatMsg.Message, chatMsg.Sender, chatMsg.SentByMe);
		}
		else
		{
			// Increase unread messages count
			// NOTE: there's no need to make sure the sender is not the current user,
			// as in this example the current user can't send buddy messages in other ways than
			// the chat panel (which would make the above if statement true)
			int unreadMsgCnt = BuddyChatHistoryManager.IncreaseUnreadMessagesCount(chatMsg.Target);

			// Update buddy list item
			buddyList.SetItemChatMsgCounter(chatMsg.Target, unreadMsgCnt);
		}
	}

	private void OnInvitation(ApiEvent evt)
	{
		Invitation invitation = (Invitation)evt.Params[EventParam.Invitation];

		// Create a queue of invitations
		// We use a queue because a user could receive multiple invitations within the time it takes to accept or refuse one
		invitationQueue ??= new();

		// Add an object wrapping the invitation to the queue
		invitationQueue.Enqueue(new InvitationWrapper(invitation, invitation.SecondsForAnswer));

		// Trigger invitation processing
		ProcessInvitations();
	}
	#endregion
}

/**
 * Utility class to store the details of a received invitation.
 */
public class InvitationWrapper
{
	public Invitation Invitation;
	public int ExpiresInSeconds;
	public DateTime Date;

	public InvitationWrapper(Invitation invitation, int expiresInSeconds)
	{
		Invitation = invitation;
		ExpiresInSeconds = expiresInSeconds;
		Date = DateTime.Now;
	}
}
