using Godot;

using Sfs3.Client;
using Sfs3.Client.Core;
using Sfs3.Client.Logging;
using Sfs3.Client.Requests;
using Sfs3.Client.Util;
using System;
using System.ComponentModel;

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

    [ExportCategory("SFS3 Connection Settings")]

    [Export, Description("IP address or domain name of the SmartFoxServer instance; if encryption is enabled, a domain name must be entered")]
    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("HTTPS listening port of the SmartFoxServer instance, used when connection encryption in enabled")]
    public int httpsPort = 8843;
    [Export, Description("Use SmartFoxServer's HTTP tunneling (BlueBox) if TCP socket connection can't be established")]
    public bool useHttpTunnel = false;
    [Export, Description("Enable SmartFoxServer protocol encryption; 'host' must be a domain name and an SSL certificate must have been deployed")]
    public bool encrypt = false;
    [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 Label errorLabel;
    [Export]
    public LineEdit nameInput;
    [Export]
    public Button loginButton;
    [Export]
    public Control mainContainer;
    [Export]
    public Label nameLabel;

    private SmartFox sfs;

    //----------------------------------------------------------
    // Callback Methods
    //----------------------------------------------------------
    #region
    public override void _Ready()
    {
        // Focus on username input
        nameInput.GrabFocus();
    }

    public override void _Process(double delta)
    {
        // Process the SmartFox events queue
        if (sfs != null)
            sfs.ProcessEvents();
    }

    public override void _Notification(int what)
    {
        if (what == NotificationWMCloseRequest)
        {
            if (sfs != null && sfs.IsConnected)
                sfs.Disconnect();

            Trace("Application quit");
        }
    }
    #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();
    }

    /**
	 * On Logout button click, disconnect from SmartFoxServer.
	 */
    public void OnLogoutButtonClick()
    {
        sfs.Disconnect();
    }
    #endregion

    //----------------------------------------------------------
    // Helper methods
    //----------------------------------------------------------
    #region
    private void Trace(string str)
    {
        GD.PrintRich(String.Format("[color=#6FBFF9]{0}[color=white]", str));
    }

    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 SwitchView()
    {
        loginContainer.Visible = !loginContainer.Visible;
        mainContainer.Visible = !mainContainer.Visible;
    }

    private void Connect()
    {
        Trace("Attempting connection to SmartFoxServer...");

        // Disable user interface
	    EnableUI(false);

        // Clear any previour error message
        SetErrorMessage();

        // Initialize SmartFox client
        sfs = new SmartFox();

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

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

        // Set connection parameters
        var cfg = new ConfigData();
        cfg.Host = host;
        cfg.Port = tcpPort;
        cfg.Zone = zone;
        cfg.HttpPort = httpPort;
        cfg.HttpsPort = httpsPort;
        cfg.UseSSL = encrypt;
        cfg.NetDebugLevel = debug ? NetDebugLevel.PROTOCOL : NetDebugLevel.OFF;
        cfg.BlueBox.IsActive = useHttpTunnel;
        cfg.BlueBox.UseHttps = encrypt;

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

    private void Login()
    {
        Trace("Performing login...");

        // Login
        sfs.Send(new LoginRequest(nameInput.Text));
    }
    #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)
        {
            Trace("Connection established successfully");
            Trace("SFS3 API version: " + sfs.Version);
            Trace("Connection mode is: " + sfs.ConnectionMode);

            // Attempt login
            Login();
        }
        else
        {
            // Show error message
            SetErrorMessage("Connection failed; is the server running at all?");

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

    private void OnConnectionLost(ApiEvent evt)
    {
        // Show login view
        SwitchView();

        // Show error message
        string reason = (string)evt.Params[EventParam.DisconnectionReason];

        Trace("Connection to SmartFoxServer lost; reason is: " + reason);

        if (reason != ClientDisconnectionReason.MANUAL)
        {
            // Show error message
            string connLostMsg = "An unexpected disconnection occurred; ";

            if (reason == ClientDisconnectionReason.IDLE)
                connLostMsg += "you have been idle for too much time.";
            else if (reason == ClientDisconnectionReason.KICKED)
                connLostMsg += "you have been kicked.";
            else if (reason == ClientDisconnectionReason.BANNED)
                connLostMsg += "you have been banned.";
            else
                connLostMsg += "reason is unknown.";

            SetErrorMessage(connLostMsg);
        }

        // Enable user interface
        EnableUI(true);
    }

    private void OnLogin(ApiEvent evt)
    {
        Trace("Login successful");

        // Display user name
        nameLabel.Text = sfs.MySelf.Name;

        // Update view
        SwitchView();
    }


    private void OnLoginError(ApiEvent evt)
    {
        Trace("Login failed");

        // Disconnect
        // NOTE: this causes a CONNECTION_LOST event with reason "manual"
        sfs.Disconnect();

        // Show error message
        SetErrorMessage("Login failed due to the following error: " + evt.Params[EventParam.ErrorMessage]);

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