using System;
using System.Threading;
using Microsoft.SPOT;

namespace WIFI
{
    // Authentication
    public class Auth
    {

        // Stores the last username
        private string _Username = "";

        // True when use is logged in
        private bool _Authorized = false;

        // Amount of password retries (default: 3)
        public int Retries { get; set; }

        // The shell
        private ShellCore _Shell;

        // Creates a new authorization module
        public Auth(ShellCore Shell)
        {
            // Sets some default values
            this.Retries = 3;
            this.TimeToLogin = 0;
            this._Shell = Shell;

            // Binds to the shell
            Shell.OnConnected += new ShellCore.ConnectionStateChange(this._Shell_OnConnected);
            Shell.OnCommandReceived += new ShellCore.CommandReceived(this._Shell_OnCommandReceived);
        }

        // Unbinds the shell
        public void Dispose()
        {
            // Unbinds from the shell
            this._Shell.OnConnected -= new ShellCore.ConnectionStateChange(this._Shell_OnConnected);
            this._Shell.OnCommandReceived -= new ShellCore.CommandReceived(this._Shell_OnCommandReceived);
        }


        // Time to login in seconds (default: 0)
        public int TimeToLogin { get; set; }
        
        // New connection has been made
        private void _Shell_OnConnected(ShellCore Shell, string RemoteAddress, DateTime Time)
        {
            if (this.OnAuthorize == null)
            {
                Shell.TelnetServer.Print("Auth.OnAuthorize is not defined, so authorization is inactive");
                return;
            }

            // Resets the current login
            this._Authorized = false;
            this._Username = "";

            // Adds the timer to check for a login timeout
            if (this.TimeToLogin > 0)
                new Timer(this._LoginTimedout, null, this.TimeToLogin * 1000, 0);

            for (int i = 0; i < Retries; ++i)
            {
                // Asks for the username
                Shell.TelnetServer.Print("Username: ", true);
                string Username = Shell.TelnetServer.Input().Trim();
                if (Username == "") continue;

                // Asks for the password (with echoing disabled!)
                Shell.TelnetServer.Print("Password: ", true);
                Shell.TelnetServer.EchoEnabled = false;
                string Password = Shell.TelnetServer.Input().Trim();
                Shell.TelnetServer.EchoEnabled = true;
                Shell.TelnetServer.Print("");

                // Verifies the details
                if (this.OnAuthorize(Shell, Username, Password, RemoteAddress))
                {
                    this._Username = Username;
                    this._Authorized = true;
                    return;
                }
                Shell.TelnetServer.Print("Username and/or password incorrect", false, true);
                Shell.TelnetServer.Print("");
            }
            // No valid login has been given
            Shell.TelnetServer.Print("Closing the connection");
            Thread.Sleep(100);
            Shell.TelnetServer.Close();
        }

        // Triggered when a command has been given
        private void _Shell_OnCommandReceived(ShellCore Shell, string[] Arguments, ref bool SuspressError, DateTime Time)
        {
            if (Arguments[0].ToUpper() == "WHOAMI")
            {
                Shell.TelnetServer.Print("Logged in as " + this._Username);
                SuspressError = true;
            }
            if (Arguments[0].ToUpper() == "HELP")
            {
                if (Arguments.Length == 1 || Arguments[1].ToUpper() == "WHOAMI")
                {
                    Shell.TelnetServer.Print("WHOAMI                             Returns the current username");
                    SuspressError = true;
                }
            }
        }

        // Triggered after this.TimeToLogin seconds to optionally close a connection
        private void _LoginTimedout(object Param)
        {
            if (this._Authorized) return ;
            this._Shell.TelnetServer.Print("", false, true);
            this._Shell.TelnetServer.Print("Login timeout");
            Thread.Sleep(100);
            this._Shell.TelnetServer.Close();
        }

        // Asks if a user is authorized
        public event IsAuthorized OnAuthorize;

        // Asks if a user is authorized
        public delegate bool IsAuthorized(ShellCore Shell, string Username, string Password, string RemoteAddress);
    }
}
