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

#Const ASYNC = True

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Data
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.Windows.Forms.ComponentModel
Imports System.IO

Imports Rebex.IO
Imports Rebex.IO.Compression

''' <summary>
''' Main WinFromZipExtractor form.
''' </summary>
Public Class MainForm

    ' ZIP archive.
    Private _archive As ZipArchive

    ''' <summary>
    ''' Event handler for ZIP archive "Browse" button.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub buttonBrowseZip_Click(ByVal sender As Object, ByVal e As EventArgs) Handles buttonBrowseZip.Click
        ' display an OpenFileDialog to open a ZIP archive
        openFileDialog.FileName = textArchivePath.Text
        Dim result As DialogResult = openFileDialog.ShowDialog(Me)
        If result <> DialogResult.OK Then
            Return
        End If

        ' set the ZIP archive path
        textArchivePath.Text = openFileDialog.FileName
    End Sub

    ''' <summary>
    ''' Event handler for target folder "Browse" button.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub buttonBrowseFolder_Click(ByVal sender As Object, ByVal e As EventArgs) Handles buttonBrowseFolder.Click
        ' display a FolderBrowserDialog to browse a target folder
        folderBrowserDialog.SelectedPath = textTargetPath.Text
        Dim result As DialogResult = folderBrowserDialog.ShowDialog()
        If result <> DialogResult.OK Then
            Return
        End If

        ' sets the target folder path
        textTargetPath.Text = folderBrowserDialog.SelectedPath
    End Sub

    ''' <summary>
    ''' Event handler for Cancel button.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub buttonCancel_Click(ByVal sender As Object, ByVal e As EventArgs) Handles buttonCancel.Click
        If _archive IsNot Nothing Then
            _archive.Cancel()
        End If
    End Sub

    ''' <summary>
    ''' Event handler for ZipArchive's ProgressChanged event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub archive_ProgressChanged(ByVal sender As Object, ByVal e As ZipProgressChangedEventArgs)
        ' update the progress bar
        progressBar.Value = e.ProgressPercentage

        ' update the status label
        Select Case e.OperationStep
            Case ArchiveOperationStep.FileChecking
                statusLabel.Text = "Checking " + e.ArchiveItemPath
                Exit Select
            Case ArchiveOperationStep.FileProcessing
                statusLabel.Text = "Extracting " + e.ArchiveItemPath
                Exit Select
        End Select

#If Not ASYNC Then
        ' process any application events to keep the window responsive
        Application.DoEvents()
#End If
    End Sub

    ''' <summary>
    ''' Event handler for ZipArchive's ProblemDetected event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub archive_ProblemDetected(ByVal sender As Object, ByVal e As ZipProblemDetectedEventArgs)
        ' we only handle 'file exists' problems
        If e.ProblemType = ArchiveProblemType.FileExists Then
            If checkBoxOverwrite.Checked Then
                ' if the 'overwrite existing files' checkbox was checked, then just overwrite the file
                e.Action = ArchiveProblemActions.Overwrite
            Else
                ' use FileExistsDialog to ask the user what to do
                Dim dialog As New FileExistsDialog()
                dialog.SetFilePath(e.ExternalItemPath)
                Dim result As DialogResult = dialog.ShowDialog()

                ' set the action selected by the user
                Select Case result
                    Case DialogResult.Yes
                        e.Action = ArchiveProblemActions.Overwrite
                        Exit Select
                    Case DialogResult.No
                        e.Action = ArchiveProblemActions.Skip
                        Exit Select
                    Case Else
                        e.Action = ArchiveProblemActions.Cancel
                        Exit Select
                End Select
            End If
        End If
    End Sub

#If Not ASYNC Then
    ''' <summary>
    ''' Event handler for Extract button. Does most of the work.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub buttonExtract_Click(ByVal sender As Object, ByVal e As EventArgs) Handles buttonExtract.Click
        Try
            ' show an error if path to a ZIP archive is missing
            If textArchivePath.Text.Length = 0 Then
                ShowError("Path to a ZIP archive was not entered.")
                Return
            End If

            ' show an error if path to a target folder is missing
            If textTargetPath.Text.Length = 0 Then
                ShowError("Path to a target folder was not entered.")
                Return
            End If

            ' disable buttons and boxes
            SetEnabled(False)

            ' open a ZIP archive
            _archive = New ZipArchive(textArchivePath.Text, ArchiveOpenMode.Open, ArchiveAccessMode.Read)

            ' set up the event handlers
            AddHandler _archive.PasswordRequired, AddressOf archive_PasswordRequired
            AddHandler _archive.ProgressChanged, AddressOf archive_ProgressChanged
            AddHandler _archive.ProblemDetected, AddressOf archive_ProblemDetected

            ' call the ExtractAll method to do all the work
            Dim result As ArchiveOperationResult = _archive.ExtractAll(textTargetPath.Text)

            ' display the results
            statusLabel.Text = "Done"
            Dim message As String = String.Format("Extracted {0} files.", result.FilesAffected)
            MessageBox.Show(Me, message, MyBase.Text)
        Catch [error] As ZipException
            ' display an error reported by the ZIP component
            ShowError([error].Message)
        Catch [error] As FileNotFoundException
            ' display file-not-found error
            ShowError("File not found: " + [error].FileName)
        Finally
            ' close the ZIP archive when done
            CloseArchive()
        End Try
    End Sub
#Else
    ''' <summary>
    ''' Event handler for Extract button. Starts asynchronous operation that does most of the work.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub buttonExtract_Click(ByVal sender As Object, ByVal e As EventArgs) Handles buttonExtract.Click
        Dim success As Boolean = False
        Try
            ' show an error if path to a ZIP archive is missing
            If textArchivePath.Text.Length = 0 Then
                ShowError("Path to a ZIP archive was not entered.")
                Return
            End If

            ' show an error if path to a target folder is missing
            If textTargetPath.Text.Length = 0 Then
                ShowError("Path to a target folder was not entered.")
                Return
            End If

            ' disable buttons and boxes
            SetEnabled(False)

            ' open a ZIP archive
            _archive = New ZipArchive(textArchivePath.Text, ArchiveOpenMode.Open, ArchiveAccessMode.Read)

            ' set up the event handlers
            AddHandler _archive.PasswordRequired, AddressOf archive_PasswordRequired
            AddHandler _archive.ProgressChanged, AddressOf archive_ProgressChanged
            AddHandler _archive.ProblemDetected, AddressOf archive_ProblemDetected
            AddHandler _archive.ExtractCompleted, AddressOf archive_ExtractCompleted

            ' starts the ZipArchive's Extract method in a background thread
            _archive.ExtractAsync("*", textTargetPath.Text, TraversalMode.Recursive, 0, 0)
            ' ^ when the Extract operation is finished, archive_ExtractCompleted method will be called

            ' background thread was started successfully
            success = True
        Catch [error] As ZipException
            ' display an error reported by the ZIP component
            ShowError([error].Message)
        Catch [error] As FileNotFoundException
            ' display file-not-found error
            ShowError("File not found: " + [error].FileName)
        Finally
            ' close the ZIP archive if no backgroud thread was started
            If Not success Then
                CloseArchive()
            End If
        End Try
    End Sub

    ''' <summary>
    ''' Event handler that is raised when the ExtractAsync method completes.
    ''' </summary>
    Private Sub archive_ExtractCompleted(ByVal sender As Object, ByVal e As ZipExtractCompletedEventArgs)
        Try
            If e.Cancelled Then
                Return
            End If

            If e.Error IsNot Nothing Then
                ' display an error reported by the ZIP component
                ShowError(e.Error.Message)
                Return
            End If

            ' retrieve the Extract method's result
            Dim result As ArchiveOperationResult = e.Result

            ' display the results
            statusLabel.Text = "Done"
            Dim message As String = String.Format("Extracted {0} files.", result.FilesAffected)
            MessageBox.Show(Me, message, MyBase.Text)
        Finally
            ' close the ZIP archive when done
            CloseArchive()
        End Try
    End Sub
#End If

    ''' <summary>
    ''' Event handler for ZipArchive's PasswordRequired event.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub archive_PasswordRequired(ByVal sender As Object, ByVal e As ZipPasswordRequiredEventArgs)
        ' use PasswordDialog to ask the user for password
        Dim dialog As New PasswordDialog()
        dialog.SetFilePath(e.ExternalItemPath, e.Reason)
        Dim result As DialogResult = dialog.ShowDialog()

        ' set the password or cancel the operation
        Select Case result
            Case DialogResult.OK
                e.Password = dialog.Password
            Case DialogResult.Ignore
                e.Action = ArchivePasswordActions.SkipFile
            Case Else
                e.Action = ArchivePasswordActions.Cancel
        End Select
    End Sub

    ''' <summary>
    ''' Event handler for DragEnter event. Makes the form responsive to Drag and Dropped files.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MainForm_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs) Handles MyBase.DragEnter
        If (e.AllowedEffect And DragDropEffects.Link) <> 0 Then
            ' allow any FileNameW data
            Dim paths As String() = TryCast(e.Data.GetData(DataFormats.FileDrop), String())
            If paths IsNot Nothing AndAlso paths.Length = 1 Then
                e.Effect = DragDropEffects.Link
            End If
        End If
    End Sub

    ''' <summary>
    ''' Event handler for DragDrop event. Accepts Drag and Dropped files.
    ''' </summary>
    ''' <param name="sender"></param>
    ''' <param name="e"></param>
    Private Sub MainForm_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs) Handles MyBase.DragDrop
        ' accept any FileNameW data and set the ZIP archive path accordingly
        Dim paths As String() = TryCast(e.Data.GetData(DataFormats.FileDrop), String())
        If paths IsNot Nothing AndAlso paths.Length = 1 Then
            textArchivePath.Text = paths(0)
        End If
    End Sub

    ''' <summary>
    ''' Sets enabled/disabled state of form's controls.
    ''' </summary>
    ''' <param name="enabled"></param>
    Private Sub SetEnabled(ByVal enabled As Boolean)
        ' enables/disables buttons and boxes
        textArchivePath.Enabled = enabled
        textTargetPath.Enabled = enabled
        buttonBrowseZip.Enabled = enabled
        buttonBrowseFolder.Enabled = enabled
        buttonExtract.Enabled = enabled
        checkBoxOverwrite.Enabled = enabled
        panelIcon.Enabled = enabled

        ' cancel button is enabled when the rest is disabled
        buttonCancel.Enabled = Not enabled

        ' shows default cursor when enabled and wait cursor if disabled
        If enabled Then
            MyBase.Cursor = Cursors.[Default]
        Else
            MyBase.Cursor = Cursors.WaitCursor
        End If
    End Sub

    ''' <summary>
    ''' Show an error message.
    ''' </summary>
    ''' <param name="message">Error message.</param>
    Private Sub ShowError(ByVal message As String)
        MessageBox.Show(Me, message, MyBase.Text)
    End Sub

    ''' <summary>
    ''' Closes the ZIP archive (if open) and makes the form usable again.
    ''' </summary>
    Private Sub CloseArchive()
        ' close the ZIP archive if open
        If _archive IsNot Nothing Then
            _archive.Close(ArchiveSaveAction.None)
            _archive = Nothing
        End If

        ' enable buttons and boxes
        SetEnabled(True)

        ' reset the status label and progress bar values
        statusLabel.Text = "Ready"
        progressBar.Value = 0
    End Sub
End Class
