using System;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.UI;

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

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

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

	public LeavePanel leavePanel;
	public InputField messageInput;
	public Text chatTextArea;
	public ScrollRect chatScrollView;

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

	private SmartFox sfsLobby;
	private SmartFox sfsGame;
	private bool runTimer;
	private float timer = 20;
	private string lastSenderName;

	//----------------------------------------------------------
	// 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
		AddSmartFoxLobbyListeners();
		AddSmartFoxGameListeners();

		// Initialize game manager
		// The SmartFox class instances related to the game node is passed, so that the game manager
        // can add its own listeners to interact with the server-side Room Extension containing the game logic
		// The lobby's Buddy Manager is also to check if the opponent is buddy of the current user or not
		gameManager.Init(sfsGame, sfsLobby.BuddyManager);

		// If user is the first player in the Room, set a timeout
		// Having a timeout is useful to suggest the user to leave the game if not yet started within some time
		// For example this could mean that the invited buddy refused the invitation, or the server couldn't locate other players to invite
		if (sfsGame.LastJoinedRoom.PlayerList.Count == 1)
			runTimer = true;
	}

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

		// Check timeout
		if (runTimer)
		{
			timer -= Time.deltaTime;
			
			int timerInt = (int)Math.Round(timer, 0);

			if (timerInt == 0)
				StopTimeout(true);
		}
	}
	#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()
	{
		leavePanel.Hide();
	}
	#endregion

	//----------------------------------------------------------
	// UI event listeners
	//----------------------------------------------------------
	#region
	/**
	 * On public chat message input edit end, if the Enter key was pressed, send the chat message.
	 */
	public void OnMessageInputEndEdit()
	{
		if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter))
			SendMessage();
	}

	/**
	 * On Send button click, send the chat message.
	 */
	public void OnSendButtonClick()
	{
		SendMessage();
	}

	/**
	 * 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()
	{
		// Destroy game manager
		gameManager.Destroy();
		
		// 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();
	}

	/**
	 * Add Add Buddy button click, add opponent to buddy list.
	 */
	public void OnAddBuddyClick(string userName)
	{
		sfsLobby.Send(new AddBuddyRequest(userName));
	}
	#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_ADD, OnBuddyAdd);
		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_ADD, OnBuddyAdd);
		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.PUBLIC_MESSAGE, OnPublicMessage);
		sfsGame.AddEventListener(SFSEvent.USER_ENTER_ROOM, OnUserEnterRoom);
		sfsGame.AddEventListener(SFSEvent.USER_EXIT_ROOM, OnUserExitRoom);
	}

	/**
	 * 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.PUBLIC_MESSAGE, OnPublicMessage);
		sfsGame.RemoveEventListener(SFSEvent.USER_ENTER_ROOM, OnUserEnterRoom);
		sfsGame.RemoveEventListener(SFSEvent.USER_EXIT_ROOM, OnUserExitRoom);
	}

	/**
	 * Disable timeout showing panel suggesting player to leave.
	 */
	private void StopTimeout(bool showPanel)
	{
		runTimer = false;

		if (showPanel)
			leavePanel.Show();
		else
			leavePanel.Hide();
	}

	/**
	 * Send a public chat message.
	 */
	private void SendMessage()
	{
		if (messageInput.text != "")
		{
			// Send public message to Room
			sfsGame.Send(new PublicMessageRequest(messageInput.text));

			// Reset message input
			messageInput.text = "";
			messageInput.ActivateInputField();
			messageInput.Select();
		}
	}

	/**
	 * Display a chat message.
	 */
	private void PrintChatMessage(string message, string senderName)
	{
		// Print sender name, unless they are the same of the last message
		if (senderName != lastSenderName)
			chatTextArea.text += "<b>" + (senderName == "" ? "Me" : senderName) + "</b>\n";

		// Print chat message
		chatTextArea.text += message + "\n";

		// Save reference to last message sender, to avoid repeating the name for subsequent messages from the same sender
		lastSenderName = senderName;

		// Scroll view to bottom
		ScrollChatToBottom();
	}

	/**
	 * Display a system message.
	 */
	private void PrintSystemMessage(string message)
	{
		// Print message
		chatTextArea.text += "<color=#ffffff><i>" + message + "</i></color>\n";

		// Scroll view to bottom
		ScrollChatToBottom();
	}

	/**
     * Scroll chat to bottom.
     */
	private void ScrollChatToBottom()
	{
		Canvas.ForceUpdateCanvases();
		chatScrollView.verticalNormalizedPosition = 0;
	}
	#endregion

	//----------------------------------------------------------
	// SmartFoxServer event listeners
	//----------------------------------------------------------
	#region
	private void OnBuddyAdd(BaseEvent evt)
	{
		// When a buddy is added, hide Add Buddy button in game interface
		gameManager.UpdatePlayerTags();
	}

	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);
	}

	private void OnPublicMessage(BaseEvent evt)
	{
		User sender = (User)evt.Params["sender"];
		string message = (string)evt.Params["message"];

		// Display chat message
		PrintChatMessage(message, sender != sfsGame.MySelf ? sender.Name : "");
	}

	private void OnUserEnterRoom(BaseEvent evt)
	{
		User user = (User)evt.Params["user"];
		Room room = (Room)evt.Params["room"];

		// Display system message
		PrintSystemMessage("User " + user.Name + " joined this game");

		// Stop timeout
		if (user.IsPlayerInRoom(room))
			StopTimeout(false);
	}

	private void OnUserExitRoom(BaseEvent evt)
	{
		User user = (User)evt.Params["user"];

		// Display system message
		if (user != sfsGame.MySelf)
			PrintSystemMessage("User " + user.Name + " left the game");
	}
	#endregion
}
