using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.SQLite;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BulkPrinting
{
    public partial class UserLoginForm : Form
    {
        public UserLoginForm()
        {
            InitializeComponent();
        }

        private void btnLogin_Click(object sender, EventArgs e)
        {
            bool LoginSuccessful = false;
            LoginData UserLoginData = new LoginData();
            UserLoginData.VendorId = int.Parse(this.txtVendorID.Text);
            UserLoginData.SerialNumber = Globals.HDDSerialNumber;
            UserLoginData.UserId = int.Parse(this.txtUserID.Text);
            UserLoginData.Username = this.txtUsername.Text;
            UserLoginData.SecurePassword = new System.Security.SecureString();
            foreach (var c in txtPassword.Text)
            {
                UserLoginData.SecurePassword.AppendChar(c);
            }
            UserLoginData.SecurePassword.MakeReadOnly();
            if (chkOffline.Checked == true) {
                byte[] userKey;
                if (Globals.SessionSalt == null || Globals.SessionIterations == 0){
                    MessageBox.Show("It looks like this user hasn't performed an online login today or since the application started. You must perform an online login at least once during the day to use the offline feature. To use offline mode, please remember not to close the application after each session.", "Cannot perform offline login", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                using (var derivedBytes = new System.Security.Cryptography.Rfc2898DeriveBytes(
                    UserLoginData.Password + "shie5heeX6pekaehovuS2yu0Ciejah7a",
                    Globals.SessionSalt,
                    Globals.SessionIterations))
                {
                    userKey = derivedBytes.GetBytes(32);
                }
                try
                {
                    Globals.SessionDatabasePassword = Utility.AesDecryptBytes(Globals.SessionEncryptedDatabasePassword, userKey);
                }
                catch (Exception) {
                    MessageBox.Show("An error occured while logging in in offline mode. Please ensure that you have entered the correct user details and password. If the problem persists, please uncheck 'Offline' before logging in again.", "Application error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }

                if (!File.Exists(Globals.MaxDBFilePath))
                {
                    //Shouldn't get here unless the database is deleted or moved
                    MessageBox.Show("A problem has been detected and online initialisation must be performed. You cannot use offline mode until this has been completed.", "Application error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return;
                }
                else
                {
                    Globals.DB = Utility.OpenDBConnection();
                    //Test login success
                    Globals.SessionMode = SessionModes.Offline;
                    Globals.SessionData = JsonConvert.DeserializeObject<OKResponse>(Convert.ToString(Globals.DB.ExecuteScalar(
                        "SELECT Value FROM SessionData WHERE Key = 'SessionDataJson'")));
                    Globals.SessionVoucherKey = Utility.AesDecryptBytes(Globals.SessionData.Credentials.Payload.EncryptedVoucherKey, userKey);
                    DateTime ServerDate = Globals.SessionData.Credentials.Payload.Date;
                    DateTime Today = DateTime.Now;
                    if (ServerDate.Date != Today.Date) {
                        MessageBox.Show("It looks like this user hasn't performed an online login today or since the application started. You must perform an online login at least once during the day to use the offline feature. To use offline mode, please remember not to close the application after each session.", "Cannot perform offline login", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        return;
                    }
                    Utility.LogEvent(Globals.DB, VendorEvent.VendorEventType.OfflineLogin);
                    LoginSuccessful = true;
                }
            }
            else
            {
                if (Utility.Login(UserLoginData, chkOffline.Checked, chkRemember.Checked))
                {
                    byte[] userKey;
                    LoginSuccessful = true;
                    Globals.SessionMode = SessionModes.Online;
                    using (var derivedBytes = new System.Security.Cryptography.Rfc2898DeriveBytes(
                        UserLoginData.Password + "shie5heeX6pekaehovuS2yu0Ciejah7a",
                        Globals.SessionData.Credentials.Salt,
                        Globals.SessionData.Credentials.Iterations))
                    {
                        userKey = derivedBytes.GetBytes(32);
                    }

                    byte[] signature;
                    using (var hmac = new System.Security.Cryptography.HMACSHA256() { Key = userKey })
                    {
                        signature = hmac.ComputeHash(Encoding.ASCII.GetBytes(Globals.SessionData.Credentials.responsePayload));
                    }

                    if (!Globals.SessionData.Credentials.Signature.SequenceEqual(signature))
                    {
                        MessageBox.Show("The login attempt failed. Please try again.", "Login Failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        Globals.SessionDatabasePassword = Utility.AesDecryptBytes(Globals.SessionEncryptedDatabasePassword, userKey);
                        Globals.SessionVoucherKey = Utility.AesDecryptBytes(Globals.SessionData.Credentials.Payload.EncryptedVoucherKey, userKey);

                        if (!File.Exists(Globals.MaxDBFilePath))
                        {
                            if (!Directory.Exists(Globals.MaxDBPath))
                            {
                                Directory.CreateDirectory(Globals.MaxDBPath);
                            }
                            SQLiteConnection.CreateFile(Globals.MaxDBFilePath);
                            Globals.DB = Utility.OpenDBConnection();
                            Migrations.InitialVersion(Globals.DB);
                        }
                        else
                        {
                            Globals.DB = Utility.OpenDBConnection();
                        }
                        Migrations.CheckMigrations(Globals.DB);
                        Utility.CheckLogSynchronisation(Globals.DB);
                        Utility.TriggerLogDownload();
                        Utility.TriggerLogUpload();
                    }
                }
            }
            if (LoginSuccessful == true)
            {
                //TODO: Remove - for testing purposes only
                /*
                Globals.SessionData.Credentials.Payload.User.Level = (int)UserLevel.CustomUser;
                Globals.SessionData.Credentials.Payload.User.CanPrintOnline = false;
                Globals.SessionData.Credentials.Payload.User.CanPrintOffline = true;
                Globals.SessionData.Credentials.Payload.User.CanReprintOnline = true;
                Globals.SessionData.Credentials.Payload.User.CanReprintOffline = true;
                Globals.SessionData.Credentials.Payload.User.BulkExport = false;
                Globals.SessionData.Credentials.Payload.User.BulkOrder = true;
                Globals.SessionData.Credentials.Payload.User.BulkViewPins = true;
                Globals.SessionData.Credentials.Payload.User.BulkOrderMaxValue = 1000;
                Globals.SessionData.Credentials.Payload.User.BulkExportMaxValue = 0;
                Globals.SessionData.Credentials.Payload.User.OnlinePrintValue = 0;
                Globals.SessionData.Credentials.Payload.User.OfflinePrintValue = 0;
                Globals.SessionData.Credentials.Payload.User.OnlineReprintValue = 0;
                Globals.SessionData.Credentials.Payload.User.OfflineReprintValue = 0;
                */
                Globals.DB.ExecuteNonQuery("DELETE FROM SessionData"); //Destroy stored session data on login - keep in memory until logout
                Globals.MaxPrinter = new Printer();
                this.txtPassword.Text = "";
                this.Hide();
                BatchForm MainWindow = new BatchForm();
                MainWindow.Show();
            }


        }

        private void UserLoginForm_Load(object sender, EventArgs e)
        {
            lblVersion.Text = Application.ProductVersion; // Configurable via AssemblyFileVersion in AssemblyInfo.cs
            lblHDDSerial.Text = Globals.HDDSerialNumber;
            txtUsername.Text = Utility.LoadSetting("Username");
            txtUserID.Text = Utility.LoadSetting("UserID");
            if (txtUsername.Text != "") {
                chkRemember.Checked = true;
            }
            txtVendorID.Text = Utility.LoadSetting("VendorID");
            if (txtVendorID.Text != "")
            {
                txtVendorID.Enabled = false;
            }
            CenterToScreen();
        }

        private void txtVendorID_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
            {
                e.Handled = true;
            }
        }

        private void UserLoginForm_Shown(object sender, EventArgs e)
        {
        }

        private void UserLoginForm_VisibleChanged(object sender, EventArgs e)
        {
            if (this.Visible == true) {
                List<Form> OpenForms = new List<Form>();

                foreach (Form f in Application.OpenForms)
                    OpenForms.Add(f);

                foreach (Form f in OpenForms)
                {
                    if (f.Name != "UserLoginForm")
                        f.Close();
                }
            }
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {

        }

        private void label4_Click(object sender, EventArgs e)
        {

        }

        private void label3_Click(object sender, EventArgs e)
        {

        }

        private void label2_Click(object sender, EventArgs e)
        {

        }

        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void label5_Click(object sender, EventArgs e)
        {

        }

        private void chkRemember_CheckedChanged(object sender, EventArgs e)
        {

        }

        private void chkOffline_CheckedChanged(object sender, EventArgs e)
        {

        }
    }
}
