using System;
using System.Collections.Generic;
using System.Timers;

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities;
using Sfs2X.Entities.Data;
using Sfs2X.Entities.Variables;
using Sfs2X.Requests;
using Sfs2X.Requests.Buddylist;
using Sfs2X.Requests.Cluster;

/**
 * Script attached to the Controller object in the Game scene.
 */
public class GameSceneController : BaseSceneController
{
	public GameEngine gameEngine;

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

	public GameObject preGameUI;
	public GameObject midGameUI;
	public GameObject postGameUI;
	public ShipMenuOption[] shipMenuOptions;
	public Text waitingText;
	public GameObject buddyInvitePanel;
	public Transform buddyInviteContent;
	public BuddyInviteItem buddyInviteItemPrefab;
	public Text rankingText;

	//----------------------------------------------------------
	// Private properties
	//----------------------------------------------------------

	private SmartFox sfsLobby;
	private SmartFox sfsGame;

	private ISFSObject level;
	private ISFSObject starshipModels;
	private ISFSObject weaponModels;
	private string selectedShipModel;
	private float countdownDelay;
	private bool countdownRunning;
	private Dictionary<string, BuddyInviteItem> buddyInviteItems;
	private int clientServerLag;

	// USER VARIABLES
	private const string UV_MODEL = "sModel";       // Starship model
	private const string UV_X = "x";                // x position
	private const string UV_Y = "y";                // y position
	private const string UV_VX = "vx";              // Velocity x component
	private const string UV_VY = "vy";              // Velocity y component
	private const string UV_DIR = "d";              // Ship direction
	private const string UV_THRUST = "t";           // Ship's thruster is active
	private const string UV_ROTATE = "r";           // Ship rotating direction
	private const string UV_SHIELD = "s";           // Ship's shield energy
	private const string USERVAR_EXPERIENCE = "xp"; // User experience

	// MMOITEM VARIABLES
	private const string IV_TYPE = "iType";         // MMOItem type
	private const string IV_MODEL = "iModel";       // MMOItem model
	private const string IV_X = "x";                // x position
	private const string IV_Y = "y";                // y position
	private const string IV_VX = "vx";              // Velocity x component
	private const string IV_VY = "vy";              // Velocity y component

	// MMOITEM TYPES
	private const string ITYPE_WEAPON = "weapon";   // MMOItem of type weapon

	// REQUESTS TO SERVER
	private const string REQ_READY = "init.ready";
	private const string REQ_SHIP = "init.ship";
	private const string REQ_ROTATE = "control.rotate";
	private const string REQ_THRUST = "control.thrust";
	private const string REQ_FIRE = "control.fire";

	// RESPONSES FROM SERVER
	private const string RESP_CONFIG = "config";
	private const string RESP_COUNTDOWN = "count";
	private const string RESP_LEAVE = "leave";
	private const string RESP_START = "start";
	private const string RESP_SHOT_XPLODE = "shot_xplode";
	private const string RESP_SHIP_XPLODE = "ship_xplode";
	private const string RESP_GAME_OVER = "game_over";

	//----------------------------------------------------------
	// Unity callback methods
	//----------------------------------------------------------
	#region
	private void Start()
	{
		Debug.Log("GAME SCENE - Scene loaded");

		// Get reference to static SmartFox client instances for the lobby and game nodes
		sfsLobby = gm.GetLobbyClient();
		sfsGame = gm.GetGameClient();

		// Set user as "Away" in lobby node Buddy List system
		if (sfsLobby.BuddyManager.MyOnlineState)
			sfsLobby.Send(new SetBuddyVariablesRequest(new List<BuddyVariable> { new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, "Away") }));

		// Add SmartFox event listeners
		// This controller is in charge of handling all pre-game and post-game flow, communicating with the Room Extension
		// (starship selection, waiting for other players, inviting buddies, showing game over screen, etc)
		AddSmartFoxLobbyListeners();
		AddSmartFoxGameListeners();

		// Enable lag monitor
		// This is required to compensate the lag and make the starships and other objects positions
		// match the current position in the server-side simulation as much as possible
		sfsGame.EnableLagMonitor(true, 1, 5);

		// Show pre-game UI
		preGameUI.SetActive(true);

		// Show game state
		ShowGameState();

		// Initialize buddy invitation panel
		InitBuddyInvitePanel();

		// Send READY message to Room Extension
		sfsGame.Send(new ExtensionRequest(REQ_READY, new SFSObject(), sfsGame.LastJoinedRoom));
	}

    override protected void Update()
    {
		base.Update();

		if (countdownRunning)
        {
			if (countdownDelay > 0)
			{
				// Show progress
				waitingText.text = "Game will start in " + Mathf.CeilToInt(countdownDelay).ToString("00") + " seconds";

				countdownDelay -= Time.deltaTime;
			}
			else
			{
				countdownDelay = 0;
				countdownRunning = false;
			}
		}
    }
	#endregion

	//----------------------------------------------------------
	// Abstract methods from BaseSceneController implementation
	//----------------------------------------------------------
	#region
	/**
	 * Remove all SmartFoxServer-related event listeners added by the scene.
	 * This method is called by the parent BaseSceneController.OnDestroy method when the scene is destroyed.
	 */
	override protected void RemoveSmartFoxListeners()
	{
		// Remove game-related SmartFox event listeners
		RemoveSmartFoxGameListeners();

		// Remove lobby-related SmartFox event listeners
		RemoveSmartFoxLobbyListeners();
	}

	override public void ShowError(string errorMsg)
	{
		// Nothing to do
	}

	override protected void HideModals()
	{
		// Nothing to do
	}
	#endregion

	//----------------------------------------------------------
	// UI event listeners
	//----------------------------------------------------------
	#region
	/**
	 * On leave button click, disconnect from SmartFoxServer.
	 * This causes the SmartFox listeners added by this scene to be removed (see BaseSceneController.OnDestroy method)
	 * and the Lobby scene to be loaded (see GlobalManager.OnGameConnectionLost method).
	 */
	public void OnLeaveButtonClick()
	{
		LeaveGameScene();
	}

	public void OnShipSelected(string shipModel)
    {
		// Deselect previously selected ship
		if (selectedShipModel != null)
        	foreach (ShipMenuOption smo in shipMenuOptions)
				if (smo.shipName == selectedShipModel)
				{
					smo.Deselect();
					break;
				}
		
		// Save selection
		selectedShipModel = shipModel;

		ISFSObject data = new SFSObject();
		data.PutUtfString("model", shipModel);
		
		// Send selection to Extension
		sfsGame.Send(new ExtensionRequest(REQ_SHIP, data, sfsGame.LastJoinedRoom));
	}

	public void OnInviteBuddyButtonClick(BuddyInviteItem buddyinviteItem, Buddy buddy)
    {
		// Disable button
        buddyinviteItem.inviteButton.interactable = false;

		// Send invitation
		ClusterTarget target = new ClusterTarget(sfsGame.NodeId, sfsGame.LastJoinedRoom.Id);

		List<object> invitedBuddies = new List<object>();
		invitedBuddies.Add(buddy);

		ISFSObject invParams = new SFSObject();
		invParams.PutUtfString("level", sfsGame.LastJoinedRoom.GetVariable("gameLevel").GetStringValue());

		sfsLobby.Send(new ClusterInviteUsersRequest(target, invitedBuddies, 15, invParams));
	}
	#endregion

	//----------------------------------------------------------
	// Game event listeners
	//----------------------------------------------------------
	#region
	/**
	 * Send the rotation event to the Room Extension.
	 */
	private void Rotate(ControlEventArgs e)
	{
		ISFSObject paramsDir = new SFSObject();
		paramsDir.PutInt("dir", e.rotationDir);
		sfsGame.Send(new ExtensionRequest(REQ_ROTATE, paramsDir, sfsGame.LastJoinedRoom));
	}

	/**
	 * Send the thrust event to the Room Extension.
	 */
	private void Thrust(ControlEventArgs e)
	{
		ISFSObject paramsThrust = new SFSObject();
		paramsThrust.PutBool("go", e.activate);
		sfsGame.Send(new ExtensionRequest(REQ_THRUST, paramsThrust, sfsGame.LastJoinedRoom));
	}

	/**
	 * Send the fire event to the Room Extension.
	 */
	private void Fire(ControlEventArgs e)
	{
		ISFSObject paramsFire = new SFSObject();
		paramsFire.PutInt("wnum", e.fire);
		sfsGame.Send(new ExtensionRequest(REQ_FIRE, paramsFire, sfsGame.LastJoinedRoom));
	}
	#endregion

	//----------------------------------------------------------
	// Helper methods
	//----------------------------------------------------------
	#region
	/**
	 * Add all SmartFoxServer lobby-related event listeners required by the scene.
	 */
	private void AddSmartFoxLobbyListeners()
	{
		Debug.Log("GAME SCENE - Adding lobby node event listeners");

		sfsLobby.AddEventListener(SFSBuddyEvent.BUDDY_ONLINE_STATE_UPDATE, OnBuddyOnlineStateUpdate);
		sfsLobby.AddEventListener(SFSBuddyEvent.BUDDY_VARIABLES_UPDATE, OnBuddyVariablesUpdate);
		sfsLobby.AddEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);
	}

	/**
	 * Remove all SmartFoxServer lobby-related event listeners added by the scene.
	 */
	private void RemoveSmartFoxLobbyListeners()
	{
		Debug.Log("GAME SCENE - Removing lobby node event listeners");

		sfsLobby.RemoveEventListener(SFSBuddyEvent.BUDDY_ONLINE_STATE_UPDATE, OnBuddyOnlineStateUpdate);
		sfsLobby.RemoveEventListener(SFSBuddyEvent.BUDDY_VARIABLES_UPDATE, OnBuddyVariablesUpdate);
		sfsLobby.RemoveEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);
	}

	/**
	 * Add all SmartFoxServer game-related event listeners required by the scene.
	 */
	private void AddSmartFoxGameListeners()
	{
		Debug.Log("GAME SCENE - Adding game node event listeners");

		sfsGame.AddEventListener(SFSEvent.PING_PONG, OnPingPong);
		sfsGame.AddEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
		sfsGame.AddEventListener(SFSEvent.USER_VARIABLES_UPDATE, OnUserVarsUpdate);
		sfsGame.AddEventListener(SFSEvent.PROXIMITY_LIST_UPDATE, OnProximityListUpdate);
	}

	/**
	 * Remove all SmartFoxServer game-related event listeners added by the scene.
	 */
	private void RemoveSmartFoxGameListeners()
	{
		Debug.Log("GAME SCENE - Removing game node event listeners");

		sfsGame.RemoveEventListener(SFSEvent.PING_PONG, OnPingPong);
		sfsGame.RemoveEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
		sfsGame.RemoveEventListener(SFSEvent.USER_VARIABLES_UPDATE, OnUserVarsUpdate);
		sfsGame.RemoveEventListener(SFSEvent.PROXIMITY_LIST_UPDATE, OnProximityListUpdate);
	}

	private void ShowGameState()
    {
		// Check if game is already started
		int gameState = sfsGame.LastJoinedRoom.GetVariable("gameState").GetIntValue();

		if (gameState == 0)
			waitingText.text = "Waiting for players...";
		else if (gameState == 3)
			waitingText.text = "Game already over";
	}

    private void LeaveGameScene()
    {
		// Set user as "Available" in lobby node Buddy List system
		if (sfsLobby.BuddyManager.MyOnlineState)
			sfsLobby.Send(new SetBuddyVariablesRequest(new List<BuddyVariable> { new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, "Available") }));

		// Disconnect from game node
		sfsGame.Disconnect();
	}

    private void InitBuddyInvitePanel()
    {
		bool isOnline = sfsLobby.BuddyManager.MyOnlineState;

		// Init buddy-related data structures
		buddyInviteItems = new Dictionary<string, BuddyInviteItem>();

		// Update the buddy list
		if (isOnline)
        {
			// For the buddy list we use a scrollable area containing a separate prefab for each buddy
			// The prefab contains clickable buttons to invite a buddy to play
			List<Buddy> buddies = sfsLobby.BuddyManager.BuddyList;

			// Display buddy invite items
			foreach (Buddy buddy in buddies)
				SetBuddyInviteItem(buddy);
		}

		// Display panel
		buddyInvitePanel.SetActive(isOnline);
	}

	private void SetBuddyInviteItem(Buddy buddy)
    {
		string buddyName = buddy.Name;

		// Get reference to buddy list item corresponding to Buddy
		buddyInviteItems.TryGetValue(buddy.Name, out BuddyInviteItem buddyInviteItem);

		if (buddyInviteItem != null)
        {
			// If buddy went offline or is not available anymore, remove it
			if (!buddy.IsOnline || buddy.IsBlocked || buddy.State != "Available")
            {
				// Remove button listener
				buddyInviteItem.inviteButton.onClick.RemoveAllListeners();

				// Remove buddy invite item from dictionary
				buddyInviteItems.Remove(buddy.Name);

				// Destroy game object
				GameObject.Destroy(buddyInviteItem.gameObject);
			}
		}
		else
        {
			// If buddy is online, not blocked and available, add it
			if (buddy.IsOnline && !buddy.IsBlocked && buddy.State == "Available")
			{
				// Create buddy invite item
				BuddyInviteItem buddyinviteItem = Instantiate(buddyInviteItemPrefab);
				buddyInviteItems.Add(buddyName, buddyinviteItem);

				// Set buddy name
				buddyinviteItem.mainLabel.text = ((buddy.NickName != null && buddy.NickName != "") ? buddy.NickName : buddy.Name);

				// Add listeners to button
				buddyinviteItem.inviteButton.onClick.AddListener(() => OnInviteBuddyButtonClick(buddyinviteItem, buddy));

				// Add buddy list item to container
				buddyinviteItem.gameObject.transform.SetParent(buddyInviteContent, false);
			}
		}
	}

	private string ToOrdinal(long number)
	{
		if (number < 0)
			return number.ToString();

		long rem = number % 100;

		if (rem >= 11 && rem <= 13)
			return number + "th";

		switch (number % 10)
		{
			case 1:
				return number + "st";
			case 2:
				return number + "nd";
			case 3:
				return number + "rd";
			default:
				return number + "th";
		}
	}
	#endregion

	//----------------------------------------------------------
	// SmartFoxServer event listeners
	//----------------------------------------------------------
	#region
	private void OnExtensionResponse(BaseEvent evt)
    {
		string cmd = (string)evt.Params["cmd"];
		ISFSObject data = (SFSObject)evt.Params["params"];

		if (cmd == RESP_CONFIG)
		{
			Debug.Log("Starships configuration received");

			// Retrieve planet settings, starship models and weapon models from custom data sent by the Room Extension
			level = data.GetSFSObject("level");
			starshipModels = data.GetSFSObject("starships");
			weaponModels = data.GetSFSObject("weapons");

			// Display startships' stats
			foreach (ShipMenuOption smo in shipMenuOptions)
            {
				string name = smo.shipName;
				ISFSObject stats = starshipModels.GetSFSObject(name);

				smo.SetSpeedStat(stats.GetInt("maxSpeed"), 200);
				smo.SetRotationStat(stats.GetInt("rotationSpeed"), 200);
				smo.SetThrustStat(stats.GetInt("thrustAccel"), 150);
				smo.SetWeaponName(stats.GetUtfString("weapon1") + "," + stats.GetUtfString("weapon2"));
			}
		}

		else if (cmd == RESP_LEAVE)
		{
			Debug.Log("Game aborted due to minimum number of players not yet reached; leaving game scene");

			LeaveGameScene();
		}

		else if (cmd == RESP_COUNTDOWN)
		{
			Debug.Log("Begin game start countdown; game will start in " + data.GetInt("delay") + "s");

			// Set initial countdown delay
			countdownDelay = data.GetInt("delay");
			countdownRunning = true;
		}

		else if (cmd == RESP_START)
		{
			Debug.Log("Game start!");

			// Hide pre-game UI
			preGameUI.SetActive(false);

			// Show mid-game UI
			midGameUI.SetActive(true);

			// Create planet
			gameEngine.CreatePlanet(level);

			// Register listeners
			gameEngine.Rotate += this.Rotate;
			gameEngine.Thrust += this.Thrust;
			gameEngine.Fire += this.Fire;
		}

		else if (cmd == RESP_SHOT_XPLODE)
		{
			// A weapon shot exploded

			int shotId = data.GetInt("id");
			int posX = data.GetInt("x");
			int posY = data.GetInt("y");
			
			gameEngine.RemoveWeaponShot(shotId, true, posX, posY);
		}

		else if (cmd == RESP_SHIP_XPLODE)
		{
			// A starship exploded

			int shipId = data.GetInt("id");

			gameEngine.RemoveStarship(shipId, true);
		}

		else if (cmd == RESP_GAME_OVER)
		{
			Debug.Log("Game over");

			// It's game over

			int ranking = data.GetInt("r");
			bool isTie = data.GetBool("t");

			// De-register listeners
			gameEngine.Rotate -= this.Rotate;
			gameEngine.Thrust -= this.Thrust;
			gameEngine.Fire -= this.Fire;

			// Remove my starship
			gameEngine.RemoveStarship(sfsGame.MySelf.Id, false);

			// Hide mid-game UI
			midGameUI.SetActive(false);

			// Show post-game UI
			postGameUI.SetActive(true);

			// Display ranking
			if (ranking == 1)
            {
				if (!isTie)
					rankingText.text = "<b>YOU ARE THE WINNER</b>";
				else
					rankingText.text = "<b>IT'S A TIE</b>";

			}
			else
            {
				if (!isTie)
					rankingText.text = "<b>You ranked " + ToOrdinal(ranking) + "</b>";
				else
					rankingText.text = "<b>You tied for " + ToOrdinal(ranking) + " place</b>";
			}
		}
	}

	/**
	 * Synchronize the user trajectory with the server-side simulation.
	 * This requires the client-server lag compensation: in other words the elapsed time since the event was sent
	 * by the server is taken into account to guess where the starship is located now.
	 */
	public void OnUserVarsUpdate(BaseEvent evt)
	{
		User user = (User) evt.Params["user"];
		List<string> changedVars = (List<string>) evt.Params["changedVars"];

		if (changedVars.Contains(UV_ROTATE))
		{
			// Make user starship start or stop rotating (excluding current user who controls his starship directly)
			if (user != sfsGame.MySelf)
			{
				int r1 = user.GetVariable(UV_ROTATE).GetIntValue();
				gameEngine.SetStarshipRotating(user.Id, r1);
			}
		}

		if (changedVars.Contains(UV_X) || changedVars.Contains(UV_Y) ||
			changedVars.Contains(UV_VX) || changedVars.Contains(UV_VY) ||
			changedVars.Contains(UV_DIR) || changedVars.Contains(UV_THRUST))
		{
			// Create current user starship if not yet existing
			if (user == sfsGame.MySelf)
			{
				string shipModel = user.GetVariable(UV_MODEL).GetStringValue();
				gameEngine.CreateStarship(user.Id, user.Name, true, shipModel, starshipModels.GetSFSObject(shipModel));
			}

			// Reset user starship state in simulator, taking lag into account
			float x = (float)user.GetVariable(UV_X).GetDoubleValue();
			float y = (float)user.GetVariable(UV_Y).GetDoubleValue();
			float vx = (float)user.GetVariable(UV_VX).GetDoubleValue();
			float vy = (float)user.GetVariable(UV_VY).GetDoubleValue();
			float d = (float)user.GetVariable(UV_DIR).GetDoubleValue();
			int s = user.GetVariable(UV_SHIELD).GetIntValue();
			bool t = user.GetVariable(UV_THRUST).GetBoolValue();

			gameEngine.SetStarshipPosition(user.Id, x, y, vx, vy, d, t, s, clientServerLag);
		}
	}

	/**
	 * Create/remove the starships of users entering/leaving the current user's Area of Interest (AoI).
	 * Create/remove the weapon shots corresponding to MMOItems entering/leaving the current user's Area of Interest (AoI).
	 */
	public void OnProximityListUpdate(BaseEvent evt)
	{
		// Loop the removedUsers list in the event params to remove the starships no more visible
		List<User> removedUsers = (List<User>) evt.Params["removedUsers"];

		foreach (User ru in removedUsers)
			gameEngine.RemoveStarship(ru.Id, false);

		// Loop the addedUsers list in the event params to create the starships now visible
		// To the usual lag we add 10ms, which is half the value of the proximityListUpdateMillis setting on the server
		// As we don't know exactly after how much time the update event was fired after the users updated their positions in the MMORoom
		// (could be 0ms up to 20ms), we use half the proximityListUpdateMillis value as a sort of mean value for an additional corretion of the lag
		List<User> addedUsers = (List<User>)evt.Params["addedUsers"];

		foreach (User au in addedUsers)
		{
			// Create starship
			string shipModel = au.GetVariable(UV_MODEL).GetStringValue();
			gameEngine.CreateStarship(au.Id, au.Name, false, shipModel, starshipModels.GetSFSObject(shipModel));

			// Get position-related User Variables
			float x = (float)au.GetVariable(UV_X).GetDoubleValue();
			float y = (float)au.GetVariable(UV_Y).GetDoubleValue();
			float vx = (float)au.GetVariable(UV_VX).GetDoubleValue();
			float vy = (float)au.GetVariable(UV_VY).GetDoubleValue();
			float d = (float)au.GetVariable(UV_DIR).GetDoubleValue();
			bool t = au.GetVariable(UV_THRUST).GetBoolValue();
			int r = au.GetVariable(UV_ROTATE).GetIntValue();
			int s = au.GetVariable(UV_SHIELD).GetIntValue();

			// Set starship rotating flag
			gameEngine.SetStarshipRotating(au.Id, r);

			// Set starship position
			gameEngine.SetStarshipPosition(au.Id, x, y, vx, vy, d, t, s, clientServerLag + 10);
		}

		// Loop the removedItems list in the event params to remove the weapon shots no more visible
		// NOTE: sprites might have been removed already, in case the shots exploded within the AoI of the user
		// (notified by a dedicated Extension response) 
		List<IMMOItem> removedItems = (List<IMMOItem>)evt.Params["removedItems"];
		foreach (IMMOItem ri in removedItems)
			gameEngine.RemoveWeaponShot(ri.Id, false);

		// Loop the addedItems list in the event params to create those now visible
		// The same note about addedUsers applies here
		List<IMMOItem> addedItems = (List<IMMOItem>)evt.Params["addedItems"];
		foreach (IMMOItem ai in addedItems)
		{
			string type = ai.GetVariable(IV_TYPE).GetStringValue();

			if (type == ITYPE_WEAPON)
			{
				// Get position-related MMOItem Variables
				string im = ai.GetVariable(IV_MODEL).GetStringValue();
				float ix = (float)ai.GetVariable(IV_X).GetDoubleValue();
				float iy = (float)ai.GetVariable(IV_Y).GetDoubleValue();
				float ivx = (float)ai.GetVariable(IV_VX).GetDoubleValue();
				float ivy = (float)ai.GetVariable(IV_VY).GetDoubleValue();

				// Create weapon shot
				gameEngine.CreateWeaponShot(ai.Id, weaponModels.GetSFSObject(im), ix, iy, ivx, ivy, clientServerLag + 10);
			}
		}
	}

	/**
	 * Evaluate the current client-server lag.
	 * Returned value is divided by two because we just need the server to client lag (not client to server to client).
	 */
	public void OnPingPong(BaseEvent evt)
	{
		clientServerLag = (int)evt.Params["lagValue"] / 2;
	}

	public void OnBuddyOnlineStateUpdate(BaseEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params["buddy"];
		bool isItMe = (bool)evt.Params["isItMe"];

		if (!isItMe)
			SetBuddyInviteItem(buddy);
	}

	public void OnBuddyVariablesUpdate(BaseEvent evt)
	{
		Buddy buddy = (Buddy)evt.Params["buddy"];
		bool isItMe = (bool)evt.Params["isItMe"];

		if (!isItMe)
			SetBuddyInviteItem(buddy);
	}

	private void OnBuddyMessage(BaseEvent evt)
	{
		// Add message to queue in BuddyChatHistoryManager static class
		BuddyChatMessage chatMsg = BuddyChatHistoryManager.AddMessage(evt.Params);

		// Increase message counter
		// 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 from the Game scene
		BuddyChatHistoryManager.IncreaseUnreadMessagesCount(chatMsg.buddyName);
	}
	#endregion
}
