using Godot;

using Sfs3.Client.Core;
using Sfs3.Client.Requests;
using Sfs3.Client.Entities;

using System;
using System.Collections.Generic;
using Sfs3.Client.Entities.Variables;
using Sfs3.Client.Requests.Buddylist;
using Sfs3.Client.Entities.Data;

/**
 * Script attached to the main node in the scene.
 */
public partial class GameController : BaseController
{
    private bool runTimer;
    private float timer = 20f;

    //----------------------------------------------------------
    // UI elements
    //----------------------------------------------------------
    [ExportCategory("UI Settings")]

    [Export]
    public PopupDialog timeoutPanel;

    [Export]
    public ChatPanel chatPanel;

    [Export]
    public TicTacToeGameManager gameManager;

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

        sfs = Global.GetSfsClient();
        
        // Add listener to send chat message when button is pressed in chat panel
        chatPanel.SendMessageAction = OnChatMessageSubmit;

        // Print system message
        chatPanel.PrintSystemMessage(String.Format("Game joined as {0}", sfs.MySelf.IsPlayer ? "player" : "spectator"));
        
        // Add SFS event listeners
        AddSmartFoxListeners();

        // Initialize game manager
        // The SmartFox class instance is passed, so that the game manager can add its own listeners
        // and interact with the server-side Room Extension containing the game logic
        gameManager.Init(sfs);

        // If user is the first player in the Room, set a timeout
        // Having a timeout is usefult 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 (sfs.MySelf.IsPlayer && sfs.LastJoinedRoom.PlayerList.Count == 1)
            runTimer = true;
    }

    public override void _Process(double delta)
    {
        if (runTimer)
        {
            timer -= (float)delta;

            int timerInt = (int)Math.Round(timer, 0);

            if (timerInt == 0)
            {
                runTimer = false;
                timeoutPanel.Show();
            }
        }
    }

    public override void _ExitTree()
    {
        base._ExitTree();
        
        runTimer = false;

        gameManager.Destroy();
    }
    #endregion

    //----------------------------------------------------------
    // UI event listeners
    //----------------------------------------------------------
    #region
    public void OnLeaveGameButtonClick()
	{
		// Leave current game room
        sfs.Send(new LeaveRoomRequest());
        
        // Set state to "Available" in Buddy List system
        if (sfs.BuddyManager.MyOnlineState)
        {
            List<BuddyVariable> buddyVars = [];
            buddyVars.Add(new SFSBuddyVariable(ReservedBuddyVariables.BV_STATE, "Available", VariableType.STRING));
            sfs.Send(new SetBuddyVariablesRequest(buddyVars));
        }

        // Return to lobby scene
        GetTree().ChangeSceneToFile("scenes/Lobby.tscn");
	}

    public void OnChatMessageSubmit(string msg)
    {
        // Send public message to Room
        sfs.Send(new PublicMessageRequest(msg));
    }
    #endregion

    //----------------------------------------------------------
    // Helper methods
    //----------------------------------------------------------
    #region
    private void AddSmartFoxListeners()
    {
        sfs.AddEventListener(SFSEvent.PUBLIC_MESSAGE, OnPublicMessage);
        sfs.AddEventListener(SFSEvent.USER_ENTER_ROOM, OnUserEnterRoom);
        sfs.AddEventListener(SFSEvent.USER_EXIT_ROOM, OnUserExitRoom);
		sfs.AddEventListener(SFSEvent.SPECTATOR_TO_PLAYER, OnSpectatorToPlayer);
        sfs.AddEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);
		sfs.AddEventListener(SFSBuddyEvent.BUDDY_ADD, OnBuddyAdd);
    }

    public override void RemoveSmartFoxListeners()
    {
        if (sfs != null)
        {
            sfs.RemoveEventListener(SFSEvent.PUBLIC_MESSAGE, OnPublicMessage);
            sfs.RemoveEventListener(SFSEvent.USER_ENTER_ROOM, OnUserEnterRoom);
            sfs.RemoveEventListener(SFSEvent.USER_EXIT_ROOM, OnUserExitRoom);
		    sfs.RemoveEventListener(SFSEvent.SPECTATOR_TO_PLAYER, OnSpectatorToPlayer);
            sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_MESSAGE, OnBuddyMessage);
		    sfs.RemoveEventListener(SFSBuddyEvent.BUDDY_ADD, OnBuddyAdd);
        }
    }
    #endregion

    //----------------------------------------------------------
    // SmartFoxServer event listeners
    //----------------------------------------------------------
    #region
    private void OnPublicMessage(ApiEvent evt)
    {
        User sender = (User) evt.Params[EventParam.Sender];
        string message = (string) evt.Params[EventParam.Message];

        // Display chat message
        chatPanel.PrintChatMessage(message, sender.Name, sender == sfs.MySelf, null);
    }

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

        // Display system message
        chatPanel.PrintSystemMessage("User " + user.Name + " joined this game as " + (user.IsPlayerInRoom(room) ? "player" : "spectator"));

        if (user.IsPlayerInRoom(room))
        {
            // Stop timeout
            runTimer = false;

            // Hide leave panel
            timeoutPanel.Visible = false;
        }
    }

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

        if (user != sfs.MySelf)
        {
            // Display system message
            chatPanel.PrintSystemMessage("User " + user.Name + " left the game");
        }
    }
    
    private void OnSpectatorToPlayer(ApiEvent evt)
    {
        User user = (User)evt.Params[EventParam.User];

        // Display system message
        if (user == sfs.MySelf)
            chatPanel.PrintSystemMessage("Game joined as player");
        else
        {
            chatPanel.PrintSystemMessage("User " + user.Name + " is now a player");

            if (user.IsPlayer)
            {
                // Stop timeout
                runTimer = false;

                // Hide leave panel
                timeoutPanel.Visible = false;
            }
        }
    }

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

        // 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)
        BuddyChatHistoryManager.IncreaseUnreadMessagesCount(chatMsg.Target);
    }

    private void OnBuddyAdd(ApiEvent evt)
    {
        Buddy buddy = (Buddy)evt.Params[EventParam.Buddy];
        User user = sfs.LastJoinedRoom.GetUserByName(buddy.Name);
        
        // When a buddy is added, hide Add Buddy button in game interface
		gameManager.UpdatePlayerTags();
    }
    #endregion
}