'  
'   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.
' 

Imports System.Drawing
Imports System.IO
Imports System.Net
Imports System.Windows.Forms
Imports Rebex.TerminalEmulation
Imports Rebex.Net

''' <summary>
''' The main form of the application. It contains the terminal control.
''' </summary>
Public Class MainForm
    Inherits Form
    ''' <summary>
    ''' The Ssh object. Used to communicate with SSH servers.
    ''' </summary>
    Private _ssh As Ssh

    ''' <summary>
    ''' The Telnet object. Used to communicate with telnet servers.
    ''' </summary>
    Private _telnet As Telnet

    ''' <summary>
    ''' Contains either _virtualTerminal or terminalControl (depending on the current terminal mode)
    ''' </summary>
    Private _terminal As ITerminal

    ''' <summary>
    ''' Virtual terminal object (a terminal with invisible console)
    ''' </summary>
    Private _virtualTerminal As VirtualTerminal

    ''' <summary>
    ''' Required designer variable.
    ''' </summary>
    Private components As System.ComponentModel.IContainer = Nothing
    Private btnConnect As Button
    Private btnOptions As Button
    Private btnDisconnect As Button
    Private btnExit As Button
    Private btnTerminal As Button
    Private btnExecute As Button
    Private rtbCode As RichTextBox
    Private lbScripts As ListBox
    Private terminalControl As Rebex.TerminalEmulation.TerminalControl

    ''' <summary>
    ''' Initialize this class.
    ''' </summary>
    Public Sub New()
        InitializeComponent()
        CheckForIllegalCrossThreadCalls = True
    End Sub

    ''' <summary>
    ''' Initialize this class after form load.
    ''' </summary>
    Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ShowWelcomeMessages()

        ' create an instance of VirtualTerminal object
        _virtualTerminal = New VirtualTerminal(80, 50)

        ' set options
        Dim options As TerminalOptions = Settings.[Default].TerminalOptions
        terminalControl.Options = options
        _virtualTerminal.Options = options

        ' register the Disconnected event
        AddHandler terminalControl.Disconnected, New EventHandler(AddressOf terminal_Disconnected)
        AddHandler _virtualTerminal.Disconnected, New EventHandler(AddressOf terminal_Disconnected)

        ' use TerminalControl by default
        _terminal = terminalControl

        ' get and display the list of sample scripts
        LoadScripts()
    End Sub

    ''' <summary>
    ''' Clean up any resources being used.
    ''' </summary>
    ''' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposing AndAlso (components IsNot Nothing) Then
            components.Dispose()
        End If
        MyBase.Dispose(disposing)
    End Sub

    #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 Sub InitializeComponent()
        Me.terminalControl = New TerminalEmulation.TerminalControl()
        Me.btnConnect = New System.Windows.Forms.Button()
        Me.btnOptions = New System.Windows.Forms.Button()
        Me.btnDisconnect = New System.Windows.Forms.Button()
        Me.btnExit = New System.Windows.Forms.Button()
        Me.btnTerminal = New System.Windows.Forms.Button()
        Me.btnExecute = New System.Windows.Forms.Button()
        Me.rtbCode = New System.Windows.Forms.RichTextBox()
        Me.lbScripts = New System.Windows.Forms.ListBox()
        Me.SuspendLayout()
        ' 
        ' terminalControl
        ' 
        Me.terminalControl.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) Or System.Windows.Forms.AnchorStyles.Left) Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.terminalControl.BackColor = System.Drawing.Color.Magenta
        Me.terminalControl.Cursor = System.Windows.Forms.Cursors.IBeam
        Me.terminalControl.CursorMouse = System.Windows.Forms.Cursors.Arrow
        Me.terminalControl.CursorText = System.Windows.Forms.Cursors.IBeam
        Me.terminalControl.HistoryMaxLength = 1000
        Me.terminalControl.Location = New System.Drawing.Point(1, 253)
        Me.terminalControl.Name = "terminalControl"
        Me.terminalControl.Recorder = Nothing
        Me.terminalControl.Size = New System.Drawing.Size(740, 300)
        Me.terminalControl.TabIndex = 4
        ' 
        ' btnConnect
        ' 
        Me.btnConnect.Location = New System.Drawing.Point(7, 7)
        Me.btnConnect.Name = "btnConnect"
        Me.btnConnect.Size = New System.Drawing.Size(75, 23)
        Me.btnConnect.TabIndex = 5
        Me.btnConnect.Text = "Connect..."
        Me.btnConnect.UseVisualStyleBackColor = True
        AddHandler Me.btnConnect.Click, New System.EventHandler(AddressOf Me.btnConnect_Click)
        ' 
        ' btnOptions
        ' 
        Me.btnOptions.Location = New System.Drawing.Point(7, 65)
        Me.btnOptions.Name = "btnOptions"
        Me.btnOptions.Size = New System.Drawing.Size(75, 23)
        Me.btnOptions.TabIndex = 6
        Me.btnOptions.Text = "Options..."
        Me.btnOptions.UseVisualStyleBackColor = True
        AddHandler Me.btnOptions.Click, New System.EventHandler(AddressOf Me.btnOptions_Click)
        ' 
        ' btnDisconnect
        ' 
        Me.btnDisconnect.Enabled = False
        Me.btnDisconnect.Location = New System.Drawing.Point(7, 36)
        Me.btnDisconnect.Name = "btnDisconnect"
        Me.btnDisconnect.Size = New System.Drawing.Size(75, 23)
        Me.btnDisconnect.TabIndex = 7
        Me.btnDisconnect.Text = "Disconnect"
        Me.btnDisconnect.UseVisualStyleBackColor = True
        AddHandler Me.btnDisconnect.Click, New System.EventHandler(AddressOf Me.btnDisconnect_Click)
        ' 
        ' btnExit
        ' 
        Me.btnExit.Location = New System.Drawing.Point(7, 94)
        Me.btnExit.Name = "btnExit"
        Me.btnExit.Size = New System.Drawing.Size(75, 23)
        Me.btnExit.TabIndex = 8
        Me.btnExit.Text = "Exit"
        Me.btnExit.UseVisualStyleBackColor = True
        AddHandler Me.btnExit.Click, New System.EventHandler(AddressOf Me.btnExit_Click)
        ' 
        ' btnTerminal
        ' 
        Me.btnTerminal.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.btnTerminal.Location = New System.Drawing.Point(0, 230)
        Me.btnTerminal.Name = "btnTerminal"
        Me.btnTerminal.Size = New System.Drawing.Size(741, 23)
        Me.btnTerminal.TabIndex = 9
        Me.btnTerminal.Text = "Use VirtualTerminal class"
        Me.btnTerminal.UseVisualStyleBackColor = True
        AddHandler Me.btnTerminal.Click, New System.EventHandler(AddressOf Me.btnTerminal_Click)
        ' 
        ' btnExecute
        ' 
        Me.btnExecute.Enabled = False
        Me.btnExecute.Location = New System.Drawing.Point(88, 201)
        Me.btnExecute.Name = "btnExecute"
        Me.btnExecute.Size = New System.Drawing.Size(196, 23)
        Me.btnExecute.TabIndex = 10
        Me.btnExecute.Text = "Run script"
        Me.btnExecute.UseVisualStyleBackColor = True
        AddHandler Me.btnExecute.Click, New System.EventHandler(AddressOf Me.btnExecute_Click)
        ' 
        ' rtbCode
        ' 
        Me.rtbCode.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Left) Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.rtbCode.BackColor = System.Drawing.SystemColors.ControlLightLight
        Me.rtbCode.BorderStyle = System.Windows.Forms.BorderStyle.None
        Me.rtbCode.Font = New System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CByte(0))
        Me.rtbCode.Location = New System.Drawing.Point(290, 0)
        Me.rtbCode.Name = "rtbCode"
        Me.rtbCode.[ReadOnly] = True
        Me.rtbCode.Size = New System.Drawing.Size(451, 230)
        Me.rtbCode.TabIndex = 11
        Me.rtbCode.Text = "Code goes here..."
        ' 
        ' lbScripts
        ' 
        Me.lbScripts.BorderStyle = System.Windows.Forms.BorderStyle.None
        Me.lbScripts.Font = New System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CByte(0))
        Me.lbScripts.FormattingEnabled = True
        Me.lbScripts.ItemHeight = 16
        Me.lbScripts.Location = New System.Drawing.Point(89, 0)
        Me.lbScripts.Name = "lbScripts"
        Me.lbScripts.ScrollAlwaysVisible = True
        Me.lbScripts.Size = New System.Drawing.Size(195, 192)
        Me.lbScripts.Sorted = True
        Me.lbScripts.TabIndex = 12
        AddHandler Me.lbScripts.SelectedValueChanged, New System.EventHandler(AddressOf Me.lbScripts_SelectedValueChanged)
        ' 
        ' MainForm
        ' 
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(741, 554)
        Me.Controls.Add(Me.lbScripts)
        Me.Controls.Add(Me.rtbCode)
        Me.Controls.Add(Me.btnExecute)
        Me.Controls.Add(Me.btnTerminal)
        Me.Controls.Add(Me.btnExit)
        Me.Controls.Add(Me.btnDisconnect)
        Me.Controls.Add(Me.btnOptions)
        Me.Controls.Add(Me.btnConnect)
        Me.Controls.Add(Me.terminalControl)
        Me.MinimumSize = New System.Drawing.Size(640, 480)
        Me.Name = "MainForm"
        Me.Text = "Rebex Terminal Scripting Sample"
        AddHandler Me.FormClosing, New System.Windows.Forms.FormClosingEventHandler(AddressOf Me.MainForm_Closing)
        Me.ResumeLayout(False)

    End Sub

    #End Region

    Private Sub ShowWelcomeMessages()
        ' show the welcome text
        rtbCode.Text = Resources.Welcome

        ' show some text in the TerminalControl
        terminalControl.Screen.WriteLine(Resources.TerminalWelcome)
    End Sub

    ''' <summary>
    ''' Get the list of sample scripts using reflection and fill the listbox
    ''' </summary>
    Private Sub LoadScripts()
        lbScripts.Items.Clear()
        For Each type As Type In [GetType]().Assembly.GetTypes()
            If type.IsClass AndAlso type.FullName.StartsWith("Rebex.Samples.Scripts.") Then
                lbScripts.Items.Add(New ListBoxItem(type))
            End If
        Next
    End Sub

    ''' <summary>
    ''' Unbinds the client object from the terminal control and disconnects.
    ''' </summary>
    Private Sub Disconnect()
        btnDisconnect.Enabled = False
        btnConnect.Enabled = True

        If _ssh IsNot Nothing OrElse _telnet IsNot Nothing Then
            ' unbind the client object from the terminal console
            _terminal.Unbind()

            If _ssh IsNot Nothing Then
                ' disconnect & dispose the client object
                _ssh.Disconnect()
                _ssh.Dispose()
                _ssh = Nothing
            End If

            _telnet = Nothing
        End If

        ' close recorder
        If _terminal.Recorder IsNot Nothing Then
            _terminal.Recorder.Flush()
            _terminal.Recorder.Close()
            _terminal.Recorder = Nothing
        End If
    End Sub

    ''' <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 Sub Connect(showDialog As Boolean)
        Try
            ' connection dialog
            Dim con As New Connection()
            con.EnableTelnetLoginInfo = True

            ' show connection dialog
            If showDialog Then
                con.ShowDialog()

                ' if connection dialog was not confirmed, return
                If Not con.OK Then
                    Return
                End If
            End If

            ' unbind the client object from the terminal console
            Disconnect()

            ' clear the screen
            _terminal.Screen.Clear()

            ' set up the recorder
            Dim recorderFile As String = Settings.Default.GetString("RecorderFile")
            If String.IsNullOrEmpty(recorderFile) Then
                _terminal.Recorder = Nothing
            Else
                ' FileMode.Create -> replace old file (keep recording of the last session only)
                Dim fs As Stream = File.Open(recorderFile, FileMode.Create, FileAccess.Write, FileShare.Read)
                _terminal.Recorder = New StreamWriter(fs)
            End If

            ' if proxy was enabled, set up the proxy property
            Dim proxy As Proxy = Nothing
            If con.ProxyEnabled Then
                Dim proxyNetworkCredential As NetworkCredential = Nothing

                If con.ProxyLoginName.Length > 0 Then
                    If con.ProxyPassword.Length = 0 Then
                        con.ProxyPassword = Nothing
                    End If

                    proxyNetworkCredential = New System.Net.NetworkCredential(con.ProxyLoginName, con.ProxyPassword)
                End If

                proxy = New Proxy(con.ProxyType, con.ProxyHostName, con.ProxyPort)
                proxy.AuthenticationMethod = ProxyAuthentication.Basic
                proxy.Credentials = proxyNetworkCredential
            End If

            If con.Protocol = Connection.ProtocolType.Ssh Then
                ' create a new Ssh client object
                _ssh = New Ssh()

                ' assign proxy if enabled
                If proxy IsNot Nothing Then
                    _ssh.Proxy = proxy
                End If

                ' 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
                Dim privateKey As SshPrivateKey = Nothing
                If con.PrivateKey.Length > 0 Then
                    Dim pp As New PassphraseDialog()
                    If pp.ShowDialog() = DialogResult.OK Then
                        privateKey = New SshPrivateKey(con.PrivateKey, pp.Passphrase)
                    Else
                        Throw New Exception("Password not entered.")
                    End If
                End If

                If con.Password.Length = 0 Then
                    con.Password = Nothing
                End If

                ' 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 IsNot Nothing Then
                    _telnet.Proxy = proxy
                End If

                If con.LoginName IsNot Nothing AndAlso con.LoginName.Length > 0 AndAlso con.Password IsNot Nothing AndAlso con.Password.Length > 0 Then
                    ' when using TerminalControl, automated data processing
                    ' has to be disabled before running scripts
                    If _terminal Is terminalControl Then
                        terminalControl.SetDataProcessingMode(DataProcessingMode.None)
                        terminalControl.UserInputEnabled = False
                    End If

                    ' 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 Is terminalControl Then
                        terminalControl.SetDataProcessingMode(DataProcessingMode.Automatic)
                        terminalControl.UserInputEnabled = True
                    End If
                Else
                    ' bind the Telnet instance to the terminal console
                    ' (connects to the server automatically)
                    _terminal.Bind(_telnet)
                End If
            End If

            btnDisconnect.Enabled = True
            btnConnect.Enabled = False
        Catch ex As Exception
            MessageBox.Show(String.Format("Error occurred: {0}", ex))
        End Try
    End Sub

    ''' <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 Sub LoginToTelnet(scripting As Scripting, userName As String, password As String)
        ' 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()
    End Sub


    ''' <summary>
    ''' Handles ITerminal.Disconnected event.
    ''' </summary>
    Private Sub terminal_Disconnected(sender As Object, e As EventArgs)
        ' display a message on the terminal screen
        _terminal.Screen.WriteLine()
        _terminal.Screen.WriteLine("Session closed.")

        ' unbind the client object and disconnect
        Disconnect()
    End Sub

    ''' <summary>
    ''' Handles 'Exit' button click.
    ''' </summary>
    Private Sub btnExit_Click(sender As Object, e As EventArgs)
        ' close the form
        Close()
    End Sub

    ''' <summary>
    ''' Handles application form closing event.
    ''' </summary>
    Private Sub MainForm_Closing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs)
        ' unbind the client object and disconnect
        Disconnect()
    End Sub

    ''' <summary>
    ''' Handles 'Options' button click.
    ''' </summary>
    Private Sub btnOptions_Click(sender As Object, e As EventArgs)
        ' show options dialog
        Dim options As New Options()
        If options.ShowDialog() = DialogResult.OK Then
            terminalControl.Options = options.TerminalOptions
            _virtualTerminal.Options = options.TerminalOptions
        End If
    End Sub

    ''' <summary>
    ''' Handles 'Connect' button click.
    ''' </summary>
    Private Sub btnConnect_Click(sender As Object, e As EventArgs)
        ' show the connection dialog and connect
        Connect(True)
    End Sub

    ''' <summary>
    ''' Handles 'Disconnect' button click.
    ''' </summary>
    Private Sub btnDisconnect_Click(sender As Object, e As EventArgs)
        ' unbind the client object and disconnect
        Disconnect()
    End Sub

    ''' <summary>
    ''' Handles terminal object change button.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub btnTerminal_Click(sender As Object, e As EventArgs)
        ' unbind the client object and disconnect
        Disconnect()

        If _terminal Is terminalControl Then
            ' switch from using TerminalControl to VirtualTerminal

            Controls.Remove(terminalControl)
            MinimumSize = New Size(MinimumSize.Width, btnTerminal.Bottom + Height - ClientSize.Height)
            MaximumSize = New Size(Integer.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 Or AnchorStyles.Left Or AnchorStyles.Bottom Or AnchorStyles.Right
            MinimumSize = New Size(MinimumSize.Width, 420)
            MaximumSize = New Size(Integer.MaxValue, Integer.MaxValue)
            ClientSize = New Size(btnTerminal.Right, terminalControl.Bottom + 1)
            Controls.Add(terminalControl)

            _terminal = terminalControl
            btnTerminal.Text = "Switch to VirtualTerminal class"
        End If

    End Sub

    ''' <summary>
    ''' Handles 'Execute' button click.
    ''' </summary>
    Private Sub btnExecute_Click(sender As Object, e As EventArgs)
        ' get the selected sample script object
        Dim sampleScript As ListBoxItem = TryCast(lbScripts.SelectedItem, ListBoxItem)
        If sampleScript Is Nothing Then
            Return
        End If

        If _ssh Is Nothing AndAlso _telnet Is Nothing Then
            Dim result As DialogResult = MessageBox.Show(Me, "Not connected to the server. Connect now?", "Not Connected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question)
            If result <> System.Windows.Forms.DialogResult.Yes Then
                Return
            End If

            ' connect without showing a dialog
            Connect(False)

            ' return if not connected,
            If _ssh Is Nothing AndAlso _telnet Is Nothing Then
                Return
            End If
        End If

        ' when using TerminalControl, automated data processing
        ' has to be disabled before running scripts
        If _terminal Is terminalControl Then
            terminalControl.SetDataProcessingMode(DataProcessingMode.None)
            terminalControl.UserInputEnabled = False
        End If

        ' execute the script
        sampleScript.Execute(_terminal.Scripting)

        ' when using TerminalControl, enable automated data processing when done
        If _terminal Is terminalControl Then
            terminalControl.SetDataProcessingMode(DataProcessingMode.Automatic)
            terminalControl.UserInputEnabled = True
        End If
    End Sub

    ''' <summary>
    ''' Handles selected script change event.
    ''' </summary>
    Private Sub lbScripts_SelectedValueChanged(sender As Object, e As EventArgs)
        Dim item As ListBoxItem = TryCast(lbScripts.SelectedItem, ListBoxItem)
        If item IsNot Nothing Then
            Dim code As String = Resources.ResourceManager.GetString(item.TypeName)
            Colorize.SetCode(rtbCode, code)
            btnExecute.Enabled = True
        Else
            rtbCode.Text = "No script selected."
            btnExecute.Enabled = False
        End If
    End Sub
End Class
