//  
//   Rebex Sample Code License
// 
//   Copyright 2025, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.Drawing;
using System.IO;
using System.Net;
using System.Windows.Forms;
using Rebex.TerminalEmulation;
using Rebex.Net;

namespace Rebex.Samples
{
    /// <summary>
    /// The main form of the application. It contains the terminal control.
    /// </summary>
    public class MainForm : Form
    {
        /// <summary>
        /// The Ssh object. Used to communicate with SSH servers.
        /// </summary>
        private Ssh _ssh;

        /// <summary>
        /// The Telnet object. Used to communicate with telnet servers.
        /// </summary>
        private Telnet _telnet;

        /// <summary>
        /// Contains either _virtualTerminal or terminalControl (depending on the current terminal mode)
        /// </summary>
        private ITerminal _terminal;

        /// <summary>
        /// Virtual terminal object (a terminal with invisible console)
        /// </summary>
        private VirtualTerminal _virtualTerminal;

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        private Button btnConnect;
        private Button btnOptions;
        private Button btnDisconnect;
        private Button btnExit;
        private Button btnTerminal;
        private Button btnExecute;
        private RichTextBox rtbCode;
        private ListBox lbScripts;
        private Panel panel1;
        private Rebex.TerminalEmulation.TerminalControl terminalControl;
        
        /// <summary>
        /// Initialize this class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = true;
        }

        /// <summary>
        /// Initialize this class after form load.
        /// </summary>
        private void MainForm_Load(object sender, EventArgs e)
        {
            ShowWelcomeMessages();

            // create an instance of VirtualTerminal object
            _virtualTerminal = new VirtualTerminal(80, 50);

            // set options
            TerminalOptions options = Settings.Default.TerminalOptions;
            terminalControl.Options = options;
            _virtualTerminal.Options = options;

            // register the Disconnected event
            terminalControl.Disconnected += new EventHandler(terminal_Disconnected);
            _virtualTerminal.Disconnected += new EventHandler(terminal_Disconnected);

            // use TerminalControl by default
            _terminal = terminalControl;

            // get and display the list of sample scripts
            LoadScripts();
        }

        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Windows Form Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.terminalControl = new Rebex.TerminalEmulation.TerminalControl();
            this.btnConnect = new System.Windows.Forms.Button();
            this.btnOptions = new System.Windows.Forms.Button();
            this.btnDisconnect = new System.Windows.Forms.Button();
            this.btnExit = new System.Windows.Forms.Button();
            this.btnTerminal = new System.Windows.Forms.Button();
            this.btnExecute = new System.Windows.Forms.Button();
            this.rtbCode = new System.Windows.Forms.RichTextBox();
            this.lbScripts = new System.Windows.Forms.ListBox();
            this.panel1 = new System.Windows.Forms.Panel();
            this.SuspendLayout();
            // 
            // terminalControl
            // 
            this.terminalControl.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.terminalControl.BackColor = System.Drawing.Color.Magenta;
            this.terminalControl.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.terminalControl.CursorMouse = System.Windows.Forms.Cursors.Arrow;
            this.terminalControl.CursorText = System.Windows.Forms.Cursors.IBeam;
            this.terminalControl.HistoryMaxLength = 1000;
            this.terminalControl.Location = new System.Drawing.Point(1, 253);
            this.terminalControl.Name = "terminalControl";
            this.terminalControl.Recorder = null;
            this.terminalControl.Size = new System.Drawing.Size(740, 300);
            this.terminalControl.TabIndex = 4;
            // 
            // btnConnect
            // 
            this.btnConnect.Location = new System.Drawing.Point(7, 7);
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.Size = new System.Drawing.Size(75, 23);
            this.btnConnect.TabIndex = 5;
            this.btnConnect.Text = "Connect...";
            this.btnConnect.UseVisualStyleBackColor = true;
            this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
            // 
            // btnOptions
            // 
            this.btnOptions.Location = new System.Drawing.Point(7, 65);
            this.btnOptions.Name = "btnOptions";
            this.btnOptions.Size = new System.Drawing.Size(75, 23);
            this.btnOptions.TabIndex = 6;
            this.btnOptions.Text = "Options...";
            this.btnOptions.UseVisualStyleBackColor = true;
            this.btnOptions.Click += new System.EventHandler(this.btnOptions_Click);
            // 
            // btnDisconnect
            // 
            this.btnDisconnect.Enabled = false;
            this.btnDisconnect.Location = new System.Drawing.Point(7, 36);
            this.btnDisconnect.Name = "btnDisconnect";
            this.btnDisconnect.Size = new System.Drawing.Size(75, 23);
            this.btnDisconnect.TabIndex = 7;
            this.btnDisconnect.Text = "Disconnect";
            this.btnDisconnect.UseVisualStyleBackColor = true;
            this.btnDisconnect.Click += new System.EventHandler(this.btnDisconnect_Click);
            // 
            // btnExit
            // 
            this.btnExit.Location = new System.Drawing.Point(7, 94);
            this.btnExit.Name = "btnExit";
            this.btnExit.Size = new System.Drawing.Size(75, 23);
            this.btnExit.TabIndex = 8;
            this.btnExit.Text = "Exit";
            this.btnExit.UseVisualStyleBackColor = true;
            this.btnExit.Click += new System.EventHandler(this.btnExit_Click);
            // 
            // btnTerminal
            // 
            this.btnTerminal.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.btnTerminal.Location = new System.Drawing.Point(0, 230);
            this.btnTerminal.Name = "btnTerminal";
            this.btnTerminal.Size = new System.Drawing.Size(741, 23);
            this.btnTerminal.TabIndex = 9;
            this.btnTerminal.Text = "Use VirtualTerminal class";
            this.btnTerminal.UseVisualStyleBackColor = true;
            this.btnTerminal.Click += new System.EventHandler(this.btnTerminal_Click);
            // 
            // btnExecute
            // 
            this.btnExecute.Enabled = false;
            this.btnExecute.Location = new System.Drawing.Point(88, 201);
            this.btnExecute.Name = "btnExecute";
            this.btnExecute.Size = new System.Drawing.Size(196, 23);
            this.btnExecute.TabIndex = 10;
            this.btnExecute.Text = "Run script";
            this.btnExecute.UseVisualStyleBackColor = true;
            this.btnExecute.Click += new System.EventHandler(this.btnExecute_Click);
            // 
            // rtbCode
            // 
            this.rtbCode.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.rtbCode.BackColor = System.Drawing.SystemColors.ControlLightLight;
            this.rtbCode.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.rtbCode.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.rtbCode.Location = new System.Drawing.Point(302, 0);
            this.rtbCode.Name = "rtbCode";
            this.rtbCode.ReadOnly = true;
            this.rtbCode.Size = new System.Drawing.Size(439, 230);
            this.rtbCode.TabIndex = 11;
            this.rtbCode.Text = "Code goes here...";
            // 
            // lbScripts
            // 
            this.lbScripts.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.lbScripts.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lbScripts.FormattingEnabled = true;
            this.lbScripts.ItemHeight = 16;
            this.lbScripts.Location = new System.Drawing.Point(89, 0);
            this.lbScripts.Name = "lbScripts";
            this.lbScripts.ScrollAlwaysVisible = true;
            this.lbScripts.Size = new System.Drawing.Size(195, 192);
            this.lbScripts.Sorted = true;
            this.lbScripts.TabIndex = 12;
            this.lbScripts.SelectedValueChanged += new System.EventHandler(this.lbScripts_SelectedValueChanged);
            // 
            // panel1
            // 
            this.panel1.BackColor = System.Drawing.SystemColors.ControlLightLight;
            this.panel1.Location = new System.Drawing.Point(290, 0);
            this.panel1.Name = "panel1";
            this.panel1.Size = new System.Drawing.Size(12, 230);
            this.panel1.TabIndex = 14;
            // 
            // MainForm
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(741, 554);
            this.Controls.Add(this.panel1);
            this.Controls.Add(this.lbScripts);
            this.Controls.Add(this.rtbCode);
            this.Controls.Add(this.btnExecute);
            this.Controls.Add(this.btnTerminal);
            this.Controls.Add(this.btnExit);
            this.Controls.Add(this.btnDisconnect);
            this.Controls.Add(this.btnOptions);
            this.Controls.Add(this.btnConnect);
            this.Controls.Add(this.terminalControl);
            this.MinimumSize = new System.Drawing.Size(640, 480);
            this.Name = "MainForm";
            this.Text = "Rebex Terminal Scripting Sample";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_Closing);
            this.Load += new System.EventHandler(this.MainForm_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private void ShowWelcomeMessages()
        {
            // show the welcome text
            rtbCode.Text = Resources.Welcome;

            // show some text in the TerminalControl
            terminalControl.Screen.WriteLine(Resources.TerminalWelcome);
        }

        /// <summary>
        /// Get the list of sample scripts using reflection and fill the listbox
        /// </summary>
        private void LoadScripts()
        {
            lbScripts.Items.Clear();
            foreach (Type type in GetType().Assembly.GetTypes())
            {
                if (type.IsClass && type.FullName.StartsWith("Rebex.Samples.Scripts."))
                {
                    lbScripts.Items.Add(new ListBoxItem(type));
                }
            }
        }

        /// <summary>
        /// Unbinds the client object from the terminal control and disconnects.
        /// </summary>
        private void Disconnect()
        {
            btnDisconnect.Enabled = false;
            btnConnect.Enabled = true;

            if (_ssh != null || _telnet != null)
            {
                // unbind the client object from the terminal console
                _terminal.Unbind();

                if (_ssh != null)
                {
                    // disconnect & dispose the client object
                    _ssh.Disconnect();
                    _ssh.Dispose();
                    _ssh = null;
                }

                _telnet = null;
            }

            // close recorder
            if (_terminal.Recorder != null)
            {
                _terminal.Recorder.Flush();
                _terminal.Recorder.Close();
                _terminal.Recorder = null;
            }
        }

        /// <summary>
        /// Connects to a server and binds the connect to the current terminal object.
        /// </summary>
        /// <param name="showDialog">True to show connection dialog, false to connect immediately.</param>
        private void Connect(bool showDialog)
        {
            try
            {
                // connection dialog
                Connection con = new Connection();
                con.EnableTelnetLoginInfo = true;

                // show connection dialog
                if (showDialog)
                {
                    con.ShowDialog();

                    // if connection dialog was not confirmed, return
                    if (!con.OK)
                        return;
                }

                // unbind the client object from the terminal console
                Disconnect();

                // clear the screen
                _terminal.Screen.Clear();

                // set up the recorder
                var recorderFile = Settings.Default.GetString("RecorderFile");
                if (string.IsNullOrEmpty(recorderFile))
                {
                    _terminal.Recorder = null;
                }
                else
                {
                    // FileMode.Create -> replace old file (keep recording of the last session only)
                    var file = File.Open(recorderFile, FileMode.Create, FileAccess.Write, FileShare.Read);
                    _terminal.Recorder = new StreamWriter(file);
                }

                // if proxy was enabled, set up the proxy property
                Proxy proxy = null;
                if (con.ProxyEnabled)
                {
                    NetworkCredential proxyNetworkCredential = null;

                    if (con.ProxyLoginName.Length > 0)
                    {
                        if (con.ProxyPassword.Length == 0)
                            con.ProxyPassword = null;

                        proxyNetworkCredential = new System.Net.NetworkCredential(con.ProxyLoginName, con.ProxyPassword);
                    }

                    proxy = new Proxy(con.ProxyType, con.ProxyHostName, con.ProxyPort);
                    proxy.AuthenticationMethod = ProxyAuthentication.Basic;
                    proxy.Credentials = proxyNetworkCredential;
                }

                if (con.Protocol == Connection.ProtocolType.Ssh)
                {
                    // create a new Ssh client object
                    _ssh = new Ssh();

                    // assign proxy if enabled
                    if (proxy != null)
                        _ssh.Proxy = proxy;

                    // connect to the SSH server
                    _ssh.Connect(con.HostName, con.Port);

                    // if private key was specified, show the passphrase dialog and set up the SshPrivateKey object
                    SshPrivateKey privateKey = null;
                    if (con.PrivateKey.Length > 0)
                    {
                        PassphraseDialog pp = new PassphraseDialog();
                        if (pp.ShowDialog() == DialogResult.OK)
                        {
                            privateKey = new SshPrivateKey(con.PrivateKey, pp.Passphrase);
                        }
                        else
                        {
                            throw new Exception("Password not entered.");
                        }
                    }

                    if (con.Password.Length == 0)
                        con.Password = null;

                    // login to the SSH server
                    _ssh.Login(con.LoginName, con.Password, privateKey);

                    // bind the connected and authenticated Ssh instance to the terminal console
                    _terminal.Bind(_ssh);
                }
                else
                {
                    // create a new Telnet client object
                    _telnet = new Telnet(con.HostName, con.Port);

                    // assign proxy if enabled
                    if (proxy != null)
                        _telnet.Proxy = proxy;

                    if (con.LoginName != null && con.LoginName.Length > 0 &&
                        con.Password != null && con.Password.Length > 0)
                    {
                        // when using TerminalControl, automated data processing
                        // has to be disabled before running scripts
                        if (_terminal == terminalControl)
                        {
                            terminalControl.SetDataProcessingMode(DataProcessingMode.None);
                            terminalControl.UserInputEnabled = false;
                        }

                        // bind the Telnet instance to the terminal console
                        // (connects to the server automatically)
                        _terminal.Bind(_telnet);

                        LoginToTelnet(_terminal.Scripting, con.LoginName, con.Password);

                        // when using TerminalControl, enable automated data processing when done
                        if (_terminal == terminalControl)
                        {
                            terminalControl.SetDataProcessingMode(DataProcessingMode.Automatic);
                            terminalControl.UserInputEnabled = true;
                        }
                    }
                    else
                    {
                        // bind the Telnet instance to the terminal console
                        // (connects to the server automatically)
                        _terminal.Bind(_telnet);
                    }
                }

                btnDisconnect.Enabled = true;
                btnConnect.Enabled = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Error occurred: {0}", ex));
            }
        }

        /// <summary>
        /// Authenticates to common telnet server.
        /// </summary>
        /// <param name="scripting">An instance of Scripting object.</param>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        private void LoginToTelnet(Scripting scripting, string userName, string password)
        {
            // wait for "login" prompt
            scripting.WaitFor(ScriptEvent.FromString("ogin:"));

            // send user name
            scripting.SendCommand(userName);

            // wait for "password" prompt
            scripting.WaitFor(ScriptEvent.FromString("assword:"));

            // send password
            scripting.SendCommand(password);

            // read response and detect prompt
            scripting.DetectPrompt();
        }

        /// <summary>
        /// Handles ITerminal.Disconnected event.
        /// </summary>
        private void terminal_Disconnected(object sender, EventArgs e)
        {
            // display a message on the terminal screen
            _terminal.Screen.WriteLine();
            _terminal.Screen.WriteLine("Session closed.");

            // unbind the client object and disconnect
            Disconnect();
        }

        /// <summary>
        /// Handles 'Exit' button click.
        /// </summary>
        private void btnExit_Click(object sender, EventArgs e)
        {
            // close the form
            Close();
        }

        /// <summary>
        /// Handles application form closing event.
        /// </summary>
        private void MainForm_Closing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            // unbind the client object and disconnect
            Disconnect();
        }

        /// <summary>
        /// Handles 'Options' button click.
        /// </summary>
        private void btnOptions_Click(object sender, EventArgs e)
        {
            // show options dialog
            Options options = new Options();
            if (options.ShowDialog() == DialogResult.OK)
            {
                terminalControl.Options = options.TerminalOptions;
                _virtualTerminal.Options = options.TerminalOptions;
            }
        }

        /// <summary>
        /// Handles 'Connect' button click.
        /// </summary>
        private void btnConnect_Click(object sender, EventArgs e)
        {
            // show the connection dialog and connect
            Connect(true);
        }

        /// <summary>
        /// Handles 'Disconnect' button click.
        /// </summary>
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            // unbind the client object and disconnect
            Disconnect();
        }

        /// <summary>
        /// Handles terminal object change button.
        /// </summary>
        private void btnTerminal_Click(object sender, EventArgs e)
        {
            // unbind the client object and disconnect
            Disconnect();

            if (_terminal == terminalControl)
            {
                // switch from using TerminalControl to VirtualTerminal

                Controls.Remove(terminalControl);
                MinimumSize = new Size(MinimumSize.Width, btnTerminal.Bottom + Height - ClientSize.Height);
                MaximumSize = new Size(int.MaxValue, MinimumSize.Height);
                ClientSize = new Size(btnTerminal.Right, btnTerminal.Bottom);

                _terminal = _virtualTerminal;
                btnTerminal.Text = "Switch to TerminalControl class";
            }
            else
            {
                // switch from using VirtualTerminal to TerminalControl

                terminalControl.Location = new Point(1, btnTerminal.Bottom);
                terminalControl.Size = new Size(ClientSize.Width - 1, terminalControl.Height);
                terminalControl.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right;
                MinimumSize = new Size(MinimumSize.Width, 420);
                MaximumSize = new Size(int.MaxValue, int.MaxValue);
                ClientSize = new Size(btnTerminal.Right, terminalControl.Bottom + 1);
                Controls.Add(terminalControl);

                _terminal = terminalControl;
                btnTerminal.Text = "Switch to VirtualTerminal class";
            }

        }

        /// <summary>
        /// Handles 'Execute' button click.
        /// </summary>
        private void btnExecute_Click(object sender, EventArgs e)
        {
            // get the selected sample script object
            ListBoxItem sampleScript = lbScripts.SelectedItem as ListBoxItem;
            if (sampleScript == null)
                return;

            if (_ssh == null && _telnet == null)
            {
                DialogResult result = MessageBox.Show(this, "Not connected to the server. Connect now?", "Not Connected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
                if (result != System.Windows.Forms.DialogResult.Yes)
                    return;

                // connect without showing a dialog
                Connect(false);

                // return if not connected,
                if (_ssh == null && _telnet == null)
                    return;
            }

            // when using TerminalControl, automated data processing
            // has to be disabled before running scripts
            if (_terminal == terminalControl)
            {
                terminalControl.SetDataProcessingMode(DataProcessingMode.None);
                terminalControl.UserInputEnabled = false;
            }

            // execute the script
            sampleScript.Execute(_terminal.Scripting);

            // when using TerminalControl, enable automated data processing when done
            if (_terminal == terminalControl)
            {
                terminalControl.SetDataProcessingMode(DataProcessingMode.Automatic);
                terminalControl.UserInputEnabled = true;
            }
        }

        /// <summary>
        /// Handles selected script change event.
        /// </summary>
        private void lbScripts_SelectedValueChanged(object sender, EventArgs e)
        {
            ListBoxItem item = lbScripts.SelectedItem as ListBoxItem;
            if (item != null)
            {
                string code = Resources.ResourceManager.GetString(item.TypeName);
                Colorize.SetCode(rtbCode, code);
                btnExecute.Enabled = true;
            }
            else
            {
                rtbCode.Text = "No script selected.";
                btnExecute.Enabled = false;
            }
        }
    }
}