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

using System;
using System.Data;
using System.IO;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;

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

namespace Rebex.Samples
{
    public partial class Default : System.Web.UI.Page
    {
        /// <summary>
        /// Retrieves the ZIP archive file uploaded from the web page and extract its content into the target folder.
        /// </summary>
        private void ExtractUploadedFile()
        {
            // construct physical path to the target folder
            string targetFolder = GetTargetFolder();

            // check the uploaded file
            if (FileUploadControl.PostedFile.ContentLength <= 0)
                throw new SampleException("No data uploaded.");

            string archiveFileName = Path.GetFileName(FileUploadControl.PostedFile.FileName);
            string archiveFilePath = SafePathCombine(targetFolder, archiveFileName);

            // create the target folder
            Directory.CreateDirectory(targetFolder);

            // upload archive file first
            FileUploadControl.PostedFile.SaveAs(archiveFilePath);

            // unzip the archive
            ArchiveOperationResult res = ZipArchive.ExtractAll(archiveFilePath, targetFolder, TransferMethod.Copy, ActionOnExistingFiles.OverwriteAll);
            UploadedFiles.Text = res.FilesAffected.ToString();

            // delete the archive file
            File.Delete(archiveFilePath);

            // show results
            ArchiveFileName.Text = archiveFileName;
            ShowFileList();
        }

        /// <summary>
        /// Deletes folder <see cref="TargetFolder"/> and all its content.
        /// </summary>
        /// <returns>Null on success. Error description on failure.</returns>
        private void CleanUpTargetFolder()
        {
            string targetFolder = GetTargetFolder();

            try
            {
                if (Directory.Exists(targetFolder))
                    Directory.Delete(targetFolder, true);
            }
            catch (UnauthorizedAccessException ex)
            {
                throw new SampleException(string.Format("Unable to clean up folder '{0}'. " + ex.Message, targetFolder), ex);
            }
            catch (IOException ex)
            {
                throw new SampleException(string.Format("Unable to clean up folder '{0}'. " + ex.Message, targetFolder), ex);
            }
        }

        #region Event handlers

        protected void PerformUpload_Click(object sender, EventArgs e)
        {
            try
            {
                // optionally clean up the folder first
                if (CleanUpBeforeUpload.Checked)
                    CleanUpTargetFolder();

                // process the upload
                ExtractUploadedFile();

                // on success proceed to the next step (next panel)
                UploadFormPanel.Visible = false;
                UploadedFilesPanel.Visible = true;
                PnlReset.Visible = true;
            }
            catch (SampleException ex)
            {
                // display an error reported by our method
                ErrorMessage.Visible = true;
                ErrorMessage.Text = ex.Message;
            }
            catch (ZipException ex)
            {
                // display an error reported by the ZIP component
                ErrorMessage.Visible = true;
                ErrorMessage.Text = ex.Message;
            }
            catch (Exception ex)
            {
                // display all other errors
                ErrorMessage.Visible = true;
                ErrorMessage.Text = "Error while processing the upload: " + ex.Message.ToString();
            }
        }

        protected void CleanUp_Click(object sender, EventArgs e)
        {
            try
            {
                // perform the cleanup
                CleanUpTargetFolder();

                // on success proceed to the next step (next panel)
                UploadedFilesPanel.Visible = false;
                CleanupCompletedPanel.Visible = true;
            }
            catch (SampleException ex)
            {
                // display an error reported by our method
                ErrorMessage.Visible = true;
                ErrorMessage.Text = ex.Message;
            }
            catch (Exception ex)
            {
                // display all other errors
                ErrorMessage.Visible = true;
                ErrorMessage.Text = "Error while cleaning up the target folder: " + ex.Message.ToString();
            }
        }

        #endregion

        #region Helper methods

        /// <summary>
        /// Constructs the physical path to the folder to which the ZIP archive should be extracted.<br/>
        /// The folder is also used as temporary storage for uploaded archive files.
        /// </summary>
        /// <returns>Target folder path.</returns>
        public string GetTargetFolder()
        {
            string targetSubfolder = TargetFolder.Text;
            if (string.IsNullOrEmpty(targetSubfolder))
                throw new SampleException("Target folder not specified.");

            return SafePathCombine(Path.GetTempPath(), targetSubfolder);
        }

        /// <summary>
        /// Show list of files in the target folder into the scrollable div on the web page.
        /// </summary>
        private void ShowFileList()
        {
            string targetFolder = GetTargetFolder();

            StringBuilder listing = new StringBuilder();
            foreach (string filePath in Directory.GetFiles(targetFolder, "*", SearchOption.AllDirectories))
            {
                listing.AppendFormat("{0}<br/>", Server.HtmlEncode(filePath));
            }

            DivFilesList.InnerHtml = listing.ToString();
        }

        /// <summary>
        /// Safely combines two path strings, making sure that the combined path is not outside the first path.
        /// </summary>
        /// <param name="path1">The first path.</param>
        /// <param name="path2">The second path.</param>
        /// <returns>Combined path.</returns>
        private static string SafePathCombine(string path1, string path2)
        {
            string path3 = Path.Combine(path1, path2);
            string fullPath3 = Path.GetFullPath(path3).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
            string fullPath1 = Path.GetFullPath(path1).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;

            if (!fullPath3.StartsWith(fullPath1))
                throw new SampleException("The combined path is outside the first path.");

            return path3;
        }

        #endregion

    }
}
