'  
'   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
Imports System.ComponentModel
Imports System.Threading.Tasks
Imports Rebex.Net
Imports Rebex.TerminalEmulation

''' <summary>
''' SSH terminal emulator control.
''' </summary>
Partial Public Class SshTerminalControl
    Inherits TerminalControl

    Private ReadOnly _ssh As Ssh
    Private _serverName As String = ServerNameDefault
    Private _serverPort As Integer = ServerPortDefault
    Private _userName As String = UserNameDefault
    Private _userPassword As String = UserPasswordDefault
    Private _userPrivateKey As SshPrivateKey

    ' default values for both designer and properties
    Private Const ServerNameDefault As String = ""
    Private Const ServerPortDefault As Integer = Ssh.DefaultPort
    Private Const UserNameDefault As String = ""
    Private Const UserPasswordDefault As String = ""

    ''' <summary>
    ''' Gets or sets the network proxy to use to access a remote server.
    ''' </summary>
    ''' <value>The <see cref="Proxy"/> to use to access a remote server.</value>
    <Browsable(False)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public Property Proxy As Proxy
        Get
            Return _ssh.Proxy
        End Get
        Set(ByVal value As Proxy)
            _ssh.Proxy = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the server address - either a hostname or a dotted string IP address.
    ''' </summary>
    ''' <value>Server address.</value>
    <Description("Gets or sets the server address - either a hostname or a dotted string IP address.")>
    <DefaultValue(ServerNameDefault)>
    Public Property ServerName As String
        Get
            Return _serverName
        End Get
        Set(ByVal value As String)
            If Equals(value, Nothing) Then Throw New ArgumentNullException("value")
            _serverName = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the server port. Default value is 22.
    ''' </summary>
    ''' <value>Server port.</value>
    <DefaultValue(ServerPortDefault)>
    <Description("Gets or sets the server port. Default value is 22.")>
    Public Property ServerPort As Integer
        Get
            Return _serverPort
        End Get
        Set(ByVal value As Integer)
            _serverPort = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the SSH security parameters.
    ''' </summary>
    ''' <value>SSH security parameters.</value>
    <Browsable(False)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public Property ServerParameters As SshParameters
        Get
            Return _ssh.Settings.SshParameters
        End Get
        Set(ByVal value As SshParameters)
            _ssh.Settings.SshParameters = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the username to be used to authenticate to the server.
    ''' </summary>
    ''' <value>The username.</value>
    <DefaultValue(UserNameDefault)>
    <Description("Gets or sets the username to be used to authenticate to the server.")>
    Public Property UserName As String
        Get
            Return _userName
        End Get
        Set(ByVal value As String)
            If Equals(value, Nothing) Then Throw New ArgumentNullException("value")
            _userName = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the password to be used to authenticate to the server.
    ''' </summary>
    ''' <value>The password.</value>
    <Description("Gets or sets the password to be used to authenticate to the server.")>
    <DefaultValue(UserPasswordDefault)>
    Public Property UserPassword As String
        Get
            Return _userPassword
        End Get
        Set(ByVal value As String)
            _userPassword = value
        End Set
    End Property

    ''' <summary>
    ''' RSA or DSA private key for key-based authentication.
    ''' </summary>
    ''' <value>RSA or DSA private key.</value>
    <Browsable(False)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public Property UserPrivateKey As SshPrivateKey
        Get
            Return _userPrivateKey
        End Get
        Set(ByVal value As SshPrivateKey)
            _userPrivateKey = value
        End Set
    End Property

    ''' <summary>
    ''' Gets the server public key fingerprint.
    ''' </summary>
    ''' <value>Server fingerprint.</value>
    <Browsable(False)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public ReadOnly Property Fingerprint As String
        Get
            Return _ssh.Fingerprint
        End Get
    End Property

    ''' <summary>
    ''' Gets or sets the text associated with this control.  
    ''' </summary>
    <Browsable(False)>
    <EditorBrowsable(EditorBrowsableState.Never)>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public Overrides Property Text As String
        Get
            Return MyBase.Text
        End Get
        Set(ByVal value As String)
            MyBase.Text = value
        End Set
    End Property

    ''' <summary>
    ''' Gets or sets the logger used by this object.
    ''' </summary>
    ''' <value>Logger.</value>
    <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
    Public Property LogWriter As ILogWriter
        Get
            Return _ssh.LogWriter
        End Get
        Set(ByVal value As ILogWriter)
            _ssh.LogWriter = value
        End Set
    End Property

    ''' <summary>
    ''' Occurs when connected to the server.
    ''' </summary>
    Public Event Connected As EventHandler

    ''' <summary>
    ''' Raises the <see cref="Connected"/> event.
    ''' </summary>
    ''' <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
    Protected Overridable Sub OnConnected(ByVal e As EventArgs)
        Dim h As EventHandler = ConnectedEvent
        If h IsNot Nothing Then h(Me, e)
    End Sub

    ''' <summary>
    ''' Raises the <see cref="TerminalControl.Disconnected"/> event.
    ''' </summary>
    ''' <param name="e">An <see cref="EventArgs"/> that contains the event data.</param>
    Protected Overrides Sub OnDisconnected(ByVal e As EventArgs)
        MyBase.OnDisconnected(e)
        _ssh.Disconnect()
    End Sub

    ''' <summary>
    ''' Creates a new instance of SSH terminal control.
    ''' </summary>
    Public Sub New()
        _ssh = New Ssh()
    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(ByVal disposing As Boolean)
        MyBase.Dispose(disposing)

        If disposing Then
            _ssh.Dispose()
        End If
    End Sub

    ''' <summary>
    ''' Connects to the SSH server specified by <see cref="ServerName"/> and <see cref="ServerPort"/> properties
    ''' and authenticates using the credential specified by <see cref="UserName"/>, <see cref="UserPassword"/> and <see cref="UserPrivateKey"/> properties.
    ''' </summary>
    Public Sub Connect()
        If _ssh.IsConnected Then Throw New SshException("Already connected.")
        Dim serverName As String = _serverName.Trim()
        If serverName.Length = 0 Then Throw New SshException("Empty host name.")
        If _serverPort < 1 OrElse _serverPort > 65535 Then Throw New SshException("Port out of range.")
        ConnectSync(serverName, _serverPort)
    End Sub

    ''' <summary>
    ''' Begins asynchronous Connect operation.
    ''' Connects to the SSH server specified by <see cref="ServerName" /> and <see cref="ServerPort" /> properties
    ''' and authenticates using the credential specified by <see cref="UserName" />, <see cref="UserPassword" /> and <see cref="UserPrivateKey" /> properties.
    ''' </summary>
    Public Function ConnectAsync() As Task
        If _ssh.IsConnected Then Throw New SshException("Already connected.")

        Dim serverName As String = _serverName.Trim()
        If serverName.Length = 0 Then Throw New SshException("Empty host name.")
        If _serverPort < 1 Or _serverPort > 65535 Then Throw New SshException("Port out of range.")

        Return Task.Run(Sub() ConnectSync(serverName, _serverPort))
    End Function

    Private Sub ConnectSync(ByVal serverName As String, ByVal _serverPort As Integer)
        _ssh.Connect(serverName, _serverPort)
        Dim ok As Boolean = False

        Try
            _ssh.Login(_userName, _userPassword, _userPrivateKey)
            ok = True
        Finally
            If Not ok Then _ssh.Disconnect()
        End Try

        Bind(_ssh)

        If InvokeRequired Then
            Invoke(New Action(Of EventArgs)(AddressOf OnConnected), EventArgs.Empty)
        Else
            OnConnected(EventArgs.Empty)
        End If
    End Sub

    ''' <summary>
    ''' Disconnects from the SSH server.
    ''' </summary>
    Public Sub Disconnect()
        Unbind()

        If _ssh.IsConnected Then
            _ssh.Disconnect()
            OnDisconnected(EventArgs.Empty)
        End If
    End Sub
End Class
