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

#undef ASYNC
#define ASYNC

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.ComponentModel;
using System.IO;
using System.Threading.Tasks;

using Rebex.IO;
using Rebex.IO.Compression;

namespace Rebex.Samples
{
    /// <summary>
    /// Main WinFromZipExtractor form.
    /// </summary>
    public partial class MainForm : Form
    {
        // ZIP archive.
        private ZipArchive _archive;

        public MainForm()
        {
            CheckForIllegalCrossThreadCalls = true;
            InitializeComponent();
        }

        /// <summary>
        /// Event handler for ZIP archive "Browse" button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonBrowseZip_Click(object sender, EventArgs e)
        {
            // display an OpenFileDialog to open a ZIP archive
            openFileDialog.FileName = textArchivePath.Text;
            DialogResult result = openFileDialog.ShowDialog(this);
            if (result != DialogResult.OK)
                return;

            // set the ZIP archive path
            textArchivePath.Text = openFileDialog.FileName;
        }

        /// <summary>
        /// Event handler for target folder "Browse" button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonBrowseFolder_Click(object sender, EventArgs e)
        {
            // display a FolderBrowserDialog to browse a target folder
            folderBrowserDialog.SelectedPath = textTargetPath.Text;
            DialogResult result = folderBrowserDialog.ShowDialog();
            if (result != DialogResult.OK)
                return;

            // sets the target folder path
            textTargetPath.Text = folderBrowserDialog.SelectedPath;
        }

        /// <summary>
        /// Event handler for Cancel button.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonCancel_Click(object sender, EventArgs e)
        {
            if (_archive != null)
                _archive.Cancel();
        }

        /// <summary>
        /// Event handler for ZipArchive's ProgressChanged event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void archive_ProgressChanged(object sender, ZipProgressChangedEventArgs e)
        {
            // update the progress bar
            progressBar.Value = e.ProgressPercentage;

            // update the status label
            switch (e.OperationStep)
            {
                case ArchiveOperationStep.FileChecking:
                    statusLabel.Text = "Checking " + e.ArchiveItemPath;
                    break;
                case ArchiveOperationStep.FileProcessing:
                    statusLabel.Text = "Extracting " + e.ArchiveItemPath;
                    break;
            }

#if !ASYNC
            // process any application events to keep the window responsive
            Application.DoEvents();
#endif
        }

        /// <summary>
        /// Event handler for ZipArchive's ProblemDetected event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void archive_ProblemDetected(object sender, ZipProblemDetectedEventArgs e)
        {
            // we only handle 'file exists' problems
            if (e.ProblemType == ArchiveProblemType.FileExists)
            {
                if (checkBoxOverwrite.Checked)
                {
                    // 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
                    FileExistsDialog dialog = new FileExistsDialog();
                    dialog.SetFilePath(e.ExternalItemPath);
                    DialogResult result = dialog.ShowDialog();

                    // set the action selected by the user
                    switch (result)
                    {
                        case DialogResult.Yes:
                            e.Action = ArchiveProblemActions.Overwrite;
                            break;
                        case DialogResult.No:
                            e.Action = ArchiveProblemActions.Skip;
                            break;
                        default:
                            e.Action = ArchiveProblemActions.Cancel;
                            break;
                    }
                }
            }
        }

#if !ASYNC
        /// <summary>
        /// Event handler for Extract button. Does most of the work.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void buttonExtract_Click(object sender, EventArgs e)
        {
            try
            {
                // show an error if path to a ZIP archive is missing
                if (textArchivePath.Text.Length == 0)
                {
                    ShowError("Path to a ZIP archive was not entered.");
                    return;
                }

                // show an error if path to a target folder is missing
                if (textTargetPath.Text.Length == 0)
                {
                    ShowError("Path to a target folder was not entered.");
                    return;
                }

                // disable buttons and boxes
                SetEnabled(false);

                // open a ZIP archive
                _archive = new ZipArchive(textArchivePath.Text, ArchiveOpenMode.Open, ArchiveAccessMode.Read);

                // set up the event handlers
                _archive.ProgressChanged += new EventHandler<ZipProgressChangedEventArgs>(archive_ProgressChanged);
                _archive.ProblemDetected += new EventHandler<ZipProblemDetectedEventArgs>(archive_ProblemDetected);
                _archive.PasswordRequired += new EventHandler<ZipPasswordRequiredEventArgs>(archive_PasswordRequired);

                // call the ExtractAll method to do all the work
                ArchiveOperationResult result = _archive.ExtractAll(textTargetPath.Text);

                // display the results
                statusLabel.Text = "Done";
                string message = string.Format("Extracted {0} files.", result.FilesAffected);
                MessageBox.Show(this, message, base.Text);
            }
            catch (ZipException error)
            {
                // display an error reported by the ZIP component
                ShowError(error.Message);
            }
            catch (FileNotFoundException error)
            {
                // display file-not-found error
                ShowError("File not found: " + error.FileName);
            }
            finally
            {
                // close the ZIP archive when done
                CloseArchive();
            }
        }

#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 void buttonExtract_Click(object sender, EventArgs e)
        {
            bool success = false;
            try
            {
                // show an error if path to a ZIP archive is missing
                if (textArchivePath.Text.Length == 0)
                {
                    ShowError("Path to a ZIP archive was not entered.");
                    return;
                }

                // show an error if path to a target folder is missing
                if (textTargetPath.Text.Length == 0)
                {
                    ShowError("Path to a target folder was not entered.");
                    return;
                }

                // disable buttons and boxes
                SetEnabled(false);

                // open a ZIP archive
                _archive = new ZipArchive(textArchivePath.Text, ArchiveOpenMode.Open, ArchiveAccessMode.Read);

                // set up the event handlers
                _archive.PasswordRequired += new EventHandler<ZipPasswordRequiredEventArgs>(archive_PasswordRequired);
                _archive.ProgressChanged += new EventHandler<ZipProgressChangedEventArgs>(archive_ProgressChanged);
                _archive.ProblemDetected += new EventHandler<ZipProblemDetectedEventArgs>(archive_ProblemDetected);

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

                // background thread was started successfully
                success = true;
            }
            catch (ZipException error)
            {
                // display an error reported by the ZIP component
                ShowError(error.Message);
            }
            catch (FileNotFoundException error)
            {
                // display file-not-found error
                ShowError("File not found: " + error.FileName);
            }
            finally
            {
                // close the ZIP archive if no backgroud thread was started
                if (!success)
                    CloseArchive();
            }
        }

        private void TaskFinished(Task<ArchiveOperationResult> task)
        {
            if (this.InvokeRequired)
            {
                Invoke(new Action<Task<ArchiveOperationResult>>(TaskFinished), task);
                return;
            }

            try
            {
                if (task.IsCanceled)
                    return;

                if (task.IsFaulted)
                {
                    // display errors reported by the ZIP component
                    foreach (Exception error in task.Exception.InnerExceptions)
                    {
                        ShowError(error.Message);
                    }
                    return;
                }

                // retrieve the ExtractAll method's result
                ArchiveOperationResult result = task.Result;

                // display the results
                statusLabel.Text = "Done";
                string message = string.Format("Extracted {0} files.", result.FilesAffected);
                MessageBox.Show(this, message, base.Text);
            }
            finally
            {
                // close the ZIP archive when done
                CloseArchive();
            }
        }
#endif

        /// <summary>
        /// Event handler for ZipArchive's PasswordRequired event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void archive_PasswordRequired(object sender, ZipPasswordRequiredEventArgs e)
        {
            // use PasswordDialog to ask the user for password
            PasswordDialog dialog = new PasswordDialog();
            dialog.SetFilePath(e.ExternalItemPath, e.Reason);
            DialogResult result = dialog.ShowDialog();

            // set the password or cancel the operation
            switch (result)
            {
                case DialogResult.OK:
                    e.Password = dialog.Password;
                    break;
                case DialogResult.Ignore:
                    e.Action = ArchivePasswordActions.SkipFile;
                    break;
                default:
                    e.Action = ArchivePasswordActions.Cancel;
                    break;
            }
        }

        /// <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 void MainForm_DragEnter(object sender, DragEventArgs e)
        {
            if ((e.AllowedEffect & DragDropEffects.Link) != 0)
            {
                // allow any FileNameW data
                string[] paths = e.Data.GetData(DataFormats.FileDrop) as string[];
                if (paths != null && paths.Length == 1)
                    e.Effect = DragDropEffects.Link;
            }
        }

        /// <summary>
        /// Event handler for DragDrop event. Accepts Drag and Dropped files.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_DragDrop(object sender, DragEventArgs e)
        {
            // accept any FileNameW data and set the ZIP archive path accordingly
            string[] paths = e.Data.GetData(DataFormats.FileDrop) as string[];
            if (paths != null && paths.Length == 1)
                textArchivePath.Text = paths[0];
        }

        /// <summary>
        /// Sets enabled/disabled state of form's controls.
        /// </summary>
        /// <param name="enabled"></param>
        private void SetEnabled(bool enabled)
        {
            // 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 = !enabled;

            // shows default cursor when enabled and wait cursor if disabled
            if (enabled)
                base.Cursor = Cursors.Default;
            else
                base.Cursor = Cursors.WaitCursor;
        }

        /// <summary>
        /// Show an error message.
        /// </summary>
        /// <param name="message">Error message.</param>
        private void ShowError(string message)
        {
            MessageBox.Show(this, message, base.Text);
        }

        /// <summary>
        /// Closes the ZIP archive (if open) and makes the form usable again.
        /// </summary>
        private void CloseArchive()
        {
            // close the ZIP archive if open
            if (_archive != null)
            {
                _archive.Close(ArchiveSaveAction.None);
                _archive = null;
            }

            // enable buttons and boxes
            SetEnabled(true);

            // reset the status label and progress bar values
            statusLabel.Text = "Ready";
            progressBar.Value = 0;
        }
    }
}
