using Godot;
using Sfs3.Client;
using Sfs3.Client.Core;
using Sfs3.Client.Logging;
using Sfs3.Client.Requests;
using Sfs3.Client.Util;

using System.ComponentModel;

/**
 * Script attached to the main node in the scene.
 */
public partial class LoginController : BaseController
{
    //----------------------------------------------------------
    // Editor public properties
    //----------------------------------------------------------

    [ExportCategory("SFS3 Connection Settings")]

    [Export, Description("IP address or domain name of the SmartFoxServer instance")]
    public string host = "127.0.0.1";
    [Export, Description("TCP listening port of the SmartFoxServer instance")]
    public int tcpPort = 9977;
    [Export, Description("HTTP listening port of the SmartFoxServer instance")]
    public int httpPort = 8088;
    [Export, Description("Name of the SmartFoxServer Zone to join")]
    public string zone = "Playground";
    [Export, Description("Display SmartFoxServer client debug messages")]
    public bool debug = false;
    [Export, Description("Client-side SmartFoxServer logging level")]
    public LogLevel logLevel = LogLevel.INFO;

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

    [ExportCategory("UI Settings")]

    [Export]
    public Control loginContainer;
    [Export]
    public LineEdit nameInput;
    [Export]
    public Button loginButton;
    [Export]
    public Label errorLabel;

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

        // Enable UI
        EnableUI(true);

        // Focus on username input
        nameInput.GrabFocus();

        // Show last disconnection message if any
        SetErrorMessage(Global.LastDisconnectionMsg);
    }
    #endregion

    //----------------------------------------------------------
    // UI event listeners
    //----------------------------------------------------------
    #region
    /**
	 * On Login button click, connect to SmartFoxServer.
	 */
    public void OnLoginButtonClick()
    {
        Connect();
    }

    /**
	 * On Name input submit, connect to SmartFoxServer.
	 */
    public void OnNameInputSubmit(string name)
    {
        Connect();
    }
    #endregion

    //----------------------------------------------------------
    // Helper methods
    //----------------------------------------------------------
    #region

    private void EnableUI(bool enable)
    {
        nameInput.Editable = enable;
        loginButton.Disabled = !enable;
    }

    private void SetErrorMessage(string msg = null)
    {
        if (msg == null || msg.Length == 0)
        {
            errorLabel.Text = "";
            errorLabel.Visible = false;
        }
        else
        {
            errorLabel.Text = msg;
            errorLabel.Visible = true;
        }
    }

    private void Connect()
    {
        Global.Trace("Attempting connection to SmartFoxServer...");
         
        // Disable user interface
        EnableUI(false);

        // Clear any previour error message
        SetErrorMessage();

        // Set connection parameters
        ConfigData cfg = new ConfigData();
        cfg.Host = host;
        cfg.Port = tcpPort;
        cfg.Zone = zone;
        cfg.NetDebugLevel = debug ? NetDebugLevel.PROTOCOL : NetDebugLevel.OFF;
  
        // Initialize SmartFox client
        sfs = Global.CreateSfsClient();

        // Configure internal logger
        sfs.Logger.Level = logLevel;

        // Add event listeners
        AddSmartFoxListeners();

        // Connect to SmartFoxServer
        sfs.Connect(cfg);
    }

    private void AddSmartFoxListeners()
    {
        // Add event listeners
        sfs.AddEventListener(SFSEvent.CONNECTION, OnConnection);
        sfs.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
        sfs.AddEventListener(SFSEvent.LOGIN, OnLogin);
        sfs.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
        sfs.AddEventListener(SFSEvent.UDP_CONNECTION, OnUdpInit);
    }

    public override void RemoveSmartFoxListeners()
    {
        // NOTE
        // If this scene is stopped before a connection is established, the SmartFox client instance
        // could still be null, causing an error when trying to remove its listeners

        if (sfs != null)
        {
            sfs.RemoveEventListener(SFSEvent.CONNECTION, OnConnection);
            sfs.RemoveEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
            sfs.RemoveEventListener(SFSEvent.LOGIN, OnLogin);
            sfs.RemoveEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
            sfs.RemoveEventListener(SFSEvent.UDP_CONNECTION, OnUdpInit);
        }
    }
    #endregion

    //----------------------------------------------------------
    // SmartFoxServer event listeners
    //----------------------------------------------------------
    #region
    private void OnConnection(ApiEvent evt)
    {
        bool success = (bool)evt.Params[EventParam.Success];

        // Check if the conenction was established or not
        if (success)
        {
            Global.Trace("SFS3 API version: " + sfs.Version);

            // Login
            sfs.Send(new LoginRequest(this.nameInput.Text));
        }
        else
        {
            // Show error message
            SetErrorMessage("Connection failed; is the server running at all?");

            // Enable user interface
            EnableUI(true);
        }
    }

    private void OnConnectionLost(ApiEvent evt)
    {
        // Enable user interface
        EnableUI(true);
    }

    private void OnLogin(ApiEvent evt)
    {
        // Initialize UDP communication
        sfs.ConnectUdp();
    }

    private void OnLoginError(ApiEvent evt)
    {
        // Disconnect
        // NOTE: this causes a CONNECTION_LOST event with reason "manual"
        sfs.Disconnect();

        string msg = (string)evt.Params[EventParam.ErrorMessage];

        // Show error message
        if (msg != null)
            SetErrorMessage("Login failed due to the following error:\n" + msg + ".");
        else
            SetErrorMessage("Login failed; no reason provided.");

        // Enable user interface
        EnableUI(true);
    }

    private void OnUdpInit(ApiEvent evt)
    {
        bool success = (bool)evt.Params[EventParam.Success];
		
        if (success)
        {
            Global.Trace("UDP initialization successful");
            
            // Load lobby scene
	        GetTree().ChangeSceneToFile("scenes/Lobby.tscn");
        }
        else
        {
            // Disconnect
            // NOTE: this causes a CONNECTION_LOST event with reason "manual"
            sfs.Disconnect();

            // Show error message
            string msg = (string)evt.Params[EventParam.ErrorMessage];
            SetErrorMessage("UDP initialization failed due to the following error:\n" + msg + ".");
        }
    }
    #endregion
}