using System.Diagnostics;
using System.Net;
using System.Net.Security;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.RegularExpressions;
using DnsClient;
using DnsClient.Protocol;
using Newtonsoft.Json.Linq;
using QRCoder;
using Point = System.Drawing.Point;
using Size = System.Drawing.Size;

namespace FireWallet
{
    public partial class MainForm : Form
    {
        #region Variables
        // Main directory for the application files
        public string dir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\FireWallet\\";
        // Settings and theme key value pairs
        public Dictionary<string, string> NodeSettings { get; set; }
        public Dictionary<string, string> UserSettings { get; set; }
        public Dictionary<string, string> Theme { get; set; }
        // HSD & Wallet settings
        public bool WatchOnly { get; set; }
        public bool HSD { get; set; }
        public Process HSDProcess { get; set; }
        public int HSDNetwork { get; set; }
        public string Account { get; set; }
        public string Password { get; set; }
        // Wallet information
        public decimal Balance { get; set; }
        public decimal Balance_Locked { get; set; }
        // Batching variables
        public bool BatchMode { get; set; }
        public BatchForm BatchForm { get; set; }
        public bool multiSig { get; set; }
        #endregion
        #region Application
        public MainForm()
        {
            InitializeComponent();
            panelaccount.Visible = true;
        }
        private async void MainForm_Load(object sender, EventArgs e)
        {
            // Set initial values
            WatchOnly = false;
            Account = "";

            // Stop timers and load settings
            timerNodeStatus.Stop();
            LoadSettings();

            // Show splash
            SplashScreen ss = new SplashScreen(false);
            bool splash = false;
            if (UserSettings.ContainsKey("hide-splash"))
            {
                if (UserSettings["hide-splash"] == "false")
                {
                    // Show splash screen
                    ss.Show();
                    splash = true;
                }
            }
            else
            {
                // Show splash screen
                ss.Show();
                splash = true;
            }
            // Record time
            DateTime start = DateTime.Now;
            // Do form events
            Application.DoEvents();

            // Load theme
            UpdateTheme();
            // Theme drop down
            foreach (ToolStripItem item in toolStripDropDownButtonHelp.DropDownItems)
            {
                if (item is ToolStripDropDownItem dropDownItem)
                {
                    dropDownItem.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
                    dropDownItem.BackColor = ColorTranslator.FromHtml(Theme["background"]);
                }
            }
            toolStripDropDownButtonHelp.DropDown.BackColor = ColorTranslator.FromHtml(Theme["background"]);

            // Load node
            if (await LoadNode(ss) != true) this.Close();

            // If node load caused app to close, exit load function
            if (this.Disposing || this.IsDisposed) return;

            // Edit the theme of the navigation panel
            panelNav.BackColor = ColorTranslator.FromHtml(Theme["background-alt"]);
            panelNav.ForeColor = ColorTranslator.FromHtml(Theme["foreground-alt"]);
            foreach (Control c in panelNav.Controls)
            {
                c.BackColor = ColorTranslator.FromHtml(Theme["background"]);
                c.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
            }
            panelNav.Dock = DockStyle.Left;

            ResizeForm();
            panelNav.Visible = false;

            // Prompt for login
            GetAccounts();
            BatchMode = false;

            if (splash)
            {
                // If not internal node
                if (!HSD)
                {
                    // Wait until the splash has been visible for 4 seconds
                    if ((DateTime.Now - start).TotalSeconds > 4) ss.CloseSplash();
                    else Thread.Sleep(4000 - (int)(DateTime.Now - start).TotalMilliseconds);
                    ss.CloseSplash();
                }
                else
                {
                    // Wait until the Node is connected before closing the splash
                    while (true)
                    {
                        string status = await APIGet("", false);
                        if (status != "Error")
                        {
                            ss.CloseSplash();
                            GetAccounts();
                            break;
                        }
                        else
                        {
                            Thread.Sleep(100);
                        }
                    }
                }
                Application.DoEvents();

                while (!ss.IsClosed)
                {
                    Thread.Sleep(100);
                    Application.DoEvents();
                }
            }
            AddLog("Loaded");


            // Pull form to front            
            this.WindowState = FormWindowState.Minimized;
            this.Show();
            this.Opacity = 1;
            this.WindowState = FormWindowState.Normal;
            textBoxaccountpassword.Focus();
        }
        private void MainForm_Closing(object sender, FormClosingEventArgs e)
        {
            // Close HSD before closing FireWallet
            AddLog("Closing");
            if (HSDProcess != null)
            {
                this.Opacity = 0;
                HSDProcess.Kill();
                AddLog("HSD Closed");
                Thread.Sleep(1000);
                try
                {
                    HSDProcess.Dispose();
                }
                catch
                {
                    AddLog("Dispose failed");
                }
            }
        }
        #endregion
        #region Settings
        private async Task<bool> LoadNode(SplashScreen? ss)
        {
            HSD = false;
            if (!File.Exists(dir + "node.txt"))
            {
                ss.Hide();
                NodeForm cf = new NodeForm();
                timerNodeStatus.Stop();
                cf.ShowDialog();
                timerNodeStatus.Start();
                ss.Show();
            }
            if (!File.Exists(dir + "node.txt"))
            {
                AddLog("Node setup failed");
                this.Close();
                await Task.Delay(1000);
                AddLog("Close Failed");
            }

            StreamReader sr = new StreamReader(dir + "node.txt");
            NodeSettings = new Dictionary<string, string>();
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
                string[] split = line.Split(':');
                NodeSettings.Add(split[0].Trim(), split[1].Trim());

            }
            sr.Dispose();

            if (!NodeSettings.ContainsKey("Network") || !NodeSettings.ContainsKey("Key") || !NodeSettings.ContainsKey("IP"))
            {
                AddLog("Node Settings file is missing key");
                this.Close();
                await Task.Delay(1000);
                AddLog("Close Failed");
            }
            HSDNetwork = Convert.ToInt32(NodeSettings["Network"]);
            switch (HSDNetwork)
            {
                case 0:
                    toolStripStatusLabelNetwork.Text = "Network: Mainnet";
                    break;
                case 1:
                    toolStripStatusLabelNetwork.Text = "Network: Regtest (Not Fully Tested)";
                    break;
                case 2:
                    toolStripStatusLabelNetwork.Text = "Network: Testnet (Not Implemented)";
                    break;
            }

            if (NodeSettings.ContainsKey("Timeout"))
            {
                int timeout = Convert.ToInt32(NodeSettings["Timeout"]);
                httpClient.Timeout = TimeSpan.FromSeconds(timeout);
            }
            else httpClient.Timeout = TimeSpan.FromSeconds(10);

            if (NodeSettings.ContainsKey("HSD"))
            {
                if (NodeSettings["HSD"].ToLower() == "true")
                {
                    HSD = true;
                    AddLog("Starting HSD");
                    toolStripStatusLabelstatus.Text = "Status: HSD Starting";

                    string hsdPath = dir + "hsd\\bin\\hsd.exe";
                    if (NodeSettings.ContainsKey("HSD-command"))
                    {
                        if (NodeSettings["HSD-command"].Contains("{default-dir}"))
                        {
                            if (!Directory.Exists(dir + "hsd"))
                            {
                                NotifyForm Notifyinstall = new NotifyForm("Installing hsd\nThis may take a few minutes\nDo not close FireWallet", false);
                                Notifyinstall.Show();
                                // Wait for the notification to show
                                await Task.Delay(1000);

                                string repositoryUrl = "https://github.com/handshake-org/hsd.git";
                                string destinationPath = dir + "hsd";
                                CloneRepository(repositoryUrl, destinationPath);

                                Notifyinstall.CloseNotification();
                                Notifyinstall.Dispose();
                            }
                            if (!Directory.Exists(dir + "hsd\\node_modules"))
                            {
                                AddLog("HSD install failed");
                                this.Close();
                                return false;
                            }
                        }
                    }
                    else
                    {
                        if (!Directory.Exists(dir + "hsd"))
                        {
                            NotifyForm Notifyinstall = new NotifyForm("Installing hsd\nThis may take a few minutes\nDo not close FireWallet", false);
                            Notifyinstall.Show();
                            // Wait for the notification to show
                            await Task.Delay(1000);

                            string repositoryUrl = "https://github.com/handshake-org/hsd.git";
                            string destinationPath = dir + "hsd";
                            CloneRepository(repositoryUrl, destinationPath);

                            Notifyinstall.CloseNotification();
                            Notifyinstall.Dispose();
                        }
                        if (!Directory.Exists(dir + "hsd\\node_modules"))
                        {
                            AddLog("HSD install failed");
                            this.Close();
                            return false;
                        }
                    }



                    HSDProcess = new Process();

                    bool hideScreen = true;
                    if (NodeSettings.ContainsKey("HideScreen"))
                    {
                        if (NodeSettings["HideScreen"].ToLower() == "false")
                        {
                            hideScreen = false;
                        }
                    }
                    try
                    {
                        HSDProcess.StartInfo.CreateNoWindow = hideScreen;

                        if (hideScreen)
                        {
                            HSDProcess.StartInfo.RedirectStandardError = true;
                            // Send errors to log
                            HSDProcess.ErrorDataReceived += (sender, e) => AddLog("HSD Error: " + e.Data);
                        }
                        else
                        {
                            HSDProcess.StartInfo.RedirectStandardError = false;
                        }

                        HSDProcess.StartInfo.RedirectStandardInput = true;
                        HSDProcess.StartInfo.RedirectStandardOutput = false;
                        HSDProcess.StartInfo.UseShellExecute = false;
                        HSDProcess.StartInfo.FileName = "node.exe";

                        if (NodeSettings.ContainsKey("HSD-command"))
                        {
                            AddLog("Using custom HSD command");
                            string command = NodeSettings["HSD-command"];
                            command = command.Replace("{default-dir}", dir + "hsd\\bin\\hsd");

                            string bobPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Bob\\hsd_data";
                            if (Directory.Exists(bobPath))
                            {
                                command = command.Replace("{Bob}", bobPath);
                            }
                            else if (command.Contains("{Bob}"))
                            {
                                AddLog("Bob not found, using default HSD command");
                                command = dir + "hsd\\bin\\hsd --agent=FireWallet --index-tx --index-address --api-key " + NodeSettings["Key"];
                            }

                            command = command.Replace("{key}", NodeSettings["Key"]);
                            HSDProcess.StartInfo.Arguments = command;
                        }
                        else
                        {
                            AddLog("Using default HSD command");
                            HSDProcess.StartInfo.Arguments = dir + "hsd\\bin\\hsd --agent=FireWallet --index-tx --index-address --api-key " + NodeSettings["Key"];
                            string bobPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Bob\\hsd_data";
                            if (Directory.Exists(bobPath))
                            {
                                HSDProcess.StartInfo.Arguments = HSDProcess.StartInfo.Arguments + " --prefix " + bobPath;
                            }
                        }




                        HSDProcess.Start();
                        // Wait for HSD to start
                        await Task.Delay(2000);

                        // Check if HSD is running
                        if (HSDProcess.HasExited)
                        {
                            AddLog("HSD Failed to start");
                            AddLog(HSDProcess.StandardError.ReadToEnd());
                            NotifyForm Notifyinstall = new NotifyForm("HSD Failed to start\nPlease check the logs");
                            Notifyinstall.ShowDialog();
                            Notifyinstall.Dispose();

                            // Wait for the notification to show
                            await Task.Delay(1000);
                            this.Close();

                            await Task.Delay(1000);
                            return false;
                        }

                    }
                    catch (Exception ex)
                    {
                        AddLog("HSD Failed to start");
                        AddLog(ex.Message);
                        this.Close();
                        await Task.Delay(1000);
                    }
                }
            }
            timerNodeStatus.Start();
            NodeStatus();
            return true;

        }
        private void LoadSettings()
        {
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            if (!File.Exists(dir + "settings.txt"))
            {
                AddLog("Creating settings file");
                StreamWriter sw = new StreamWriter(dir + "settings.txt");
                sw.WriteLine("explorer-tx: https://niami.io/tx/");
                sw.WriteLine("explorer-addr: https://niami.io/address/");
                sw.WriteLine("explorer-block: https://niami.io/block/");
                sw.WriteLine("explorer-domain: https://niami.io/domain/");
                sw.WriteLine("confirmations: 1");
                sw.WriteLine("portfolio-tx: 20");
                sw.WriteLine("hide-splash: false");
                sw.Dispose();
            }


            StreamReader sr = new StreamReader(dir + "settings.txt");
            UserSettings = new Dictionary<string, string>();
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
                UserSettings.Add(line.Substring(0, line.IndexOf(":")).Trim(), line.Substring(line.IndexOf(":") + 1).Trim());
            }
            sr.Dispose();
        }


        #endregion
        #region Logging
        public void AddLog(string message)
        {
            if (message.Contains("Get Error: No connection could be made because the target machine actively refused it")) return;

            // If file size is over 1MB, rename it to old.log.txt
            if (File.Exists(dir + "log.txt"))
            {
                FileInfo fi = new FileInfo(dir + "log.txt");
                if (fi.Length > 1000000)
                {
                    if (File.Exists(dir + "old.log.txt")) File.Delete(dir + "old.log.txt"); // Delete old log file as it is super old
                    File.Move(dir + "log.txt", dir + "old.log.txt");
                }
            }

            StreamWriter sw = new StreamWriter(dir + "log.txt", true);
            sw.WriteLine(DateTime.Now.ToString() + ": " + message);
            sw.Dispose();
        }
        #endregion
        #region Theming
        private void UpdateTheme()
        {
            // Check if file exists
            if (!Directory.Exists(dir))
            {
                CreateConfig(dir);
            }
            if (!File.Exists(dir + "theme.txt"))
            {
                CreateConfig(dir);
            }

            // Read file
            StreamReader sr = new StreamReader(dir + "theme.txt");
            Theme = new Dictionary<string, string>();
            while (!sr.EndOfStream)
            {
                string line = sr.ReadLine();
                string[] split = line.Split(':');
                Theme.Add(split[0].Trim(), split[1].Trim());
            }
            sr.Dispose();

            if (!Theme.ContainsKey("background") || !Theme.ContainsKey("background-alt") || !Theme.ContainsKey("foreground") || !Theme.ContainsKey("foreground-alt"))
            {
                AddLog("Theme file is missing key");
                return;
            }

            // Apply theme
            this.BackColor = ColorTranslator.FromHtml(Theme["background"]);

            // Foreground
            this.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);


            // Need to specify this for each groupbox to override the black text
            foreach (Control c in Controls)
            {
                ThemeControl(c);
            }




            this.Width = Screen.PrimaryScreen.Bounds.Width / 5 * 3;
            this.Height = Screen.PrimaryScreen.Bounds.Height / 5 * 3;
            applyTransparency(Theme);

            ResizeForm();
        }
        public void ThemeControl(Control c)
        {
            if (c.GetType() == typeof(GroupBox) || c.GetType() == typeof(Panel))
            {
                c.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
                foreach (Control sub in c.Controls)
                {
                    ThemeControl(sub);
                }
            }
            if (c.GetType() == typeof(TextBox) || c.GetType() == typeof(Button)
                || c.GetType() == typeof(ComboBox) || c.GetType() == typeof(StatusStrip) || c.GetType() == typeof(ToolStrip)
                || c.GetType() == typeof(NumericUpDown))
            {
                c.ForeColor = ColorTranslator.FromHtml(Theme["foreground-alt"]);
                c.BackColor = ColorTranslator.FromHtml(Theme["background-alt"]);
            }
            if (c.GetType() == typeof(Panel)) c.Dock = DockStyle.Fill;
        }

        private void applyTransparency(Dictionary<string, string> theme)
        {
            if (theme.ContainsKey("transparent-mode"))
            {
                switch (theme["transparent-mode"])
                {
                    case "mica":
                        var accent = new AccentPolicy { AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND };
                        var accentStructSize = Marshal.SizeOf(accent);
                        var accentPtr = Marshal.AllocHGlobal(accentStructSize);
                        Marshal.StructureToPtr(accent, accentPtr, false);
                        var data = new WindowCompositionAttributeData
                        {
                            Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY,
                            SizeOfData = accentStructSize,
                            Data = accentPtr
                        };
                        User32.SetWindowCompositionAttribute(Handle, ref data);
                        Marshal.FreeHGlobal(accentPtr);
                        break;
                    case "key":
                        if (theme.ContainsKey("transparency-key"))
                        {
                            switch (theme["transparency-key"])
                            {
                                case "alt":
                                    this.TransparencyKey = ColorTranslator.FromHtml(theme["background-alt"]);
                                    break;
                                case "main":
                                    this.TransparencyKey = ColorTranslator.FromHtml(theme["background"]);
                                    break;
                                default:
                                    this.TransparencyKey = ColorTranslator.FromHtml(theme["transparency-key"]);
                                    break;
                            }
                        }
                        else
                        {
                            AddLog("No transparency-key found in theme file");
                        }
                        break;
                    case "percent":
                        if (theme.ContainsKey("transparency-percent"))
                        {
                            Opacity = Convert.ToDouble(theme["transparency-percent"]) / 100;
                        }
                        else
                        {
                            AddLog("No transparency-percent found in theme file");
                        }
                        break;
                }
            }
        }

        private void CreateConfig(string dir)
        {
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            StreamWriter sw = new StreamWriter(dir + "theme.txt");
            sw.WriteLine("background: #000000");
            sw.WriteLine("foreground: #8e05c2");
            sw.WriteLine("background-alt: #3e065f");
            sw.WriteLine("foreground-alt: #ffffff");
            sw.WriteLine("transparent-mode: off");
            sw.WriteLine("transparency-key: main");
            sw.WriteLine("transparency-percent: 90");
            sw.WriteLine("selected-bg: #000000");
            sw.WriteLine("selected-fg: #ffffff");
            sw.WriteLine("error: #ff0000");

            sw.Dispose();
            AddLog("Created theme file");
        }

        // Required for mica effect
        internal enum AccentState
        {
            ACCENT_DISABLED = 0,
            ACCENT_ENABLE_GRADIENT = 1,
            ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
            ACCENT_ENABLE_BLURBEHIND = 3,
            ACCENT_INVALID_STATE = 4
        }

        internal enum WindowCompositionAttribute
        {
            WCA_ACCENT_POLICY = 19
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct AccentPolicy
        {
            public AccentState AccentState;
            public int AccentFlags;
            public int GradientColor;
            public int AnimationId;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct WindowCompositionAttributeData
        {
            public WindowCompositionAttribute Attribute;
            public IntPtr Data;
            public int SizeOfData;
        }

        internal static class User32
        {
            [DllImport("user32.dll")]
            internal static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
        }
        private void MainForm_ResizeEnd(object sender, EventArgs e)
        {
            ResizeForm();
        }
        private void Form1_Resize(object sender, EventArgs e)
        {
            ResizeForm();
        }
        private void ResizeForm()
        {
            groupBoxaccount.Left = (this.ClientSize.Width - groupBoxaccount.Width) / 2;
            groupBoxaccount.Top = (this.ClientSize.Height - groupBoxaccount.Height) / 2;
            groupBoxDomains.Width = panelDomains.Width - 20;
            groupBoxDomains.Left = 10;
            groupBoxDomains.Height = panelDomains.Height - groupBoxDomains.Top - 10;
            buttonNavSettings.Top = panelNav.Height - buttonNavSettings.Height - 10;
            buttonSettingsSave.Top = panelSettings.Height - buttonSettingsSave.Height - 10;
            groupBoxTransactions.Height = panelPortfolio.Height - groupBoxbalance.Height - 10;

            // SEND Page
            labelSendPrompt.Left = (panelSend.Width - labelSendPrompt.Width) / 2;
            buttonSendHNS.Left = (panelSend.Width - buttonSendHNS.Width) / 2;
            labelSendingTo.Left = (panelSend.Width - labelSendingTo.Width - textBoxSendingTo.Width) / 2;
            labelSendingAmount.Left = labelSendingTo.Left;
            textBoxSendingTo.Left = labelSendingTo.Left + labelSendingTo.Width + 10;
            textBoxSendingAmount.Left = textBoxSendingTo.Left;
            labelSendingMax.Left = labelSendingTo.Left;
            labelSendingError.Left = textBoxSendingTo.Left + textBoxSendingTo.Width + 10;
            labelSendingFee.Left = labelSendingTo.Left;
            buttonSendMax.Left = textBoxSendingAmount.Left + textBoxSendingAmount.Width - buttonSendMax.Width;
            checkBoxSendSubFee.Left = labelSendingTo.Left;

            // RECEIVE Page
            labelReceive1.Left = (panelRecieve.Width - labelReceive1.Width) / 2;
            labelReceive2.Left = (panelRecieve.Width - labelReceive2.Width) / 2;
            textBoxReceiveAddress.Left = (panelRecieve.Width - textBoxReceiveAddress.Width) / 2;
            Size size = TextRenderer.MeasureText(textBoxReceiveAddress.Text, textBoxReceiveAddress.Font);
            textBoxReceiveAddress.Width = size.Width + 10;
            textBoxReceiveAddress.Left = (panelRecieve.Width - textBoxReceiveAddress.Width) / 2;
            pictureBoxReceiveQR.Width = panelRecieve.Width / 3;
            pictureBoxReceiveQR.Left = (panelRecieve.Width - pictureBoxReceiveQR.Width) / 2;

        }
        #endregion
        #region Accounts
        private void buttonaccountnew_Click(object sender, EventArgs e)
        {
            NewAccountForm newAccount = new NewAccountForm(this);
            newAccount.ShowDialog();
            newAccount.Dispose();
            GetAccounts();
        }
        private async void GetAccounts()
        {
            try
            {
                string APIresponse = await APIGet("wallet", true);
                comboBoxaccount.Items.Clear();
                if (APIresponse != "Error")
                {
                    comboBoxaccount.Enabled = true;
                    JArray jArray = JArray.Parse(APIresponse);
                    foreach (string account in jArray)
                    {
                        comboBoxaccount.Items.Add(account);
                    }
                    if (comboBoxaccount.Items.Count > 0)
                    {
                        comboBoxaccount.SelectedIndex = 0;
                    }
                    else
                    {
                        comboBoxaccount.Items.Add("No accounts found");
                        comboBoxaccount.Enabled = false;
                    }
                }
                else
                {
                    comboBoxaccount.Items.Add("No accounts found");
                    comboBoxaccount.Enabled = false;
                }
            }
            catch
            {
                AddLog("Error getting accounts");
            }
        }
        private async Task<bool> Login()
        {
            string path = "wallet/" + Account + "/unlock";

            string content = "{\"passphrase\": \"" + Password + "\",\"timeout\": 60}";
            if (Password == "")
            {
                // For some reason, the API doesn't like an empty password, so we'll just use a default one
                content = "{\"passphrase\": \"password\" ,\"timeout\": 60}";
            }

            string APIresponse = await APIPost(path, true, content);

            if (!APIresponse.Contains("true"))
            {
                AddLog("Login failed");
                NotifyForm notifyForm = new NotifyForm("Login Failed\nMake sure your password is correct");
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                return false;
            }

            path = "";
            content = "{\"method\": \"selectwallet\",\"params\":[ \"" + Account + "\"]}";

            APIresponse = await APIPost(path, true, content);
            if (!APIresponse.Contains("\"error\":null"))
            {
                AddLog("Wallet selection failed");
                NotifyForm notifyForm = new NotifyForm("Wallet selection failed\n" + APIresponse);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                return false;
            }
            UpdateBalance();

            path = "wallet/" + Account + "";
            APIresponse = await APIGet(path, true);
            JObject jObject = JObject.Parse(APIresponse);
            if (jObject["watchOnly"].ToString() == "True")
            {
                WatchOnly = true;
                toolStripStatusLabelLedger.Visible = true;
                buttonAddressVerify.Visible = true;
            }
            else
            {
                WatchOnly = false;
                toolStripStatusLabelLedger.Visible = false;
                buttonAddressVerify.Visible = false;
            }

            path = "wallet/" + Account + "/account/default";
            APIresponse = await APIGet(path, true);
            if (APIresponse.Contains("Error"))
            {
                AddLog("Error getting default account");
                multiSig = false;
            }
            else
            {
                jObject = JObject.Parse(APIresponse);
                if (jObject["n"].ToString() == "1")
                {
                    multiSig = false;
                }
                else
                {
                    multiSig = true;
                }
            }

            if (multiSig)
            {
                toolStripStatusLabelMultisig.Visible = true;
                buttonMultiSettings.Visible = true;
            }
            else
            {
                toolStripStatusLabelMultisig.Visible = false;
                buttonMultiSettings.Visible = false;
            }
            if (WatchOnly || multiSig)
            {
                buttonRevealAll.Visible = false;
                buttonRedeemAll.Visible = false;
                buttonSendAll.Visible = false;
            }
            else
            {
                buttonRevealAll.Visible = true;
                buttonRedeemAll.Visible = true;
                buttonSendAll.Visible = true;
            }

            return true;
        }

        private async void LoginClick(object sender, EventArgs e)
        {
            // If the node isn't connected show a message
            try
            {
                if (await APIGet("", false) == "Error")
                {
                    NotifyForm notifyForm = new NotifyForm("Node not connected");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                    return;
                }

                Account = comboBoxaccount.Text;
                Password = textBoxaccountpassword.Text;
                bool loggedin = await Login();
                if (loggedin)
                {
                    toolStripStatusLabelaccount.Text = "Account: " + Account;
                    textBoxaccountpassword.Text = "";
                    panelaccount.Visible = false;
                    toolStripSplitButtonlogout.Visible = true;
                    panelNav.Visible = true;
                    buttonNavPortfolio.PerformClick();
                }
            }
            catch (Exception ex)
            {
                NotifyForm notifyForm = new NotifyForm("Node not connected\n" + ex.Message);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                return;
            }
        }

        private void PasswordEntered(object sender, KeyEventArgs e)
        {
            if (e.KeyValue == 13)
            {
                LoginClick(sender, e);
            }
        }

        private void AccountChoose(object sender, EventArgs e)
        {
            textBoxaccountpassword.Focus();
        }

        private async void Logout(object sender, EventArgs e)
        {
            Password = ""; // Clear password from memory as soon as possible
            toolStripSplitButtonlogout.Visible = false;
            toolStripStatusLabelLedger.Visible = false;
            string path = "wallet/" + Account + "/lock";
            string content = "";
            string APIresponse = await APIPost(path, true, content);
            if (!APIresponse.Contains("true"))
            {
                AddLog("Logout failed");
                NotifyForm notifyForm = new NotifyForm("Logout Failed\n" + APIresponse);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                panelaccount.Visible = true;
                return;
            }
            panelaccount.Visible = true;
            panelNav.Visible = false;
            hidePages();
            toolStripStatusLabelaccount.Text = "Account: Not Logged In";
            textBoxaccountpassword.Focus();
        }
        private async void buttonRevealAll_Click(object sender, EventArgs e)
        {
            buttonRevealAll.Enabled = false;
            string content = "{\"method\": \"sendreveal\"}";
            string response = await APIPost("", true, content);
            AddLog(response);
            if (response == "Error")
            {
                AddLog("Error sending reveal");
                NotifyForm notifyForm = new NotifyForm("Error sending reveal");
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonRevealAll.Enabled = true;
                return;
            }
            JObject resp = JObject.Parse(response);
            if (resp["error"].ToString() != "")
            {
                AddLog("Error sending reveal");
                AddLog(resp["error"].ToString());
                JObject error = JObject.Parse(resp["error"].ToString());
                NotifyForm notifyForm = new NotifyForm("Error sending reveal\n" + error["message"].ToString());
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonRevealAll.Enabled = true;
                return;
            }
            if (resp.ContainsKey("result"))
            {
                JObject result = JObject.Parse(resp["result"].ToString());
                string hash = result["hash"].ToString();
                NotifyForm notifyForm = new NotifyForm("Reveal sent\n" + hash, "Explorer", UserSettings["explorer-tx"] + hash);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
            }
            buttonRevealAll.Enabled = true;
        }
        private async void buttonRedeemAll_Click(object sender, EventArgs e)
        {
            buttonRedeemAll.Enabled = false;
            string content = "{\"method\": \"sendbatch\", \"params\":[[[\"REDEEM\"]]]}";
            AddLog(content);
            string response = await APIPost("", true, content);
            if (response == "Error")
            {
                AddLog("Error sending batch");
                NotifyForm notifyForm = new NotifyForm("Error sending batch");
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonRedeemAll.Enabled = true;
                return;
            }

            JObject resp = JObject.Parse(response);
            if (resp["error"].ToString() != "")
            {
                AddLog("Error sending batch");
                AddLog(resp["error"].ToString());
                JObject error = JObject.Parse(resp["error"].ToString());
                NotifyForm notifyForm = new NotifyForm("Error sending batch\n" + error["message"].ToString());
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonRedeemAll.Enabled = true;
                return;
            }
            if (resp.ContainsKey("result"))
            {
                JObject result = JObject.Parse(resp["result"].ToString());
                string hash = result["hash"].ToString();
                NotifyForm notifyForm = new NotifyForm("Batch sent\n" + hash, "Explorer", UserSettings["explorer-tx"] + hash);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
            }
            buttonRedeemAll.Enabled = true;
        }

        private async void buttonSendAll_Click(object sender, EventArgs e)
        {
            buttonSendAll.Enabled = false;
            string content = "{\"method\": \"sendbatch\", \"params\":[[[\"REVEAL\"],[\"REDEEM\"],[\"RENEW\"]]]}";
            AddLog(content);
            string response = await APIPost("", true, content);
            if (response == "Error")
            {
                AddLog("Error sending batch");
                NotifyForm notifyForm = new NotifyForm("Error sending batch");
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonSendAll.Enabled = true;
                return;
            }

            JObject resp = JObject.Parse(response);
            if (resp["error"].ToString() != "")
            {
                AddLog("Error sending batch");
                AddLog(resp["error"].ToString());
                JObject error = JObject.Parse(resp["error"].ToString());
                NotifyForm notifyForm = new NotifyForm("Error sending batch\n" + error["message"].ToString());
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                buttonSendAll.Enabled = true;
                return;
            }
            if (resp.ContainsKey("result"))
            {
                JObject result = JObject.Parse(resp["result"].ToString());
                string hash = result["hash"].ToString();
                NotifyForm notifyForm = new NotifyForm("Batch sent\n" + hash, "Explorer", UserSettings["explorer-tx"] + hash);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
            }
            buttonSendAll.Enabled = true;
        }
        #endregion
        #region API
        HttpClient httpClient = new HttpClient();
        private async void NodeStatus()
        {

            if (await APIGet("", false) == "Error")
            {
                if (toolStripStatusLabelstatus.Text != "Status: HSD Starting")
                {
                    toolStripStatusLabelstatus.Text = "Status: Node Not Connected";
                }
                return;
            }
            else
            {
                if (toolStripStatusLabelstatus.Text != "Status: Node Connected") GetAccounts(); // Get accounts if node was not connected before
                toolStripStatusLabelstatus.Text = "Status: Node Connected";
            }
            if (Account == "") return; // Don't update balance if not logged in

            // Try to keep wallet unlocked
            string path = "wallet/" + Account + "/unlock";
            string content = "{\"passphrase\": \"" + Password + "\",\"timeout\": 60}";

            await APIPost(path, true, content);

            path = "";
            content = "{\"method\": \"selectwallet\",\"params\":[ \"" + Account + "\"]}";

            await APIPost(path, true, content);

        }
        private async Task UpdateBalance()
        {
            string response = await APIGet("wallet/" + Account + "/balance?account=default", true);
            if (response == "Error") return;

            JObject resp = JObject.Parse(response);

            decimal available = (Convert.ToDecimal(resp["unconfirmed"].ToString()) - Convert.ToDecimal(resp["lockedUnconfirmed"].ToString())) / 1000000;
            decimal locked = Convert.ToDecimal(resp["lockedUnconfirmed"].ToString()) / 1000000;
            available = decimal.Round(available, 2);
            locked = decimal.Round(locked, 2);
            Balance = available;
            Balance_Locked = locked;
        }
        /// <summary>
        /// Post to HSD API
        /// </summary>
        /// <param name="path">Path to post to</param>
        /// <param name="wallet">Whether to use port 12039</param>
        /// <param name="content">Content to post</param>
        /// <returns></returns>
        public async Task<string> APIPost(string path, bool wallet, string content)
        {
            if (content == "{\"passphrase\": \"\",\"timeout\": 60}")
            {
                return "";
            }
            string key = NodeSettings["Key"];
            string ip = NodeSettings["IP"];
            string port = "1203";
            if (HSDNetwork == 1)
            {
                port = "1303";
            }
            if (wallet) port = port + "9";
            else port = port + "7";

            HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "http://" + ip + ":" + port + "/" + path);
            req.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("x:" + key)));
            req.Content = new StringContent(content);
            // Send request


            try
            {
                HttpResponseMessage resp = await httpClient.SendAsync(req);
                if (!resp.IsSuccessStatusCode)
                {
                    AddLog("Post Error: " + resp.StatusCode);
                    AddLog(await resp.Content.ReadAsStringAsync());
                    return "Error";
                }
                return await resp.Content.ReadAsStringAsync();

            }
            catch (Exception ex)
            {
                AddLog("Post Error: " + ex.Message);
                if (ex.Message.Contains("The request was canceled due to the configured HttpClient.Timeout"))
                {
                    await RestartNode();
                }
                return "Error";
            }
        }
        /// <summary>
        /// Get from HSD API
        /// </summary>
        /// <param name="path">Path to get</param>
        /// <param name="wallet">Whether to use port 12039</param>
        /// <returns></returns>
        public async Task<string> APIGet(string path, bool wallet)
        {
            if (NodeSettings == null) return "Error";
            if (!NodeSettings.ContainsKey("Key") || !NodeSettings.ContainsKey("IP")) return "Error";
            string key = NodeSettings["Key"];
            string ip = NodeSettings["IP"];

            string port = "1203";
            if (HSDNetwork == 1)
            {
                port = "1303";
            }
            if (wallet) port = port + "9";
            else port = port + "7";

            try
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "http://" + ip + ":" + port + "/" + path);
                // Add API key to header
                request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes("x:" + key)));
                // Send request and log response
                HttpResponseMessage response = await httpClient.SendAsync(request);
                if (!response.IsSuccessStatusCode)
                {
                    AddLog("Get Error: " + response.StatusCode);
                    AddLog(await response.Content.ReadAsStringAsync());
                    return "Error";
                }
                return await response.Content.ReadAsStringAsync();

            }
            // Log errors to log textbox
            catch (Exception ex)
            {
                AddLog("Get Error: " + ex.Message);
                if (ex.Message.Contains("The request was canceled due to the configured HttpClient.Timeout"))
                {
                    await RestartNode();
                }
                return "Error";
            }
        }
        private async Task<bool> RestartNode()
        {
            if (!HSD)
            {
                NotifyForm nf = new NotifyForm("NODE Not responding");
                nf.ShowDialog();
                nf.Dispose();
                return false;
            }
            this.Enabled = false;
            this.Visible = false;
            // Show splash
            SplashScreen ss = new SplashScreen(false);
            bool splash = false;
            if (UserSettings.ContainsKey("hide-splash"))
            {
                if (UserSettings["hide-splash"] == "false")
                {
                    // Show splash screen
                    ss.Show();
                    splash = true;
                }
            }
            else
            {
                // Show splash screen
                ss.Show();
                splash = true;
            }
            // Kill node
            if (HSDProcess != null)
            {
                HSDProcess.Kill();
                AddLog("Killed HSD");
                Thread.Sleep(1000);
                try
                {
                    HSDProcess.Dispose();
                }
                catch
                {
                    AddLog("Dispose failed");
                }
            }
            else AddLog("HSD was not running");

            HSDProcess = new Process();
            bool hideScreen = true;
            if (NodeSettings.ContainsKey("HideScreen"))
            {
                if (NodeSettings["HideScreen"].ToLower() == "false")
                {
                    hideScreen = false;
                }
            }
            try
            {
                HSDProcess.StartInfo.CreateNoWindow = hideScreen;
                if (hideScreen)
                {
                    HSDProcess.StartInfo.RedirectStandardError = true;
                    HSDProcess.ErrorDataReceived += (sender, e) => AddLog("HSD Error: " + e.Data);
                }
                else HSDProcess.StartInfo.RedirectStandardError = false;

                HSDProcess.StartInfo.RedirectStandardInput = true;
                HSDProcess.StartInfo.RedirectStandardOutput = false;
                HSDProcess.StartInfo.UseShellExecute = false;
                HSDProcess.StartInfo.FileName = "node.exe";

                if (NodeSettings.ContainsKey("HSD-command"))
                {
                    AddLog("Using custom HSD command");
                    string command = NodeSettings["HSD-command"];
                    command = command.Replace("{default-dir}", dir + "hsd\\bin\\hsd");

                    string bobPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Bob\\hsd_data";
                    if (Directory.Exists(bobPath))
                    {
                        command = command.Replace("{Bob}", bobPath);
                    }
                    else if (command.Contains("{Bob}"))
                    {
                        AddLog("Bob not found, using default HSD command");
                        command = dir + "hsd\\bin\\hsd --agent=FireWallet --index-tx --index-address --api-key " + NodeSettings["Key"];
                    }

                    command = command.Replace("{key}", NodeSettings["Key"]);
                    HSDProcess.StartInfo.Arguments = command;
                }
                else
                {
                    AddLog("Using default HSD command");
                    HSDProcess.StartInfo.Arguments = dir + "hsd\\bin\\hsd --agent=FireWallet --index-tx --index-address --api-key " + NodeSettings["Key"];
                    string bobPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Bob\\hsd_data";
                    if (Directory.Exists(bobPath))
                    {
                        HSDProcess.StartInfo.Arguments = HSDProcess.StartInfo.Arguments + " --prefix " + bobPath;
                    }
                }
                HSDProcess.Start();
                // Wait for HSD to start
                await Task.Delay(2000);

                // Check if HSD is running
                if (HSDProcess.HasExited)
                {
                    AddLog("HSD Failed to start");
                    AddLog(HSDProcess.StandardError.ReadToEnd());
                    NotifyForm Notifyinstall = new NotifyForm("HSD Failed to start\nPlease check the logs");
                    Notifyinstall.ShowDialog();
                    Notifyinstall.Dispose();

                    // Wait for the notification to show
                    await Task.Delay(1000);
                    this.Close();

                    await Task.Delay(1000);
                    return false;
                }

            }
            catch (Exception ex)
            {
                AddLog("HSD Failed to start");
                AddLog(ex.Message);
                this.Close();
                await Task.Delay(1000);
            }

            if (splash) ss.CloseSplash();
            this.Enabled = true;
            this.Visible = true;
            return true;
        }
        private async Task<string> GetAddress()
        {
            string content = "{\"account\":\"default\"}";
            string path = "wallet/" + Account + "/address";
            string APIresponse = await APIPost(path, true, content);
            if (APIresponse == "Error")
            {
                AddLog("GetAddress Error");
                return "Error";
            }
            try
            {
                JObject resp = JObject.Parse(APIresponse);
                return resp["address"].ToString();
            }
            catch { return "Error"; }
        }

        private async void GetTXHistory()
        {
            // Get height and progress
            String APIresponse = await APIGet("", false);
            JObject resp = JObject.Parse(APIresponse);
            JObject chain = JObject.Parse(resp["chain"].ToString());
            labelHeight.Text = "Height: " + chain["height"].ToString();
            decimal progress = Convert.ToDecimal(chain["progress"].ToString());
            labelSyncPercent.Text = "Sync: " + decimal.Round(progress * 100, 2) + "%";


            // Exit if set to 0 TXs
            if (UserSettings.ContainsKey("portfolio-tx"))
            {
                if (UserSettings["portfolio-tx"] == "0") return;
            }


            // Get Unconfirmed TX
            string path = "wallet/" + Account + "/tx/unconfirmed";
            APIresponse = await APIGet(path, true);
            if (APIresponse == "Error")
            {
                AddLog("GetInfo Error");
                return;
            }
            JArray pendingTxs = JArray.Parse(APIresponse);
            labelPendingCount.Text = "Unconfirmed TX: " + pendingTxs.Count.ToString();


            // Check how many TX there are
            APIresponse = await APIGet("wallet/" + Account, true);
            JObject wallet = JObject.Parse(APIresponse);
            if (!wallet.ContainsKey("balance"))
            {
                AddLog("GetInfo Error");
                AddLog(APIresponse);
                return;
            }
            JObject balance = JObject.Parse(wallet["balance"].ToString());
            int TotalTX = Convert.ToInt32(balance["tx"].ToString());
            int toGet = 10;
            if (UserSettings.ContainsKey("portfolio-tx")) toGet = Convert.ToInt32(UserSettings["portfolio-tx"]);

            if (toGet > TotalTX) toGet = TotalTX;
            int toSkip = TotalTX - toGet;

            // GET TXs
            if (WatchOnly)
            {
                APIresponse = await APIPost("", true, "{\"method\": \"listtransactions\",\"params\": [\"default\"," + toGet + "," + toSkip + ", true]}");
            }
            else
            {
                APIresponse = await APIPost("", true, "{\"method\": \"listtransactions\",\"params\": [\"default\"," + toGet + "," + toSkip + "]}");
            }

            if (APIresponse == "Error")
            {
                AddLog("GetInfo Error");
                return;
            }
            JObject TXGET = JObject.Parse(APIresponse);

            // Check for error
            if (TXGET["error"].ToString() != "")
            {
                AddLog("GetInfo Error");
                AddLog(APIresponse);
                return;
            }

            JArray txs = JArray.Parse(TXGET["result"].ToString());
            if (toGet > txs.Count) toGet = txs.Count; // In case there are less TXs than expected (usually happens when the get TX's fails)
            Control[] tmpControls = new Control[toGet];
            for (int i = 0; i < toGet; i++)
            {

                // Get last tx
                JObject tx = JObject.Parse(await APIGet("wallet/" + Account + "/tx/" + txs[toGet - i - 1]["txid"].ToString(), true));

                string hash = tx["hash"].ToString();
                string date = tx["mdate"].ToString();

                date = DateTime.Parse(date).ToShortDateString();




                Panel tmpPanel = new Panel();
                tmpPanel.Width = groupBoxTransactions.Width - SystemInformation.VerticalScrollBarWidth - 20;
                tmpPanel.Height = 50;
                tmpPanel.Location = new Point(5, (i * 55));
                tmpPanel.BorderStyle = BorderStyle.FixedSingle;
                tmpPanel.BackColor = ColorTranslator.FromHtml(Theme["background-alt"]);
                tmpPanel.ForeColor = ColorTranslator.FromHtml(Theme["foreground-alt"]);
                tmpPanel.Controls.Add(
                    new Label()
                    {
                        Text = "Date: " + date,
                        Location = new Point(10, 5)
                    }
                    );
                int confirmations = Convert.ToInt32(tx["confirmations"].ToString());
                if (UserSettings.ContainsKey("confirmations"))
                {
                    if (confirmations < Convert.ToInt32(UserSettings["confirmations"]))
                    {
                        Label txPending = new Label()
                        {
                            Text = "Pending",
                            Location = new Point(100, 5)
                        };
                        tmpPanel.Controls.Add(txPending);
                        txPending.BringToFront();
                    }
                }
                Label labelHash = new Label()
                {
                    Text = "Hash: " + hash.Substring(0, 10) + "..." + hash.Substring(hash.Length - 10),
                    AutoSize = true,
                    Location = new Point(10, 25)
                };

                tmpPanel.Controls.Add(labelHash);

                JArray inputs = JArray.Parse(tx["inputs"].ToString());
                JArray outputs = JArray.Parse(tx["outputs"].ToString());
                int inputCount = inputs.Count;
                int outputCount = outputs.Count;

                decimal costHNS = decimal.Parse(txs[toGet - i - 1]["amount"].ToString());
                string cost = "";
                if (costHNS < 0)
                {
                    cost = "Spent: " + (costHNS * -1).ToString() + " HNS";
                }
                else if (costHNS > 0)
                {
                    cost = "Received: " + costHNS.ToString() + " HNS";
                }





                Label labelInputOutput = new Label()
                {
                    Text = "Inputs: " + inputCount + " Outputs: " + outputCount + "\n" + cost,
                    AutoSize = true,
                    Location = new Point(300, 5)
                };
                tmpPanel.Controls.Add(labelInputOutput);


                tmpPanel.Click += (sender, e) =>
                {
                    TXForm txForm = new TXForm(this, hash);
                    txForm.Show();
                };
                foreach (Control c in tmpPanel.Controls)
                {
                    c.Click += (sender, e) =>
                    {
                        TXForm txForm = new TXForm(this, hash);
                        txForm.Show();
                    };
                }

                tmpControls[i] = tmpPanel;


            }
            groupBoxTransactions.Controls.Clear();
            Panel txPanel = new Panel();
            txPanel.Width = groupBoxTransactions.Width - SystemInformation.VerticalScrollBarWidth;
            txPanel.Controls.AddRange(tmpControls);
            txPanel.AutoScroll = true;
            txPanel.Dock = DockStyle.Fill;
            groupBoxTransactions.Controls.Add(txPanel);


        }
        private async Task<string> GetFee()
        {
            // This doesn't work
            try
            {
                string response = await APIPost("", false, "{\"method\": \"estimatefee\",\"params\": [ 3 ]}");
                JObject resp = JObject.Parse(response);
                string result = resp["result"].ToString();
                decimal fee = decimal.Parse(result);
                if (fee < 0.001m) fee = 1;

                return fee.ToString();
            }
            catch
            {
                AddLog("GetFee Error");
                return "1";
            }
        }
        public async Task<bool> ValidAddress(string address)
        {
            string output = await APIPost("", false, "{\"method\": \"validateaddress\",\"params\": [ \"" + address + "\" ]}");
            JObject APIresp = JObject.Parse(output);
            JObject result = JObject.Parse(APIresp["result"].ToString());
            if (result["isvalid"].ToString() == "True") return true;
            else return false;
        }

        #endregion
        #region Timers
        private void timerNodeStatus_Tick(object sender, EventArgs e)
        {
            NodeStatus();
        }
        #endregion
        #region Nav
        private void buttonMultiSign_Click(object sender, EventArgs e)
        {
            ImportTXForm importTXForm = new ImportTXForm(this);
            importTXForm.ShowDialog();
            importTXForm.Dispose();
        }
        private async void PortfolioPanel_Click(object sender, EventArgs e)
        {
            hidePages();
            panelPortfolio.Show();
            await UpdateBalance();
            GetTXHistory();
            labelBalance.Text = "Available: " + Balance.ToString() + " HNS";
            labelLocked.Text = "Locked: " + Balance_Locked.ToString() + " HNS*";
            labelBalanceTotal.Text = "Total: " + (Balance + Balance_Locked).ToString() + " HNS";
            if (Theme.ContainsKey("selected-bg") && Theme.ContainsKey("selected-fg"))
            {
                buttonNavPortfolio.BackColor = ColorTranslator.FromHtml(Theme["selected-bg"]);
                buttonNavPortfolio.ForeColor = ColorTranslator.FromHtml(Theme["selected-fg"]);
            }
            groupBoxTransactions.Height = panelPortfolio.Height - groupBoxbalance.Height - 10;
        }

        private async void SendPanel_Click(object sender, EventArgs e)
        {
            hidePages();
            panelSend.Show();
            if (Theme.ContainsKey("selected-bg") && Theme.ContainsKey("selected-fg"))
            {
                buttonNavSend.BackColor = ColorTranslator.FromHtml(Theme["selected-bg"]);
                buttonNavSend.ForeColor = ColorTranslator.FromHtml(Theme["selected-fg"]);
            }
            if (Theme.ContainsKey("error"))
            {
                labelSendingError.ForeColor = ColorTranslator.FromHtml(Theme["error"]);
            }

            labelSendPrompt.Left = (panelSend.Width - labelSendPrompt.Width) / 2;
            buttonSendHNS.Left = (panelSend.Width - buttonSendHNS.Width) / 2;
            labelSendingTo.Left = (panelSend.Width - labelSendingTo.Width - textBoxSendingTo.Width) / 2;
            labelSendingAmount.Left = labelSendingTo.Left;
            textBoxSendingTo.Left = labelSendingTo.Left + labelSendingTo.Width + 10;
            textBoxSendingAmount.Left = textBoxSendingTo.Left;
            labelSendingMax.Left = labelSendingTo.Left;
            labelSendingError.Left = textBoxSendingTo.Left + textBoxSendingTo.Width + 10;
            labelSendingFee.Left = labelSendingTo.Left;
            buttonSendMax.Left = textBoxSendingAmount.Left + textBoxSendingAmount.Width - buttonSendMax.Width;
            checkBoxSendSubFee.Left = labelSendingTo.Left;

            labelSendingMax.Text = "Max: " + Balance.ToString() + " HNS";
            textBoxSendingTo.Focus();
            string fee = await GetFee();

            labelSendingFee.Text = "Est. Fee: " + fee + " HNS";
            labelSendingError.Hide();

        }
        private async void ReceivePanel_Click(object sender, EventArgs e)
        {
            hidePages();
            panelRecieve.Show();

            if (Theme.ContainsKey("selected-bg") && Theme.ContainsKey("selected-fg"))
            {
                buttonNavReceive.BackColor = ColorTranslator.FromHtml(Theme["selected-bg"]);
                buttonNavReceive.ForeColor = ColorTranslator.FromHtml(Theme["selected-fg"]);
            }
            labelReceive1.Left = (panelRecieve.Width - labelReceive1.Width) / 2;
            labelReceive2.Left = (panelRecieve.Width - labelReceive2.Width) / 2;
            textBoxReceiveAddress.Left = (panelRecieve.Width - textBoxReceiveAddress.Width) / 2;


            string address = await GetAddress();
            textBoxReceiveAddress.Text = address;
            textBoxReceiveAddress.TextAlign = HorizontalAlignment.Center;
            Size size = TextRenderer.MeasureText(textBoxReceiveAddress.Text, textBoxReceiveAddress.Font);
            textBoxReceiveAddress.Width = size.Width + 10;
            textBoxReceiveAddress.Left = (panelRecieve.Width - textBoxReceiveAddress.Width) / 2;

            QRCodeGenerator qrcode = new QRCodeGenerator();
            QRCodeData qrData = qrcode.CreateQrCode(textBoxReceiveAddress.Text, QRCodeGenerator.ECCLevel.Q);
            QRCode qrCode = new QRCode(qrData);
            pictureBoxReceiveQR.Image = qrCode.GetGraphic(20, Theme["foreground"], Theme["background"]);
            pictureBoxReceiveQR.SizeMode = PictureBoxSizeMode.Zoom;
            pictureBoxReceiveQR.Width = panelRecieve.Width / 3;
            pictureBoxReceiveQR.Left = (panelRecieve.Width - pictureBoxReceiveQR.Width) / 2;
        }
        private void buttonNavDomains_Click(object sender, EventArgs e)
        {
            hidePages();
            panelDomains.Show();

            if (Theme.ContainsKey("selected-bg") && Theme.ContainsKey("selected-fg"))
            {
                buttonNavDomains.BackColor = ColorTranslator.FromHtml(Theme["selected-bg"]);
                buttonNavDomains.ForeColor = ColorTranslator.FromHtml(Theme["selected-fg"]);
            }
            textBoxDomainSearch.Focus();
            groupBoxDomains.Width = panelDomains.Width - 20;
            groupBoxDomains.Left = 10;
            groupBoxDomains.Height = panelDomains.Height - groupBoxDomains.Top - 10;
            comboBoxDomainSort.SelectedIndex = 0;

            UpdateDomains();

        }
        private void hidePages()
        {
            panelSend.Hide();
            panelPortfolio.Hide();
            panelRecieve.Hide();
            panelDomains.Hide();
            panelSettings.Hide();
            buttonNavPortfolio.BackColor = ColorTranslator.FromHtml(Theme["background"]);
            buttonNavPortfolio.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
            buttonNavSend.BackColor = ColorTranslator.FromHtml(Theme["background"]);
            buttonNavSend.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
            buttonNavReceive.BackColor = ColorTranslator.FromHtml(Theme["background"]);
            buttonNavReceive.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
            buttonNavDomains.BackColor = ColorTranslator.FromHtml(Theme["background"]);
            buttonNavDomains.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
            buttonNavSettings.BackColor = ColorTranslator.FromHtml(Theme["background"]);
            buttonNavSettings.ForeColor = ColorTranslator.FromHtml(Theme["foreground"]);
        }
        private void buttonNavSettings_Click(object sender, EventArgs e)
        {
            hidePages();
            if (Theme.ContainsKey("selected-bg") && Theme.ContainsKey("selected-fg"))
            {
                buttonNavSettings.BackColor = ColorTranslator.FromHtml(Theme["selected-bg"]);
                buttonNavSettings.ForeColor = ColorTranslator.FromHtml(Theme["selected-fg"]);
            }

            panelSettings.Show();
            panelSettings.Dock = DockStyle.Fill;
            buttonSettingsSave.Top = panelSettings.Height - buttonSettingsSave.Height - 10;
            labelSettingsSaved.Top = buttonSettingsSave.Top + 10;
            textBoxExTX.Text = UserSettings["explorer-tx"];
            textBoxExAddr.Text = UserSettings["explorer-addr"];
            textBoxExBlock.Text = UserSettings["explorer-block"];
            textBoxExName.Text = UserSettings["explorer-domain"];
            numericUpDownConfirmations.Value = int.Parse(UserSettings["confirmations"]);
            numericUpDownTXCount.Value = int.Parse(UserSettings["portfolio-tx"]);
            labelSettingsSaved.Hide();
        }
        #endregion
        #region Send

        // Store TLSA hash
        public string TLSA { get; set; }
        public async Task<string> HIP02Lookup(string domain)
        {
            try
            {
                IPAddress iPAddress = null;
                TLSA = "";


                // Create an instance of LookupClient using the custom options
                string ip = "127.0.0.1";
                int port = 5350;
                if (UserSettings.ContainsKey("hip-02-ip"))
                {
                    ip = UserSettings["hip-02-ip"];
                }
                if (UserSettings.ContainsKey("hip-02-port"))
                {
                    port = int.Parse(UserSettings["hip-02-port"]);
                }
                else if (!HSD)
                {
                    string bobPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\Bob\\hsd_data";
                    if (Directory.Exists(bobPath))
                    {
                        port = 9892;
                    }
                }

                NameServer nameServer = new NameServer(IPAddress.Parse(ip), port);
                var options = new LookupClientOptions(nameServer);
                options.EnableAuditTrail = true;
                options.UseTcpOnly = true;
                options.Recursion = true;
                options.UseCache = false;
                options.RequestDnsSecRecords = true;
                options.Timeout = httpClient.Timeout;
                var client = new LookupClient(options);

                // Perform the DNS lookup for the specified domain using DNSSec
                var result = client.Query(domain, QueryType.A);
                // Display the DNS lookup results
                foreach (var record in result.Answers.OfType<ARecord>())
                {
                    iPAddress = record.Address;
                }

                if (iPAddress == null)
                {
                    labelSendingError.Show();
                    labelSendingError.Text = "HIP-02 lookup failed";
                    AddLog("No IP found");
                    return "ERROR";
                }

                // Get TLSA record
                var resultTLSA = client.Query("_443._tcp." + domain, QueryType.TLSA);
                foreach (var record in resultTLSA.Answers.OfType<TlsaRecord>())
                {
                    TLSA = record.CertificateAssociationDataAsString;
                }



                string url = "https://" + iPAddress.ToString() + "/.well-known/wallets/HNS";
                var handler = new HttpClientHandler();

                handler.ServerCertificateCustomValidationCallback = ValidateServerCertificate;

                // Create an instance of HttpClient with the custom handler
                using (var httpclient = new HttpClient(handler))
                {
                    httpclient.DefaultRequestHeaders.Add("Host", domain);
                    // Send a GET request to the specified URL
                    HttpResponseMessage response = httpclient.GetAsync(url).Result;

                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        labelSendingError.Show();
                        labelSendingError.Text = "HIP-02 lookup failed";
                        AddLog("HTTPS get failed");
                        AddLog(response.Content.ReadAsStringAsync().Result);
                        return "ERROR";
                    }

                    // Response
                    string address = response.Content.ReadAsStringAsync().Result;
                    address = address.Trim();


                    if (await ValidAddress(address))
                    {
                        return address;
                    }
                    else
                    {
                        AddLog("Invalid Address\n" + address);
                        return "ERROR";
                    }


                }
            }
            catch (Exception ex)
            {
                AddLog("HIP-02 lookup error");
                AddLog(ex.Message);
                return "ERROR";
            }
        }
        private async void textBoxSendingTo_Leave(object sender, EventArgs e)
        {

            labelSendingError.Hide();
            labelHIPArrow.Hide();
            labelSendingHIPAddress.Hide();
            if (textBoxSendingTo.Text == "") return;
            if (textBoxSendingTo.Text.Substring(0, 1) == "@")
            {
                string domain = textBoxSendingTo.Text.Substring(1);
                string address = await HIP02Lookup(domain);
                if (address == "ERROR")
                {
                    labelSendingError.Show();
                    labelSendingError.Text = "HIP-02 lookup failed";
                }
                else
                {
                    labelSendingHIPAddress.Text = address;
                    labelSendingHIPAddress.Show();
                    labelHIPArrow.Show();
                }

            }
            else
            {
                try
                {
                    bool valid = await ValidAddress(textBoxSendingTo.Text);
                    if (valid)
                    {
                        labelSendingError.Hide();
                        labelSendingError.Text = "";
                    }
                    else
                    {
                        labelSendingError.Show();
                        labelSendingError.Text = "Invalid Address";
                    }
                }
                catch (Exception ex)
                {
                    labelSendingError.Show();
                    labelSendingError.Text = ex.Message;
                }
            }
        }
        /// <summary>
        /// DANE validation. Used verifying Handshake domains
        /// </summary>
        /// <param name="certificate">certificate to check</param>
        /// <returns>True if DANE passes, Else False</returns>
        public bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            X509Certificate2 cert2 = new X509Certificate2(certificate);
            var rsaPublicKey = (RSA)cert2.PublicKey.Key;
            using (var sha256 = SHA256.Create())
            {
                byte[] publicKeyBytes = rsaPublicKey.ExportSubjectPublicKeyInfo();
                byte[] publicKeyHash = sha256.ComputeHash(publicKeyBytes);
                string hexFingerprint = ByteArrayToHexString(publicKeyHash);
                if (hexFingerprint == TLSA) return true;
                else
                {
                    AddLog("TLSA mismatch");
                    return false;
                }
            }
        }
        /// <summary>
        /// Convert byte array to hex string
        /// </summary>
        /// <param name="bytes">Byte array to convert</param>
        /// <returns>String of converted byte array</returns>
        static string ByteArrayToHexString(byte[] bytes)
        {
            StringBuilder hex = new StringBuilder(bytes.Length * 2);
            foreach (byte b in bytes)
            {
                hex.AppendFormat("{0:X2}", b);
            }
            return hex.ToString();
        }

        private void textBoxSendingAmount_Leave(object sender, EventArgs e)
        {
            decimal amount = 0;
            if (decimal.TryParse(textBoxSendingAmount.Text, out amount))
            {
                labelSendingError.Hide();
                labelSendingError.Text = "";
            }
            else
            {
                labelSendingError.Show();
                labelSendingError.Text = "Invalid Amount";
            }
        }

        private async void buttonSendMax_Click(object sender, EventArgs e)
        {
            string fee = await GetFee();
            decimal feeDecimal = decimal.Parse(fee);
            textBoxSendingAmount.Text = (Balance - feeDecimal).ToString();
        }

        private async void buttonSendHNS_Click(object sender, EventArgs e)
        {
            try
            {
                string address = textBoxSendingTo.Text;
                if (labelHIPArrow.Visible)
                {
                    address = labelSendingHIPAddress.Text;
                }
                bool valid = await ValidAddress(address);
                if (!valid)
                {
                    labelSendingError.Show();
                    labelSendingError.Text = "Invalid Address";
                    return;
                }
                decimal amount = 0;
                if (!decimal.TryParse(textBoxSendingAmount.Text, out amount))
                {
                    labelSendingError.Show();
                    labelSendingError.Text += " Invalid Amount";
                    return;
                }
                string feeString = await GetFee();
                decimal fee = decimal.Parse(feeString);
                string subtractFee = "false";
                if (checkBoxSendSubFee.Checked) subtractFee = "true";
                else if (amount > (Balance - fee))
                {
                    labelSendingError.Show();
                    labelSendingError.Text += " Insufficient Funds";
                    return;
                }

                if (!WatchOnly && !multiSig)
                {
                    string content = "{\"method\": \"sendtoaddress\",\"params\": [ \"" + address + "\", " +
                        amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}";
                    string output = await APIPost("", true, content);
                    JObject APIresp = JObject.Parse(output);
                    if (APIresp["error"].ToString() != "")
                    {
                        AddLog("Failed:");
                        AddLog(APIresp.ToString());
                        JObject error = JObject.Parse(APIresp["error"].ToString());
                        string ErrorMessage = error["message"].ToString();

                        NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage);
                        notify.ShowDialog();
                        return;
                    }
                    string hash = APIresp["result"].ToString();
                    string link = UserSettings["explorer-tx"] + hash;
                    NotifyForm notifySuccess = new NotifyForm("Transaction Sent\nThis transaction could take up to 20 minutes to mine",
                        "Explorer", link);
                    notifySuccess.ShowDialog();
                    textBoxSendingTo.Text = "";
                    textBoxSendingAmount.Text = "";
                    labelSendingError.Hide();
                    labelSendingError.Text = "";
                    buttonNavPortfolio.PerformClick();
                }
                else // Cold or multisig wallet signing
                {
                    if (multiSig)
                    {
                        if (!WatchOnly)
                        {
                            string content = "{\"method\": \"createsendtoaddress\",\"params\": [ \"" + address + "\", " +
                                amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}";
                            string output = await APIPost("", true, content);
                            JObject APIresp = JObject.Parse(output);
                            if (APIresp["error"].ToString() != "")
                            {
                                AddLog("Failed:");
                                AddLog(APIresp.ToString());
                                JObject error = JObject.Parse(APIresp["error"].ToString());
                                string ErrorMessage = error["message"].ToString();

                                NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage);
                                notify.ShowDialog();
                                return;
                            }
                            JObject result = JObject.Parse(APIresp["result"].ToString());
                            string hex = result["hex"].ToString();

                            content = "{\"tx\": \"" + hex + "\",\"passphrase\":\"" + Password + "\"}";
                            output = await APIPost("wallet/" + Account + "/sign", true, content);
                            if (!output.Contains("hex"))
                            {
                                AddLog("Failed:");
                                AddLog(APIresp.ToString());
                                NotifyForm notify = new NotifyForm("Error Transaction Failed\nCheck the logs");
                                notify.ShowDialog();
                                return;
                            }
                            ExportTransaction(output);
                        }
                        else
                        {
                            string content = "{\"method\": \"createsendtoaddress\",\"params\": [ \"" + address + "\", " +
                                    amount.ToString() + ", \"\", \"\", " + subtractFee + " ]}";
                            string output = await APIPost("", true, content);
                            JObject APIresp = JObject.Parse(output);
                            if (APIresp["error"].ToString() != "")
                            {
                                AddLog("Failed:");
                                AddLog(APIresp.ToString());
                                JObject error = JObject.Parse(APIresp["error"].ToString());
                                string ErrorMessage = error["message"].ToString();

                                NotifyForm notify = new NotifyForm("Error Transaction Failed\n" + ErrorMessage);
                                notify.ShowDialog();
                                return;
                            }
                            string signed = signRaw(output, new string[0]);
                            ExportTransaction(signed);
                        }


                    }
                    else // Cold wallet non multisig
                    {
                        AddLog("Sending CW " + amount.ToString() + " HNS to " + address);

                        if (!Directory.Exists(dir + "hsd-ledger"))
                        {
                            if (CheckNodeInstalled() == false)
                            {
                                AddLog("Node not installed");
                                NotifyForm notify1 = new NotifyForm("Node not installed\nPlease install it to use Ledger");
                                notify1.ShowDialog();
                                notify1.Dispose();
                                return;
                            }
                            AddLog("Installing hsd-ledger");

                            // Try to install hsd-ledger
                            try
                            {
                                NotifyForm Notifyinstall = new NotifyForm("Installing hsd-ledger\nThis may take a few minutes\nDo not close FireWallet", false);
                                Notifyinstall.Show();
                                // Wait for the notification to show
                                await Task.Delay(1000);

                                string repositoryUrl = "https://github.com/handshake-org/hsd-ledger.git";
                                string destinationPath = dir + "hsd-ledger";
                                CloneRepository(repositoryUrl, destinationPath);

                                Notifyinstall.CloseNotification();
                                Notifyinstall.Dispose();
                            }
                            catch (Exception ex)
                            {
                                NotifyForm notifyError = new NotifyForm("Error installing hsd-ledger\n" + ex.Message);
                                AddLog(ex.Message);
                                notifyError.ShowDialog();
                                notifyError.Dispose();
                                return;
                            }

                        }

                        NotifyForm notify = new NotifyForm("Please confirm the transaction on your Ledger device", false);
                        notify.Show();

                        var proc = new Process();
                        proc.StartInfo.CreateNoWindow = true;
                        proc.StartInfo.RedirectStandardInput = true;
                        proc.StartInfo.RedirectStandardOutput = true;
                        proc.StartInfo.UseShellExecute = false;
                        proc.StartInfo.RedirectStandardError = true;
                        proc.StartInfo.FileName = "node.exe";
                        proc.StartInfo.Arguments = dir + "hsd-ledger/bin/hsd-ledger sendtoaddress " + textBoxSendingTo.Text
                            + " " + textBoxSendingAmount.Text + " --api-key " + NodeSettings["Key"] + " -w " + Account;
                        var outputBuilder = new StringBuilder();

                        // Event handler for capturing output data
                        proc.OutputDataReceived += (sender, args) =>
                        {
                            if (!string.IsNullOrEmpty(args.Data))
                            {
                                outputBuilder.AppendLine(args.Data);
                            }
                        };

                        proc.Start();
                        proc.BeginOutputReadLine();
                        proc.WaitForExit();

                        notify.CloseNotification();
                        notify.Dispose();

                        string output = outputBuilder.ToString();
                        AddLog(output);
                        if (output.Contains("Submitted TXID"))
                        {
                            string hash = output.Substring(output.IndexOf("Submitted TXID") + 16, 64);
                            string link = UserSettings["explorer-tx"] + hash;
                            NotifyForm notifySuccess = new NotifyForm("Transaction Sent\nThis transaction could take up to 20 minutes to mine",
                                                           "Explorer", link);
                            notifySuccess.ShowDialog();
                            textBoxSendingTo.Text = "";
                            textBoxSendingAmount.Text = "";
                            buttonNavPortfolio.PerformClick();
                        }
                        else
                        {
                            NotifyForm notifyError = new NotifyForm("Error Transaction Failed\nCheck logs for more details");
                            notifyError.ShowDialog();
                            notifyError.Dispose();
                        }
                    }

                }

            }
            catch (Exception ex)
            {
                AddLog(ex.Message);
                labelSendingError.Show();
                labelSendingError.Text = ex.Message;
            }
        }

        public void CloneRepository(string repositoryUrl, string destinationPath)
        {
            try
            {
                bool hideScreen = true;
                if (NodeSettings.ContainsKey("HideScreen"))
                {
                    if (NodeSettings["HideScreen"].ToLower() == "false")
                    {
                        hideScreen = false;
                    }
                }

                // Check if git is installed
                Process testInstalled = new Process();
                testInstalled.StartInfo.FileName = "git";
                testInstalled.StartInfo.Arguments = "-v";
                testInstalled.StartInfo.RedirectStandardOutput = true;
                testInstalled.StartInfo.UseShellExecute = false;
                testInstalled.StartInfo.CreateNoWindow = true;
                testInstalled.Start();
                string outputInstalled = testInstalled.StandardOutput.ReadToEnd();
                testInstalled.WaitForExit();

                if (!outputInstalled.Contains("git version"))
                {
                    AddLog("Git is not installed");
                    NotifyForm notifyForm = new NotifyForm("Git is not installed\nPlease install it to install HSD dependencies", "Install", "https://git-scm.com/download/win");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                    this.Close();
                    Thread.Sleep(1000);
                    return;
                }

                // Check if node installed
                testInstalled = new Process();
                testInstalled.StartInfo.FileName = "node";
                testInstalled.StartInfo.Arguments = "-v";
                testInstalled.StartInfo.RedirectStandardOutput = true;
                testInstalled.StartInfo.UseShellExecute = false;
                testInstalled.StartInfo.CreateNoWindow = true;
                testInstalled.Start();
                outputInstalled = testInstalled.StandardOutput.ReadToEnd();
                testInstalled.WaitForExit();

                if (!outputInstalled.Contains("v"))
                {
                    AddLog("Node is not installed");
                    NotifyForm notifyForm = new NotifyForm("Node is not installed\nPlease install it to install HSD dependencies", "Install", "https://nodejs.org/en/download");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                    this.Close();
                    Thread.Sleep(1000);
                    return;
                }


                // Check if npm installed
                testInstalled = new Process();
                testInstalled.StartInfo.FileName = "cmd.exe";
                testInstalled.StartInfo.Arguments = "npm -v";
                testInstalled.StartInfo.RedirectStandardOutput = true;
                testInstalled.StartInfo.UseShellExecute = false;
                testInstalled.StartInfo.CreateNoWindow = false;
                testInstalled.Start();
                // Wait 3 seconds and then kill
                Thread.Sleep(3000);
                testInstalled.Kill();
                outputInstalled = testInstalled.StandardOutput.ReadToEnd();
                testInstalled.WaitForExit();
                if (Regex.IsMatch(outputInstalled, @"^\d+\.\d+\.\d+$"))
                {
                    AddLog("NPM is not installed");
                    AddLog(outputInstalled);
                    NotifyForm notifyForm = new NotifyForm("NPM is not installed\nPlease install it to install HSD dependencies", "Install", "https://docs.npmjs.com/downloading-and-installing-node-js-and-npm");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                    this.Close();
                    Thread.Sleep(1000);
                    return;
                }

                AddLog("Prerequisites installed");

                ProcessStartInfo startInfo = new ProcessStartInfo();
                startInfo.FileName = "git";
                startInfo.Arguments = $"clone {repositoryUrl} {destinationPath}";

                if (repositoryUrl == "https://github.com/handshake-org/hsd.git")
                {
                    startInfo.Arguments = $"clone --depth 1 --branch latest {repositoryUrl} {destinationPath}";
                }

                startInfo.RedirectStandardOutput = true;
                startInfo.UseShellExecute = false;
                startInfo.CreateNoWindow = hideScreen;

                Process process = new Process();
                process.StartInfo = startInfo;
                process.Start();

                string output = process.StandardOutput.ReadToEnd();
                process.WaitForExit();

                while (!process.HasExited)
                {
                    output += process.StandardOutput.ReadToEnd();
                }
                var psiNpmRunDist = new ProcessStartInfo
                {
                    FileName = "cmd",
                    RedirectStandardInput = true,
                    WorkingDirectory = destinationPath,
                    CreateNoWindow = hideScreen
                };
                var pNpmRunDist = Process.Start(psiNpmRunDist);
                pNpmRunDist.StandardInput.WriteLine("npm install & exit");
                pNpmRunDist.WaitForExit();

                if (repositoryUrl == "https://github.com/handshake-org/hsd-ledger.git")
                {
                    // Replace /bin/hsd-ledger with /bin/hsd-ledger from
                    // https://raw.githubusercontent.com/Nathanwoodburn/FireWallet/master/hsd-ledger
                    // This version of hsd-ledger has the sendraw transaction function added which is needed for batching

                    string sourcePath = destinationPath + "\\bin\\hsd-ledger";
                    File.Delete(sourcePath);

                    // Download the new hsd-ledger
                    WebClient downloader = new WebClient();
                    downloader.DownloadFile("https://raw.githubusercontent.com/Nathanwoodburn/FireWallet/master/hsd-ledger", sourcePath);
                }
            }
            catch (Exception ex)
            {
                AddLog("Git/NPM Install FAILED");
                AddLog(ex.Message);
                if (ex.Message.Contains("to start process 'git'"))
                {
                    NotifyForm notifyForm = new NotifyForm("Git needs to be installed\nCheck logs for more details");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                }
                else if (ex.Message.Contains("to start process 'node'"))
                {
                    NotifyForm notifyForm = new NotifyForm("Node needs to be installed\nCheck logs for more details");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                }
                else if (ex.Message.Contains("to start process 'npm'"))
                {
                    NotifyForm notifyForm = new NotifyForm("NPM needs to be installed\nCheck logs for more details");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                }
                else
                {

                    NotifyForm notifyForm = new NotifyForm("Git/NPM Install FAILED\nCheck logs for more details");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                }
                this.Close();
            }
        }
        public bool CheckNodeInstalled()
        {
            try
            {
                // Create a new process to execute the 'node' command
                Process process = new Process();
                process.StartInfo.FileName = "node";
                process.StartInfo.Arguments = "--version";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;

                // Start the process and read the output
                process.Start();
                string output = process.StandardOutput.ReadToEnd();

                // Wait for the process to exit
                process.WaitForExit();

                // Check if the output contains a version number
                return !string.IsNullOrEmpty(output);
            }
            catch (Exception)
            {
                // An exception occurred, indicating that 'node' is not installed or accessible
                return false;
            }
        }
        #endregion
        #region Receive
        private void textBoxRecieveAddress_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(textBoxReceiveAddress.Text);
            labelReceive2.Text = "Copied to clipboard";
            labelReceive2.Left = (panelRecieve.Width - labelReceive2.Width) / 2;
        }
        private async void buttonAddressVerify_Click(object sender, EventArgs e)
        {

            var proc = new Process();
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.FileName = "node.exe";
            proc.StartInfo.Arguments = dir + "hsd-ledger/bin/hsd-ledger createaddress --api-key " + NodeSettings["Key"] + " -w " + Account;
            proc.Start();

            // Wait 1 sec
            await Task.Delay(1000);

            // Get the output into a string
            string result = proc.StandardOutput.ReadLine();
            try
            {
                result = result.Replace("Verify address on Ledger device: ", "");
            }
            catch { }
            AddLog(result);
            textBoxReceiveAddress.Text = result;
            Size size = TextRenderer.MeasureText(textBoxReceiveAddress.Text, textBoxReceiveAddress.Font);
            textBoxReceiveAddress.Width = size.Width + 10;
            textBoxReceiveAddress.Left = (panelRecieve.Width - textBoxReceiveAddress.Width) / 2;


            NotifyForm notify = new NotifyForm("Please confirm the address on your Ledger device", false);
            notify.Show();

            // Handle events until process exits
            while (!proc.HasExited)
            {
                // Check for cancellation
                if (proc.WaitForExit(100))
                    break;
                Application.DoEvents();
            }

            notify.CloseNotification();
            notify.Dispose();

        }
        #endregion
        #region Domains
        public string[] Domains { get; set; }
        public string[] DomainsRenewable { get; set; }
        private async void UpdateDomains()
        {
            string response = await APIGet("wallet/" + Account + "/name?own=true", true);

            try
            {
                JArray names = JArray.Parse(response);
                Domains = new string[names.Count];
                DomainsRenewable = new string[names.Count];
                int i = 0;
                int renewable = 0;
                panelDomainList.Controls.Clear();

                // Sort the domains
                switch (comboBoxDomainSort.Text)
                {
                    case "Default":
                        break;
                    case "Alphabetical":
                        names = new JArray(names.OrderBy(obj => (string)obj["name"]));
                        break;
                    case "Expiring":
                        names = new JArray(names.OrderBy(obj =>
                        {
                            JToken daysUntilExpireToken = obj["stats"]?["daysUntilExpire"];
                            return (int)(daysUntilExpireToken ?? int.MaxValue);
                        }));
                        break;
                    case "Value":
                        // Sort by most valuable first
                        names = new JArray(names.OrderByDescending(obj =>
                        {
                            JToken valueToken = obj?["value"];
                            return (int)(valueToken ?? 0);
                        }));
                        break;
                }
                foreach (JObject name in names)
                {
                    Domains[i] = name["name"].ToString();
                    Panel domainTMP = new Panel();
                    domainTMP.Width = panelDomainList.Width - 20 - SystemInformation.VerticalScrollBarWidth;
                    domainTMP.Height = 30;
                    domainTMP.Top = 30 * (i);
                    domainTMP.Left = 10;
                    domainTMP.BorderStyle = BorderStyle.FixedSingle;

                    Label domainName = new Label();
                    domainName.Text = Domains[i];
                    domainName.Top = 5;
                    domainName.Left = 5;
                    domainName.AutoSize = true;


                    domainTMP.Controls.Add(domainName);

                    if (!name.ContainsKey("stats"))
                    {
                        AddLog("Domain " + Domains[i] + " does not have stats");
                        continue;
                    }
                    Label expiry = new Label();
                    JObject stats = JObject.Parse(name["stats"].ToString());
                    if (stats.ContainsKey("daysUntilExpire"))
                    {
                        expiry.Text = "Expires: " + stats["daysUntilExpire"].ToString() + " days";
                        expiry.Top = 5;
                        expiry.AutoSize = true;
                        expiry.Left = domainTMP.Width - expiry.Width - 100;
                        domainTMP.Controls.Add(expiry);

                        // Add to domains renewable
                        DomainsRenewable[renewable] = Domains[i];
                        renewable++;
                    }
                    else
                    {
                        expiry.Text = "Expires: Not Registered yet";
                        expiry.Top = 5;
                        expiry.AutoSize = true;
                        expiry.Left = domainTMP.Width - expiry.Width - 100;
                        domainTMP.Controls.Add(expiry);
                    }


                    // On Click open domain
                    domainTMP.Click += new EventHandler((sender, e) =>
                    {
                        DomainForm domainForm = new DomainForm(this, name["name"].ToString(), UserSettings["explorer-tx"], UserSettings["explorer-domain"]);
                        domainForm.Show();
                    });


                    foreach (Control c in domainTMP.Controls)
                    {
                        c.Click += new EventHandler((sender, e) =>
                        {
                            DomainForm domainForm = new DomainForm(this, name["name"].ToString(), UserSettings["explorer-tx"], UserSettings["explorer-domain"]);
                            domainForm.Show();
                        });
                    }

                    panelDomainList.Controls.Add(domainTMP);
                    i++;
                }
            }
            catch (Exception ex)
            {
                AddLog("Error getting domains");
                AddLog(ex.Message);
            }
        }
        private void textBoxDomainSearch_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyValue == 13)
            {
                textBoxDomainSearch.Text = textBoxDomainSearch.Text.Trim().ToLower();
                e.SuppressKeyPress = true;
                DomainForm domainForm = new DomainForm(this, textBoxDomainSearch.Text, UserSettings["explorer-tx"], UserSettings["explorer-domain"]);

                domainForm.Show();

            }
        }
        private void textBoxDomainSearch_TextChanged(object sender, EventArgs e)
        {
            string domainSearch = textBoxDomainSearch.Text;
            domainSearch = Regex.Replace(textBoxDomainSearch.Text, "[^a-zA-Z0-9-_]", "");
            textBoxDomainSearch.Text = domainSearch;
        }
        private void export_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            saveFileDialog.Filter = "CSV file (*.csv)|*.csv";
            saveFileDialog.Title = "Export";
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
                foreach (string domain in DomainsRenewable)
                {
                    if (domain == null) break;
                    sw.WriteLine(domain);
                }
                sw.Dispose();
            }
        }

        private void buttonRenewAll_Click(object sender, EventArgs e)
        {
            if (DomainsRenewable == null)
            {
                NotifyForm notifyForm = new NotifyForm("No renewable domains found");
                notifyForm.ShowDialog();
                notifyForm.Dispose();
                return;
            }
            foreach (string domain in DomainsRenewable)
            {
                if (domain == null) break;
                AddBatch(domain, "RENEW");
            }
        }
        private void comboBoxDomainSort_DropDownClosed(object sender, EventArgs e)
        {
            UpdateDomains();
        }


        #endregion
        #region Batching
        public void AddBatch(string domain, string operation)
        {
            if (operation == "BID") return;
            if (!BatchMode)
            {
                BatchForm = new BatchForm(this);
                BatchForm.Show();
                BatchMode = true;
            }
            BatchForm.AddBatch(domain, operation);
            BatchForm.UpdateTheme();

        }
        public void AddBatch(string domain, string operation, decimal bid, decimal lockup)
        {
            if (operation != "BID") return;
            if (!BatchMode)
            {
                BatchForm = new BatchForm(this);
                BatchForm.Show();
                BatchMode = true;
            }
            BatchForm.AddBatch(domain, operation, bid, lockup);
            BatchForm.UpdateTheme();

        }
        public void AddBatch(string domain, string operation, DNS[] updateRecords)
        {
            if (!BatchMode)
            {
                BatchForm = new BatchForm(this);
                BatchForm.Show();
                BatchMode = true;
            }
            BatchForm.AddBatch(domain, operation, updateRecords);
            BatchForm.UpdateTheme();
        }
        public void AddBatch(string domain, string operation, string address)
        {
            if (!BatchMode)
            {
                BatchForm = new BatchForm(this);
                BatchForm.Show();
                BatchMode = true;
            }
            BatchForm.AddBatch(domain, operation, address);
            BatchForm.UpdateTheme();
        }
        public void FinishBatch()
        {
            BatchMode = false;
            BatchForm.Dispose();
        }
        private void buttonBatch_Click(object sender, EventArgs e)
        {
            if (!BatchMode)
            {
                BatchForm = new BatchForm(this);
                BatchForm.Show();
                BatchMode = true;
            }
            else BatchForm.Focus();
        }
        #endregion
        #region SettingsPage
        private void buttonSettingsSave_Click(object sender, EventArgs e)
        {
            StreamWriter sw = new StreamWriter(dir + "settings.txt");
            sw.WriteLine("explorer-tx: " + textBoxExTX.Text);
            sw.WriteLine("explorer-addr: " + textBoxExAddr.Text);
            sw.WriteLine("explorer-block: " + textBoxExBlock.Text);
            sw.WriteLine("explorer-domain: " + textBoxExName.Text);
            sw.WriteLine("confirmations: " + numericUpDownConfirmations.Value);
            sw.WriteLine("portfolio-tx: " + numericUpDownTXCount.Value);
            sw.WriteLine("hide-splash: " + UserSettings["hide-splash"]);
            sw.Dispose();
            LoadSettings();
            labelSettingsSaved.Show();
        }
        private async void buttonSeed_Click(object sender, EventArgs e)
        {
            string path = "wallet/" + Account + "/master";
            string response = await APIGet(path, true);
            JObject resp = JObject.Parse(response);
            if (resp["encrypted"].ToString() == "False")
            {
                JObject mnemonic = JObject.Parse(resp["mnemonic"].ToString());
                string phrase = mnemonic["phrase"].ToString();
                NotifyForm notifyForm = new NotifyForm("Your seed phrase is:\n" + phrase, "Copy", phrase, true);
                notifyForm.ShowDialog();
                notifyForm.Dispose();
            }
            else
            {
                string algorithm = resp["algorithm"].ToString();
                if (algorithm != "pbkdf2")
                {
                    AddLog("Invalid algorithm");
                    AddLog(response);
                    NotifyForm notifyForm = new NotifyForm("Invalid algorithm");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                    return;
                }

                try
                {
                    string iv = resp["iv"].ToString();
                    string ciphertext = resp["ciphertext"].ToString();
                    string tmpn = resp["n"].ToString();
                    string tmpr = resp["r"].ToString();
                    string tmpp = resp["p"].ToString();

                    int n = int.Parse(tmpn);
                    int p = int.Parse(tmpp);

                    int iterations = n;


                }
                catch (Exception ex)
                {
                    AddLog("Error decrypting seed");
                    AddLog(ex.ToString());
                    NotifyForm notifyForm = new NotifyForm("Error decrypting seed");
                    notifyForm.ShowDialog();
                    notifyForm.Dispose();
                }


            }
        }

        private async void Rescan_Click(object sender, EventArgs e)
        {
            string content = "{\"height\": 0}";
            string response = await APIPost("rescan", true, content);
            if (!response.Contains("true"))
            {
                AddLog("Error starting rescan");
                AddLog(response);
                return;
            }
            AddLog("Starting rescan");

        }
        #endregion
        #region Help Menu
        private void githubToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Open the GitHub page
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "https://github.com/Nathanwoodburn/FireWallet/",
                UseShellExecute = true
            };
            Process.Start(psi);
        }

        private void websiteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "https://firewallet",
                UseShellExecute = true
            };
            Process.Start(psi);
        }

        private void supportDiscordServerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "https://l.woodburn.au/discord",
                UseShellExecute = true
            };
            Process.Start(psi);
        }
        private void otherProjectsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = "https://nathan.woodburn.au/projects",
                UseShellExecute = true
            };
            Process.Start(psi);
        }
        #endregion



        #region Multi
        public async Task<string> ExportTransaction(string rawTX)
        {
            return await ExportTransaction(rawTX, new string[0]);
        }
        public async Task<string> ExportTransaction(string rawTX, string[] domains)
        {
            JObject tx = JObject.Parse(rawTX);
            JObject toExport = new JObject();
            toExport["version"] = 1;
            toExport["tx"] = tx["hex"];
            JArray inputsParsed = new JArray();
            JArray outputsParsed = new JArray();
            JArray inputs = JArray.Parse(tx["inputs"].ToString());
            JArray outputs = JArray.Parse(tx["outputs"].ToString());

            Dictionary<string, string> hashes = new Dictionary<string, string>();
            foreach (string domain in domains)
            {
                // sha3 hash of domain
                hashes.Add(CalculateSHA3Hash(domain), domain);
            }

            foreach (JObject input in inputs)
            {
                JObject coin = JObject.Parse(input["coin"].ToString());
                JObject covenant = JObject.Parse(coin["covenant"].ToString());
                string type = covenant["type"].ToString();
                JObject data = new JObject();
                if (type == "0")
                {
                    inputsParsed.Add(data);
                }
                else
                {
                    AddLog("Not supported yet");
                    return "Error";
                }
            }
            foreach (JObject output in outputs)
            {
                JObject covenant = JObject.Parse(output["covenant"].ToString());
                string type = covenant["type"].ToString();
                JObject data = new JObject();
                if (type == "0")
                {
                    outputsParsed.Add(data);
                }
                else
                {
                    string hash = covenant["items"][0].ToString();
                    if (hashes.ContainsKey(hash))
                    {
                        data["name"] = hashes[hash];
                    }
                    else
                    {
                        AddLog("Cannot find name for hash " + hash);
                        return "Error";
                    }

                    //data["name"];
                    outputsParsed.Add(data);
                }
            }
            JObject metadata = new JObject();
            metadata["inputs"] = inputsParsed;
            metadata["outputs"] = outputsParsed;
            toExport["metadata"] = metadata;

            SaveFileDialog saveFileDialog = new SaveFileDialog
            {
                Filter = "JSON file (*.json)|*.json",
                Title = "Save transaction as JSON"
            };
            if (saveFileDialog.ShowDialog() == DialogResult.OK)
            {
                StreamWriter sw = new StreamWriter(saveFileDialog.FileName);
                sw.Write(toExport.ToString());
                sw.Dispose();
                return toExport.ToString();
            }
            else
            {
                return "Error";
            }
        }
        public string signRaw(string tx, string[] domains)
        {
            string domainslist = string.Join(",", domains.Select(domain => "\\\"" + domain + "\\\""));
            NotifyForm notify = new NotifyForm("Please confirm the transaction on your Ledger device", false);
            notify.Show();

            var proc = new Process();
            proc.StartInfo.CreateNoWindow = true;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.FileName = "node.exe";
            proc.StartInfo.WorkingDirectory = dir + "hsd-ledger/bin/";
            string args = "hsd-ledger/bin/hsd-ledger signraw \"\"" + tx.Replace("\"", "\\\"").Trim() + "\"\" [" + domainslist + "] --api-key " + NodeSettings["Key"] + " -w " + Account;
            AddLog(args);

            proc.StartInfo.Arguments = dir + args;
            var outputBuilder = new StringBuilder();

            // Event handler for capturing output data
            proc.OutputDataReceived += (sender, args) =>
            {
                if (!string.IsNullOrEmpty(args.Data))
                {
                    outputBuilder.AppendLine(args.Data);
                }
            };

            proc.Start();
            proc.BeginOutputReadLine();
            proc.WaitForExit();

            notify.CloseNotification();
            notify.Dispose();

            string output = outputBuilder.ToString();
            AddLog(output);
            if (output.Length > 2)
            {
                // Signed
                return output;
            }
            else
            {
                AddLog(args);
                AddLog(proc.StandardError.ReadToEnd());
                NotifyForm notifyError = new NotifyForm("Error sign Failed\nCheck logs for more details");
                notifyError.ShowDialog();
                notifyError.Dispose();
                return "Error";
            }
        }
        static string CalculateSHA3Hash(string input)
        {
            var hashAlgorithm = new Org.BouncyCastle.Crypto.Digests.Sha3Digest(256);

            // Choose correct encoding based on your usecase
            byte[] inputBytes = Encoding.UTF8.GetBytes(input);

            hashAlgorithm.BlockUpdate(inputBytes, 0, inputBytes.Length);

            byte[] result = new byte[256 / 8];
            hashAlgorithm.DoFinal(result, 0);

            string hashString = BitConverter.ToString(result);
            hashString = hashString.Replace("-", "").ToLowerInvariant();
            return hashString;
        }
        private void buttonMultiSettings_Click(object sender, EventArgs e)
        {
            MultisigSettingsForm multisigSettingsForm = new MultisigSettingsForm(this);
            multisigSettingsForm.Show();
        }
        #endregion


    }
}