'  
'   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.Text
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms

Imports Rebex.TerminalEmulation

''' <summary>
''' The main form of the application. It cantains the terminal control.
''' </summary>
Public Class MainForm
    Inherits Form
    ''' <summary>
    ''' Required designer variable.
    ''' </summary>
    Private components As System.ComponentModel.IContainer = Nothing
    Private terminalFontDialog As System.Windows.Forms.FontDialog
    Private terminalOpenFileDialog As System.Windows.Forms.OpenFileDialog
    Private menuItemFile As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemOpen As System.Windows.Forms.ToolStripMenuItem
    Private menuItemTools As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemFont As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents menuItemOptions As System.Windows.Forms.ToolStripMenuItem
    Private statusStrip As System.Windows.Forms.StatusStrip
    Private statusLabel As System.Windows.Forms.ToolStripStatusLabel
    Private WithEvents mmenu As System.Windows.Forms.MenuStrip
    Private WithEvents tcConsole As TerminalControl
    Private WithEvents MenuItemPlayer As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemPauseUnpause As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemSeekToNextFrame As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemSpeedUp As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemSlowDown As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemClose As System.Windows.Forms.ToolStripMenuItem
    Private WithEvents MenuItemExit As System.Windows.Forms.ToolStripMenuItem

    ''' <summary>
    ''' Player object. It simulates the ssh server.
    ''' </summary>
    Private _player As AnsiPlayerShellChannel

    ''' <summary>
    ''' Initializes this class.
    ''' </summary>
    Public Sub New()
        InitializeComponent()

        CheckForIllegalCrossThreadCalls = True

        ' set the default encoding
        tcConsole.Options.Encoding = Encoding.UTF8

        ' set the size of the form to the size equal to 80 columns, 25 rows
        SetFormSize(80, 25)

        ' register events
        AddHandler tcConsole.RemoteResize, AddressOf Console_RemoteResize
        AddHandler tcConsole.Disconnected, AddressOf tcConsole_Disconnected
    End Sub

    ''' <summary>
    ''' Clean up any resources being used.
    ''' </summary>
    ''' <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing AndAlso Not (components Is 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.components = New System.ComponentModel.Container
        Me.terminalFontDialog = New System.Windows.Forms.FontDialog
        Me.terminalOpenFileDialog = New System.Windows.Forms.OpenFileDialog
        Me.mmenu = New System.Windows.Forms.MenuStrip
        Me.menuItemFile = New System.Windows.Forms.ToolStripMenuItem
        Me.menuItemOpen = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemExit = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemPlayer = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemPauseUnpause = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemSeekToNextFrame = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemSpeedUp = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemSlowDown = New System.Windows.Forms.ToolStripMenuItem
        Me.MenuItemClose = New System.Windows.Forms.ToolStripMenuItem
        Me.menuItemTools = New System.Windows.Forms.ToolStripMenuItem
        Me.menuItemFont = New System.Windows.Forms.ToolStripMenuItem
        Me.menuItemOptions = New System.Windows.Forms.ToolStripMenuItem
        Me.tcConsole = New Rebex.TerminalEmulation.TerminalControl
        Me.statusStrip = New System.Windows.Forms.StatusStrip
        Me.statusLabel = New System.Windows.Forms.ToolStripStatusLabel
        Me.mmenu.SuspendLayout()
        Me.statusStrip.SuspendLayout()
        Me.SuspendLayout()
        '
        'terminalOpenFileDialog
        '
        Me.terminalOpenFileDialog.Filter = "ANSI files (*.ans;*.ansi)|*.ans;*.ansi|All files (*.*)|*.*"
        Me.terminalOpenFileDialog.InitialDirectory = "..\..\SampleFiles"
        '
        'mmenu
        '
        Me.mmenu.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.menuItemFile, Me.MenuItemPlayer, Me.menuItemTools})
        Me.mmenu.Location = New System.Drawing.Point(0, 0)
        Me.mmenu.Name = "mmenu"
        Me.mmenu.Size = New System.Drawing.Size(736, 24)
        Me.mmenu.TabIndex = 3
        '
        'menuItemFile
        '
        Me.menuItemFile.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.menuItemOpen, Me.MenuItemExit})
        Me.menuItemFile.MergeIndex = 0
        Me.menuItemFile.Name = "menuItemFile"
        Me.menuItemFile.Size = New System.Drawing.Size(37, 20)
        Me.menuItemFile.Text = "&File"
        '
        'menuItemOpen
        '
        Me.menuItemOpen.MergeIndex = 0
        Me.menuItemOpen.Name = "menuItemOpen"
        Me.menuItemOpen.Size = New System.Drawing.Size(167, 22)
        Me.menuItemOpen.Text = "&Open... (Ctrl+O)"
        '
        'MenuItemExit
        '
        Me.MenuItemExit.MergeIndex = 1
        Me.MenuItemExit.Name = "MenuItemExit"
        Me.MenuItemExit.Size = New System.Drawing.Size(167, 22)
        Me.MenuItemExit.Text = "E&xit (Alt+F4)"
        '
        'MenuItemPlayer
        '
        Me.MenuItemPlayer.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.MenuItemPauseUnpause, Me.MenuItemSeekToNextFrame, Me.MenuItemSpeedUp, Me.MenuItemSlowDown, Me.MenuItemClose})
        Me.MenuItemPlayer.MergeIndex = 1
        Me.MenuItemPlayer.Name = "MenuItemPlayer"
        Me.MenuItemPlayer.Size = New System.Drawing.Size(51, 20)
        Me.MenuItemPlayer.Text = "&Player"
        '
        'MenuItemPauseUnpause
        '
        Me.MenuItemPauseUnpause.MergeIndex = 0
        Me.MenuItemPauseUnpause.Name = "MenuItemPauseUnpause"
        Me.MenuItemPauseUnpause.Size = New System.Drawing.Size(222, 22)
        Me.MenuItemPauseUnpause.Text = "&Pause / Unpause (Space)"
        '
        'MenuItemSeekToNextFrame
        '
        Me.MenuItemSeekToNextFrame.MergeIndex = 1
        Me.MenuItemSeekToNextFrame.Name = "MenuItemSeekToNextFrame"
        Me.MenuItemSeekToNextFrame.Size = New System.Drawing.Size(222, 22)
        Me.MenuItemSeekToNextFrame.Text = "&Seek to next frame (N)"
        '
        'MenuItemSpeedUp
        '
        Me.MenuItemSpeedUp.MergeIndex = 2
        Me.MenuItemSpeedUp.Name = "MenuItemSpeedUp"
        Me.MenuItemSpeedUp.Size = New System.Drawing.Size(222, 22)
        Me.MenuItemSpeedUp.Text = "Speed &up 2x (Up arrow)"
        '
        'MenuItemSlowDown
        '
        Me.MenuItemSlowDown.MergeIndex = 3
        Me.MenuItemSlowDown.Name = "MenuItemSlowDown"
        Me.MenuItemSlowDown.Size = New System.Drawing.Size(222, 22)
        Me.MenuItemSlowDown.Text = "Slow &down 2x (Down arrow)"
        '
        'MenuItemClose
        '
        Me.MenuItemClose.MergeIndex = 4
        Me.MenuItemClose.Name = "MenuItemClose"
        Me.MenuItemClose.Size = New System.Drawing.Size(222, 22)
        Me.MenuItemClose.Text = "&Close and Clear (Esc)"
        '
        'menuItemTools
        '
        Me.menuItemTools.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.menuItemFont, Me.menuItemOptions})
        Me.menuItemTools.MergeIndex = 2
        Me.menuItemTools.Name = "menuItemTools"
        Me.menuItemTools.Size = New System.Drawing.Size(46, 20)
        Me.menuItemTools.Text = "&Tools"
        '
        'menuItemFont
        '
        Me.menuItemFont.MergeIndex = 0
        Me.menuItemFont.Name = "menuItemFont"
        Me.menuItemFont.Size = New System.Drawing.Size(125, 22)
        Me.menuItemFont.Text = "&Font..."
        '
        'menuItemOptions
        '
        Me.menuItemOptions.MergeIndex = 1
        Me.menuItemOptions.Name = "menuItemOptions"
        Me.menuItemOptions.Size = New System.Drawing.Size(125, 22)
        Me.menuItemOptions.Text = "&Options..."
        '
        'statusStrip
        '
        Me.statusStrip.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.statusLabel})
        Me.statusStrip.Location = New System.Drawing.Point(0, 395)
        Me.statusStrip.Name = "statusStrip"
        Me.statusStrip.Size = New System.Drawing.Size(736, 22)
        Me.statusStrip.TabIndex = 1
        '
        'statusLabel
        '
        Me.statusLabel.Name = "statusLabel"
        Me.statusLabel.Size = New System.Drawing.Size(90, 17)
        Me.statusLabel.Text = "Open a file first."
        '
        'tcConsole
        '
        Me.tcConsole.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.tcConsole.BackColor = System.Drawing.Color.Magenta
        Me.tcConsole.Location = New System.Drawing.Point(0, 27)
        Me.tcConsole.MousePasteEnabled = True
        Me.tcConsole.Name = "tcConsole"
        Me.tcConsole.Recorder = Nothing
        Me.tcConsole.ScrollbackResetOnDisplayActivity = True
        Me.tcConsole.ScrollBarEnabled = True
        Me.tcConsole.SelectionMode = TextSelectionMode.Standard
        Me.tcConsole.Size = New System.Drawing.Size(737, 400)
        Me.tcConsole.TabIndex = 2
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(736, 425)
        Me.Controls.Add(Me.tcConsole)
        Me.Controls.Add(Me.statusStrip)
        Me.Controls.Add(Me.mmenu)
        Me.KeyPreview = True
        Me.MainMenuStrip = Me.mmenu
        Me.Name = "MainForm"
        Me.Text = "Rebex ANSI Player"
        Me.mmenu.ResumeLayout(False)
        Me.mmenu.PerformLayout()
        Me.statusStrip.ResumeLayout(False)
        Me.statusStrip.PerformLayout()
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub

#End Region

    ''' <summary>
    ''' Sets the correct form size with respect to all controls on it.
    ''' </summary>
    ''' <param name="columns">Number of columns of the console.</param>
    ''' <param name="rows">Number of rows of the console.</param>
    Private Sub SetFormSize(ByVal columns As Integer, ByVal rows As Integer)
        ' set the size of the form to the size of the console + height of the status bar
        Dim newSize As Size = tcConsole.GetControlSize(columns, rows)
        newSize.Height += statusStrip.Height
        ClientSize = newSize

        ' console screen is not set immediately, set it manually right now
        tcConsole.SetScreenSize(columns, rows)
    End Sub

    ''' <summary>
    ''' Handles the console RemoteResize event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub Console_RemoteResize(ByVal sender As Object, ByVal e As RemoteResizeEventArgs)
        SetFormSize(e.Columns, e.Rows)
    End Sub

    ''' <summary>
    '''  Handles the console Disconnected event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub tcConsole_Disconnected(ByVal sender As Object, ByVal e As EventArgs)
        ' update the status bar
        statusLabel.Text = "Playing finished."
    End Sub

    ''' <summary>
    ''' Handles the 'Font' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemFont_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemFont.Click
        ' show the font dialog
        If terminalFontDialog.ShowDialog() <> DialogResult.OK Then
            Return
        End If

        ' check whether the selected font family is supported
        If Not TerminalFont.IsSupportedFamily(terminalFontDialog.Font.FontFamily) Then
            MessageBox.Show(String.Format("Font {0} is not supported. Please choose another.", terminalFontDialog.Font.FontFamily.Name))
            Return
        End If

        ' set selected font to the console
        tcConsole.TerminalFont = New TerminalFont(terminalFontDialog.Font.FontFamily, terminalFontDialog.Font.Size)
    End Sub

    ''' <summary>
    ''' Handles the terminal control keydown event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub tcConsole_KeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        If e.KeyCode = Keys.Space Then
            ' pause / unpause the player on space
            PlayerPause()
        ElseIf e.KeyCode = Keys.N OrElse e.KeyCode = Keys.S Then
            ' seek to the next frame on 'N' or 'S'
            PlayerSeekToNextFrame()
        ElseIf e.KeyCode = Keys.Right OrElse e.KeyCode = Keys.Up Then
            ' increase the speed on right arrow or up arrow
            PlayerSpeedUp()
        ElseIf e.KeyCode = Keys.Left OrElse e.KeyCode = Keys.Down Then
            ' decrease the speed on left arrow or down arrow
            PlayerSlowDown()
        ElseIf e.KeyCode = Keys.Escape Then
            ' close the player and clear the screen on ESC
            PlayerClose()
        ElseIf e.KeyCode = Keys.O AndAlso e.Modifiers = Keys.Control Then
            ' open file on CTRL+O
            Open()
        ElseIf e.KeyCode = Keys.F4 AndAlso e.Modifiers = Keys.Alt Then
            ' application exit on ALT+F4
            [Exit]()
        End If
    End Sub

    ''' <summary>
    ''' Updates player status bar.
    ''' </summary>
    Private Sub UpdateStatus()
        If Not _player Is Nothing Then
            If _player.Connected Then
                If _player.IsPaused Then
                    statusLabel.Text = "Paused..."
                Else
                    statusLabel.Text = "Playing..."
                End If

                If _player.Speed <> 1 Then
                    statusLabel.Text += "   | Speed: " + _player.Speed.ToString()
                End If
            End If
        End If
    End Sub

    ''' <summary>
    ''' Handles the form closing event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MainForm_Closing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        ' if the player is still connected, close it
        If (Not _player Is Nothing) AndAlso _player.Connected Then
            _player.Close()
        End If
    End Sub

    ''' <summary>
    ''' Handles the 'Option' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemOptions_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemOptions.Click
        ' show options dialog
        Dim options As New Options(tcConsole.Options)
        options.ShowDialog()
    End Sub

    ''' <summary>
    ''' Handles the 'Open' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub menuItemOpen_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles menuItemOpen.Click
        Open()
    End Sub

    ''' <summary>
    ''' Handles the 'Exit' menu item click event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MenuItemExit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemExit.Click
        [Exit]()
    End Sub

    ''' <summary>
    ''' Event handler for menuItemPauseUnpause click'.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MenuItemPauseUnpause_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemPauseUnpause.Click
        PlayerPause()
    End Sub

    ''' <summary>
    ''' Event handler for menuItemSeektoNextFrame click.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MenuItemSeekToNextFrame_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemSeekToNextFrame.Click
        PlayerSeekToNextFrame()
    End Sub

    ''' <summary>
    ''' Event handler for menuItemSpeedUp click.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MenuItemSpeedUp_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemSpeedUp.Click
        PlayerSpeedUp()
    End Sub

    ''' <summary>
    ''' Event handler for menuItemSlowDown click.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MenuItemSlowDown_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemSlowDown.Click
        PlayerSlowDown()
    End Sub

    ''' <summary>
    ''' Event handler for menuItemClose click.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    ''' <remarks></remarks>
    Private Sub MenuItemClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemClose.Click
        PlayerClose()
    End Sub

    ''' <summary>
    ''' Asks for a file and starts the player.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub Open()
        ' open file dialog
        If terminalOpenFileDialog.ShowDialog() <> DialogResult.OK Then
            Return
        End If

        ' if player was already created, close it and unbind it
        If Not _player Is Nothing Then
            If _player.Connected Then
                _player.Close()
            End If

            tcConsole.Unbind()

            ' also clear screen
            tcConsole.Screen.Clear()
        End If

        ' create a new AnsiPlayerShellChannel based on the selected file
        _player = New AnsiPlayerShellChannel(terminalOpenFileDialog.OpenFile())

        ' bind created AnsiPlayerShellChannel
        tcConsole.Bind(_player)

        ' update the status bar
        statusLabel.Text = "Playing..."
    End Sub

    ''' <summary>
    ''' Exits from the application.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub [Exit]()
        ' if player was already created, close it and unbind it
        If Not _player Is Nothing Then
            If _player.Connected Then
                _player.Close()
            End If

            tcConsole.Unbind()
        End If

        Close()
    End Sub

    ''' <summary>
    ''' Pauses player.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PlayerPause()
        If Not _player Is Nothing Then
            _player.IsPaused = Not _player.IsPaused
            UpdateStatus()
        End If
    End Sub

    ''' <summary>
    ''' Seeks player to next frame.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PlayerSeekToNextFrame()
        If Not _player Is Nothing Then
            _player.SeekToNextFrame()

            UpdateStatus()
        End If
    End Sub

    ''' <summary>
    ''' Speeds up player.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PlayerSpeedUp()
        If Not _player Is Nothing Then
            _player.Speed *= 2

            UpdateStatus()
        End If
    End Sub

    ''' <summary>
    ''' Slows down player.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PlayerSlowDown()
        If Not _player Is Nothing Then
            _player.Speed /= 2

            UpdateStatus()
        End If
    End Sub

    ''' <summary>
    ''' Closes player.
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub PlayerClose()
        If Not _player Is Nothing Then
            _player.Close()
            tcConsole.Screen.Clear()

            UpdateStatus()
        End If
    End Sub
End Class
