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

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.IO;

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

namespace Rebex.Samples
{
    /// <summary>
    /// Provides various helper methods.
    /// </summary>
    public static class Utils
    {
        /// <summary>
        /// Path of the ZIP archive root.
        /// </summary>
        public static readonly string RootPath = Path.DirectorySeparatorChar.ToString();

        /// <summary>
        /// Rebuilds the specified TreeView according to the specified ZIP archive content, persisting currently selected and expanded nodes.
        /// </summary>
        /// <param name="treeView">TreeView control to rebuild.</param>
        /// <param name="zipArchive">A ZIP archive instance.</param>
        public static void FillTreeView(TreeView treeView, ZipArchive zipArchive)
        {
            // save selected node
            ZipItem selectedNodeItem = null;
            if (treeView.SelectedNode != null)
                selectedNodeItem = (ZipItem)treeView.SelectedNode.Tag;

            // save expanded nodes into a dictionary
            Dictionary<ZipItem, object> expandedNodeItems = new Dictionary<ZipItem, object>();
            foreach (TreeNode subNode in treeView.Nodes)
            {
                // call recursive method to get all expanded nodes
                GetExpandedNodes(subNode, expandedNodeItems);
            }

            // clear the TreeView before rebuilding
            treeView.Nodes.Clear();

            // create the root node with the name of the archive file
            TreeNode rootNode = new TreeNode(string.Format("({0})", Path.GetFileName(zipArchive.FilePath)));
            rootNode.Tag = zipArchive.GetItem(RootPath);

            // recursively add all subdirectories and get the node to select
            TreeNode selectedNode = AddSubdirectories(rootNode, zipArchive, expandedNodeItems, selectedNodeItem);

            // add root node to the TreeView
            treeView.Nodes.Add(rootNode);

            // set selected node
            treeView.SelectedNode = selectedNode;
        }

        /// <summary>
        /// Saves all expanded nodes into a dictionary.
        /// </summary>
        /// <param name="node">Currently processed directory node.</param>
        /// <param name="expandedNodes">Dictionary of expanded nodes to fill.</param>
        private static void GetExpandedNodes(TreeNode node, Dictionary<ZipItem, object> expandedNodes)
        {
            // if the node is expanded, add it to the dictionary
            if (node.IsExpanded)
                expandedNodes.Add((ZipItem)node.Tag, null);

            // do the same for all expanded subnodes
            foreach (TreeNode subNode in node.Nodes)
            {
                GetExpandedNodes(subNode, expandedNodes);
            }
        }

        /// <summary>
        /// Recursively adds subdirectories to the specified directory node, expanding them if necessary.
        /// </summary>
        /// <param name="node">Directory node to add subdirectories to.</param>
        /// <param name="zipArchive">A ZIP archive instance.</param>
        /// <param name="expandedNodeItems">Dictionary of expanded nodes.</param>
        /// <param name="selectedNodeItem">Selected item.</param>
        private static TreeNode AddSubdirectories(TreeNode node, ZipArchive zipArchive, Dictionary<ZipItem, object> expandedNodeItems, ZipItem selectedNodeItem)
        {
            TreeNode selectedNode = null;

            // get subdirectories of the current directory node
            ZipItem item = (ZipItem)node.Tag;
            ZipItemCollection directories = zipArchive.GetItems(Path.Combine(item.Path, "*"), TraversalMode.NonRecursive, ArchiveItemTypes.Directories);
            directories.Sort();

            // add subdirectories first!
            foreach (ZipItem directory in directories)
            {
                // add new tree node
                TreeNode treeNode = new TreeNode(directory.Name);
                treeNode.Tag = directory;
                node.Nodes.Add(treeNode);

                // recursively add subdirectories
                TreeNode selectedSubNode = AddSubdirectories(treeNode, zipArchive, expandedNodeItems, selectedNodeItem);
                if (selectedSubNode != null)
                    selectedNode = selectedSubNode;
            }

            // check whether to expand
            if (expandedNodeItems.ContainsKey(item))
                node.Expand();

            // check whether to select
            if (selectedNodeItem == item)
                return node;
            else
                return selectedNode;
        }

        /// <summary>
        /// Fill the specified ListView with files of the selected directory node.
        /// </summary>
        /// <param name="ZipArchive">An archive instance.</param>
        /// <param name="listView">ListView control to fill.</param>
        /// <param name="selectedNode">Currently selected directory node.</param>
        public static void FillListView(ZipArchive zipArchive, ListView listView, TreeNode selectedNode)
        {
            // clear selection to raise registered events
            listView.SelectedItems.Clear();

            listView.Items.Clear();

            if (selectedNode == null)
                return;

            // get files of the selected directory node
            ZipItem selectedItem = (ZipItem)selectedNode.Tag;
            ZipItemCollection items = zipArchive.GetItems(Path.Combine(selectedItem.Path, "*"), TraversalMode.MatchFilesShallow);

            List<ListViewItem> listViewItems = new List<ListViewItem>();
            foreach (ZipItem item in items)
            {
                // add a new ListViewItem for every file
                ListViewItem listViewItem = new ListViewItem(item.Name);

                AddSubItem(listViewItem, item.Length);
                AddSubItem(listViewItem, item.CompressedLength);

                if (item.Length > 0)
                {
                    // compute compression ratio 
                    int ratio = (int)(100 * item.CompressedLength / item.Length);
                    AddSubItem(listViewItem, ratio, string.Format("{0}%", ratio));
                }
                else
                {
                    AddSubItem(listViewItem, int.MaxValue, "N/A");
                }

                AddSubItem(listViewItem, item.LastWriteTime);

                listViewItem.Tag = item;

                if (item.IsEncrypted)
                    listViewItem.ImageIndex = 0;

                listViewItems.Add(listViewItem);
            }
            listView.Items.AddRange(listViewItems.ToArray());
        }

        /// <summary>
        /// Adds a value to the specified ListViewItem.
        /// </summary>
        /// <param name="item">ListViewItem to which to add the value.</param>
        /// <param name="value">A value to add.</param>
        private static void AddSubItem(ListViewItem item, object value)
        {
            AddSubItem(item, value, value.ToString());
        }

        /// <summary>
        /// Adds a value to the specified ListViewItem.
        /// </summary>
        /// <param name="item">ListViewItem to which to add the value.</param>
        /// <param name="tag">A value to add.</param>
        /// <param name="text">Description.</param>
        private static void AddSubItem(ListViewItem item, object tag, string text)
        {
            ListViewItem.ListViewSubItem subItem = new ListViewItem.ListViewSubItem();
            subItem.Text = text;
            subItem.Tag = tag;
            item.SubItems.Add(subItem);
        }

    }
}
