//  
//   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>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;
        private System.Windows.Forms.FontDialog terminalFontDialog;
        private System.Windows.Forms.SaveFileDialog saveDialog;
        private System.Windows.Forms.ToolStripMenuItem menuItemFile;
        private System.Windows.Forms.ToolStripMenuItem menuItemConnect;
        private System.Windows.Forms.ToolStripMenuItem menuItemSave;
        private System.Windows.Forms.ToolStripMenuItem menuItemTools;
        private System.Windows.Forms.ToolStripMenuItem menuItemFont;
        private System.Windows.Forms.ToolStripMenuItem menuItemOptions;
        private System.Windows.Forms.ToolStripMenuItem menuItemStartRecording;
        private System.Windows.Forms.ToolStripMenuItem menuItemStopRecording;
        private System.Windows.Forms.MenuStrip mmenu;
        private Rebex.TerminalEmulation.TerminalControl tcConsole;
        private System.Windows.Forms.ToolStripMenuItem menuItemExit;
        private ToolStripMenuItem menuItemDisconnect;
        private System.Windows.Forms.SaveFileDialog recorderSaveDialog;

        /// <summary>
        /// Initialize this class.
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            CheckForIllegalCrossThreadCalls = true;

            // set the size of the form to the size equal to 80 columns, 25 rows
            ClientSize = tcConsole.GetControlSize(80, 25);

            // register the RemoteResize event 
            tcConsole.RemoteResize += Console_RemoteResize;

            // register the Disconnected event 
            tcConsole.Disconnected += new EventHandler(Console_Disconnected);

            // set the terminal options from the config file
            tcConsole.Options = Settings.Default.TerminalOptions;
        }

        /// <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.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
            this.saveDialog = new System.Windows.Forms.SaveFileDialog();
            this.terminalFontDialog = new System.Windows.Forms.FontDialog();
            this.mmenu = new System.Windows.Forms.MenuStrip();
            this.menuItemFile = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemConnect = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemSave = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemExit = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemTools = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemFont = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemOptions = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemStartRecording = new System.Windows.Forms.ToolStripMenuItem();
            this.menuItemStopRecording = new System.Windows.Forms.ToolStripMenuItem();
            this.recorderSaveDialog = new System.Windows.Forms.SaveFileDialog();
            this.tcConsole = new Rebex.TerminalEmulation.TerminalControl();
            this.menuItemDisconnect = new System.Windows.Forms.ToolStripMenuItem();
            this.mmenu.SuspendLayout();
            this.SuspendLayout();
            // 
            // saveDialog
            // 
            this.saveDialog.Filter = resources.GetString("saveDialog.Filter");
            this.saveDialog.RestoreDirectory = true;
            // 
            // mmenu
            // 
            this.mmenu.Items.AddRange(new System.Windows.Forms.ToolStripMenuItem[] {
            this.menuItemFile,
            this.menuItemTools});
            // 
            // menuItemFile
            // 
            this.menuItemFile.MergeIndex = 0;
            this.menuItemFile.DropDownItems.AddRange(new System.Windows.Forms.ToolStripMenuItem[] {
            this.menuItemConnect,
            this.menuItemDisconnect,
            this.menuItemSave,
            this.menuItemExit});
            this.menuItemFile.Text = "&File";
            // 
            // menuItemConnect
            // 
            this.menuItemConnect.MergeIndex = 0;
            this.menuItemConnect.Text = "&Connect...";
            this.menuItemConnect.Click += new System.EventHandler(this.menuItemConnect_Click);
            // 
            // menuItemSave
            // 
            this.menuItemSave.MergeIndex = 2;
            this.menuItemSave.Text = "&Save...";
            this.menuItemSave.Click += new System.EventHandler(this.menuItemSave_Click);
            // 
            // menuItemExit
            // 
            this.menuItemExit.MergeIndex = 3;
            this.menuItemExit.Text = "E&xit";
            this.menuItemExit.Click += new System.EventHandler(this.menuItemExit_Click);
            // 
            // menuItemTools
            // 
            this.menuItemTools.MergeIndex = 1;
            this.menuItemTools.DropDownItems.AddRange(new System.Windows.Forms.ToolStripMenuItem[] {
            this.menuItemFont,
            this.menuItemOptions,
            this.menuItemStartRecording,
            this.menuItemStopRecording});
            this.menuItemTools.Text = "&Tools";
            // 
            // menuItemFont
            // 
            this.menuItemFont.MergeIndex = 0;
            this.menuItemFont.Text = "&Font...";
            this.menuItemFont.Click += new System.EventHandler(this.menuItemFont_Click);
            // 
            // menuItemOptions
            // 
            this.menuItemOptions.MergeIndex = 1;
            this.menuItemOptions.Text = "&Options...";
            this.menuItemOptions.Click += new System.EventHandler(this.menuItemOptions_Click);
            // 
            // menuItemStartRecording
            // 
            this.menuItemStartRecording.MergeIndex = 2;
            this.menuItemStartRecording.Text = "&Start recording...";
            this.menuItemStartRecording.Click += new System.EventHandler(this.menuItemStartRecording_Click);
            // 
            // menuItemStopRecording
            // 
            this.menuItemStopRecording.Enabled = false;
            this.menuItemStopRecording.MergeIndex = 3;
            this.menuItemStopRecording.Text = "S&top recording";
            this.menuItemStopRecording.Click += new System.EventHandler(this.menuItemStopRecording_Click);
            // 
            // recorderSaveDialog
            // 
            this.recorderSaveDialog.DefaultExt = "ans";
            this.recorderSaveDialog.Filter = "Ansi file (*.ans;*.ansi)|*.ans;*.ansi|All files (*.*)|*.*";
            this.recorderSaveDialog.OverwritePrompt = false;
            // 
            // tcConsole
            // 
            this.tcConsole.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.tcConsole.BackColor = System.Drawing.Color.Magenta;
            this.tcConsole.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.tcConsole.CursorMouse = System.Windows.Forms.Cursors.Arrow;
            this.tcConsole.CursorText = System.Windows.Forms.Cursors.IBeam;
            this.tcConsole.HistoryMaxLength = 256;
            this.tcConsole.Location = new System.Drawing.Point(0, 27);
            this.tcConsole.Name = "tcConsole";
            this.tcConsole.Recorder = null;
            this.tcConsole.Size = new System.Drawing.Size(737, 408);
            this.tcConsole.TabIndex = 4;
            // 
            // menuItemDisconnect
            // 
            this.menuItemDisconnect.Enabled = false;
            this.menuItemDisconnect.MergeIndex = 1;
            this.menuItemDisconnect.Text = "&Disconnect";
            this.menuItemDisconnect.Click += new System.EventHandler(this.menuItemDisconnect_Click);
            // 
            // MainForm
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(736, 435);
            this.Controls.Add(this.tcConsole);
            this.Controls.Add(this.mmenu);
            this.MainMenuStrip = this.mmenu;
            this.Name = "MainForm";
            this.Text = "Rebex SSH and Telnet Terminal Client";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_Closing);
            this.mmenu.ResumeLayout(false);
            this.mmenu.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

        }

        #endregion

        /// <summary>
        /// Handles the console Disconnected event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Console_Disconnected(object sender, EventArgs e)
        {
            tcConsole.Screen.WriteLine();
            tcConsole.Screen.WriteLine("Session closed.");
            menuItemDisconnect.Enabled = false;
        }

        /// <summary>
        /// Handles the console RemoteResize event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Console_RemoteResize(object sender, RemoteResizeEventArgs e)
        {
            // set the size of the form to the size of the console
            ClientSize = tcConsole.GetControlSize(e.Columns, e.Rows);

            // console screen is not set immediately, set it manually right now
            tcConsole.SetScreenSize(e.Columns, e.Rows);
        }

        /// <summary>
        /// Handles the 'Font' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemFont_Click(object sender, System.EventArgs e)
        {
            // show dialog
            if (terminalFontDialog.ShowDialog() != DialogResult.OK)
                return;

            // check whether the selected font family is supported
            if (!TerminalFont.IsSupportedFamily(terminalFontDialog.Font.FontFamily))
            {
                MessageBox.Show(string.Format("Font {0} is not supported. Please choose another.",
                    terminalFontDialog.Font.FontFamily.Name));
                return;
            }

            // set selected font to the console
            tcConsole.TerminalFont = new TerminalFont(terminalFontDialog.Font.FontFamily, terminalFontDialog.Font.Size);
        }

        /// <summary>
        /// Handles the 'Connect' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemConnect_Click(object sender, System.EventArgs e)
        {
            try
            {
                // connection dialog
                Connection con = new Connection();

                // show connection dialog
                con.ShowDialog();

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

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

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

                // if proxy was enabled, set up the proxy property
                Proxy proxy = null;
                if (con.Protocol != Connection.ProtocolType.SerialPort && 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;
                }

                switch (con.Protocol)
                {
                    case 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
                        tcConsole.Bind(_ssh);
                        break;

                    case Connection.ProtocolType.Telnet:
                        // create a new Telnet client object
                        _telnet = new Telnet(con.HostName, con.Port);

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

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

                }

                menuItemDisconnect.Enabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Exception occured: {0}", ex));
            }
        }

        /// <summary>
        /// Handles the 'Disconnect' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemDisconnect_Click(object sender, EventArgs e)
        {
            // unbind the client object
            UnbindClient();
        }

        /// <summary>
        /// Handles the 'Option' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemOptions_Click(object sender, System.EventArgs e)
        {
            // show options dialog
            Options options = new Options();
            if (options.ShowDialog() == DialogResult.OK)
            {
                tcConsole.Options = options.TerminalOptions;
            }
        }

        /// <summary>
        /// Handles the 'Save As' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemSave_Click(object sender, System.EventArgs e)
        {
            // show Save As dialog
            if (saveDialog.ShowDialog() != DialogResult.OK)
                return;

            // get selected extension
            string[] extensionsArray = saveDialog.Filter.Split('|');
            string extensions = extensionsArray[2 * saveDialog.FilterIndex - 1];
            string[] extensionArray = extensions.Split(';');
            string extension = extensionArray[0];

            // parse TerminalCaptureFormat from the selected extension
            TerminalCaptureFormat captureFormat;
            switch (extension.ToLower())
            {
                case "*.bmp": captureFormat = TerminalCaptureFormat.Bmp; break;
                case "*.gif": captureFormat = TerminalCaptureFormat.Gif; break;
                case "*.html":
                case "*.htm": captureFormat = TerminalCaptureFormat.Html; break;
                case "*.jpeg":
                case "*.jpg": captureFormat = TerminalCaptureFormat.Jpeg; break;
                case "*.png": captureFormat = TerminalCaptureFormat.Png; break;
                case "*.tiff":
                case "*.tif": captureFormat = TerminalCaptureFormat.Tiff; break;
                case "*.ansi":
                case "*.ans": captureFormat = TerminalCaptureFormat.Ansi; break;
                default: captureFormat = TerminalCaptureFormat.Text; break;
            }

            // save the terminal console buffer
            tcConsole.Save(saveDialog.FileName, captureFormat, TerminalCaptureOptions.SaveTerminalResolution);
        }

        /// <summary>
        /// Handles the 'Start recording' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemStartRecording_Click(object sender, System.EventArgs e)
        {
            // set the recorder file from config (it was set when options form was submited)
            string fileName = Settings.Default.GetString("RecorderFile");

            // if recorder file is not specified, prompt the user to select one
            if (fileName == null || fileName.Length == 0)
            {
                if (recorderSaveDialog.ShowDialog() != DialogResult.OK)
                    return;

                fileName = recorderSaveDialog.FileName;
            }

            try
            {
                // try open the file for append and bind it to the terminal control
                tcConsole.Recorder = File.AppendText(fileName);
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Exception occured: {0}", ex));
                return;
            }

            // enable the 'Stop' menu item and disable the 'Start' menu item
            menuItemStartRecording.Enabled = false;
            menuItemStopRecording.Enabled = true;
        }

        /// <summary>
        /// Handles the 'Stop Recording' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemStopRecording_Click(object sender, System.EventArgs e)
        {
            // unbind the recorder
            UnbindRecorder();

            // enable the 'Start' menu item and disable the 'Stop' menu item
            menuItemStartRecording.Enabled = true;
            menuItemStopRecording.Enabled = false;
        }

        /// <summary>
        /// Handles the 'Exit' menu item click event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void menuItemExit_Click(object sender, System.EventArgs e)
        {
            // close the form
            Close();
        }

        /// <summary>
        /// Handles the form closing event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_Closing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            // unbind the client object
            UnbindClient();

            // unbind the recorder
            UnbindRecorder();
        }

        /// <summary>
        /// Unbinds the client object from the terminal control.
        /// </summary>
        private void UnbindClient()
        {
            if (_ssh != null || _telnet != null)
            {
                // unbind the client object from the terminal console
                tcConsole.Unbind();

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

                _ssh = null;
                _telnet = null;
            }
        }

        /// <summary>
        /// Unbinds the recorder from the terminal control.
        /// </summary>
        private void UnbindRecorder()
        {
            // unbind and close the recorder if it was bound
            if (tcConsole.Recorder != null)
            {
                TextWriter writer = tcConsole.Recorder;
                tcConsole.Recorder = null;
                writer.Close();
            }
        }

    }
}