'  
'   Rebex Sample Code License
' 
'   Copyright 2026, 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 System.Threading.Tasks

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

            ' starts the ZipArchive's Extract method in a background thread
            _archive.ExtractAsync("*", textTargetPath.Text, TraversalMode.Recursive, 0, 0).ContinueWith(AddressOf TaskFinished)

            ' 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

    Private Sub TaskFinished(ByVal finishedTask As Task(Of ArchiveOperationResult))
        If Me.InvokeRequired Then
            Invoke(New Action(Of Task(Of ArchiveOperationResult))(AddressOf TaskFinished), New Object() {finishedTask})
            Return
        End If

        Try
            If finishedTask.IsCanceled Then
                Return
            End If

            If finishedTask.IsFaulted Then
                ' display errors reported by the ZIP component
                For Each [error] As Exception In finishedTask.Exception.InnerExceptions
                    ShowError([error].Message)
                Next
                Return
            End If

            ' retrieve the Extract method's result
            Dim result As ArchiveOperationResult = finishedTask.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
