using EmreIstikam001.component.TehranOffice.ToosForAll;
using Rhino;
using Rhino.Display;
using Rhino.DocObjects;
using Rhino.FileIO;
using Rhino.Geometry;
using Rhino.Runtime.InProcess;
using Rhino.UI;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace EmreIstikam001.component.TehranOffice.Components.Other.CustomLibrary
{
    public partial class CustomLibraryFrm : Form
    {
        public event EventHandler<PictureBoxEventArgs> PictureBoxClicked;
        public event EventHandler<PictureBoxEventArgs> PictureBoxDoubleClicked;

        private PictureBox selectedPictureBox = null;
        public DateTime selectTime = DateTime.Now;
        private List<PictureBox> pictureBoxes = new List<PictureBox>();
        private List<FileInfo> cachedFiles = new List<FileInfo>();
        public string SelectedFilePath { get; private set; }
        public string FileName { get; private set; }
        public string UserPath { get; private set; }

        private ToolTip toolTip;
        private string initialPath;
        private Timer searchTimer;

        public CustomLibraryFrm()
        {
            InitializeComponent();

            initialPath = @"C:\PARDUS_CEPHE_YAZILIMI\Pardus_CY\01_BLOK";

            InitializeGrid();
            toolTip = new ToolTip();
            SetPlaceholder();

            // Subscribe to TreeView events
            treeView1.BeforeExpand += TreeView1_BeforeExpand;
            treeView1.AfterSelect += TreeView1_AfterSelect;

            // Initialize ImageList for the TreeView
            InitializeImageList();

            // Populate the TreeView
            PopulateTreeView(initialPath);

            // Initialize the search timer
            searchTimer = new System.Windows.Forms.Timer();
            searchTimer.Interval = 500; // Set the debounce delay to 500 milliseconds
            searchTimer.Tick += SearchTimer_Tick;

        }

        /// <summary>
        /// Initializes the ImageList for the TreeView control.
        /// </summary>
        private void InitializeImageList()
        {
            ImageList imageList = new ImageList();

            // Set the desired image size (e.g., 64x64 pixels)
            imageList.ImageSize = new Size(32, 32);

            // Add images to the image list
            imageList.Images.Add("folder", Properties.Resources.kutuphane);  // folder icon
            imageList.Images.Add("file", Properties.Resources.kutuphane);    // file icon
            imageList.Images.Add("file3dm", Properties.Resources.kutuphane); // .3dm file icon

            // Assign the ImageList to the TreeView
            treeView1.ImageList = imageList;

            //// Adjust the ItemHeight to accommodate larger images
            //treeView1.ItemHeight = 70;
        }


        /// <summary>
        /// Sets the placeholder text in the search TextBox.
        /// </summary>
        private void SetPlaceholder()
        {
            txtSearch.Text = "search...";
            txtSearch.ForeColor = Color.Gray;
        }

        /// <summary>
        /// Event handler for when the user enters the search TextBox.
        /// </summary>
        private void txtSearch_Enter(object sender, EventArgs e)
        {
            if (txtSearch.Text == "search...")
            {
                txtSearch.Text = "";
                txtSearch.ForeColor = Color.Black;
            }
        }

        /// <summary>
        /// Event handler for when the user leaves the search TextBox.
        /// </summary>
        private void txtSearch_Leave(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtSearch.Text))
            {
                SetPlaceholder();
            }
        }

        /// <summary>
        /// Initializes the grid layout for displaying thumbnails.
        /// </summary>
        private void InitializeGrid()
        {
            tableLayoutPanel01.RowCount = 3;
            tableLayoutPanel01.ColumnCount = 3;
            tableLayoutPanel01.AutoScroll = true;
        }

        /// <summary>
        /// Populates the TreeView with directories and files from the given root path.
        /// Implements lazy loading for better performance.
        /// </summary>
        private void PopulateTreeView(string rootPath)
        {
            if (!Directory.Exists(rootPath))
            {
                string systemDriveRoot = Path.GetPathRoot(Environment.SystemDirectory);
                string newFolderName = "PARDUS_CEPHE_YAZILIMI";
                string newFolderPath = Path.Combine(systemDriveRoot, newFolderName);

                if (!Directory.Exists(newFolderPath))
                {
                    Directory.CreateDirectory(newFolderPath);
                }

                rootPath = newFolderPath;
            }

            DirectoryInfo rootDirectory = new DirectoryInfo(rootPath);

            TreeNode rootNode = new TreeNode(rootDirectory.Name)
            {
                Tag = rootDirectory,
                ImageKey = "folder"
            };

            // Add a dummy node for lazy loading
            rootNode.Nodes.Add(new TreeNode("Loading..."));
            treeView1.Nodes.Add(rootNode);
            rootNode.Expand();
            textBox1.Text = rootPath;
        }

        /// <summary>
        /// Event handler for when a TreeView node is about to expand.
        /// Implements lazy loading by loading subdirectories and files at this point.
        /// </summary>
        private void TreeView1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            TreeNode node = e.Node;

            // Check if node contains a dummy "Loading..." node
            if (node.Nodes.Count == 1 && node.Nodes[0].Text == "Loading...")
            {
                node.Nodes.Clear();

                if (node.Tag is DirectoryInfo dir)
                {
                    GetDirectories(dir, node);
                }
            }
        }

        /// <summary>
        /// Recursively adds subdirectories and files to the TreeView node.
        /// </summary>
        private void GetDirectories(DirectoryInfo dir, TreeNode nodeToAddTo)
        {
            try
            {
                // Add subdirectories
                DirectoryInfo[] subDirs = dir.GetDirectories();

                foreach (DirectoryInfo subDir in subDirs)
                {
                    TreeNode subNode = new TreeNode(subDir.Name)
                    {
                        Tag = subDir,
                        ImageKey = "folder"
                    };
                    // Add a dummy node for lazy loading
                    subNode.Nodes.Add(new TreeNode("Loading..."));
                    nodeToAddTo.Nodes.Add(subNode);
                }

                // Add files
                FileInfo[] files = dir.GetFiles();
                foreach (FileInfo file in files)
                {
                    TreeNode fileNode = new TreeNode(file.Name)
                    {
                        Tag = file,
                        ImageKey = file.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase) ? "file3dm" : "file"
                    };
                    nodeToAddTo.Nodes.Add(fileNode);
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine($"Access denied to {dir.FullName}: {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred accessing {dir.FullName}: {ex.Message}");
            }
        }

        /// <summary>
        /// Event handler for when a node in the TreeView is selected.
        /// </summary>
        private void TreeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            var selectedNode = e.Node;
            FileName = selectedNode.Text;

            if (selectedNode.Tag is DirectoryInfo selectedDirectory)
            {
                pictureBoxes.Clear();
                PopulateTableWithThumbnails(selectedDirectory);
            }
            else if (selectedNode.Tag is FileInfo selectedFile)
            {
                SelectedFilePath = selectedFile.FullName;

                // Clear existing controls
                pictureBoxes.Clear();
                tableLayoutPanel01.Controls.Clear();
                tableLayoutPanel01.RowStyles.Clear();
                tableLayoutPanel01.ColumnStyles.Clear();

                tableLayoutPanel01.RowCount = 0;
                tableLayoutPanel01.ColumnCount = 0;

                tableLayoutPanel01.RowCount = 1;
                tableLayoutPanel01.ColumnCount = 1;

                tableLayoutPanel01.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
                tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));

                // Create the PictureBox
                PictureBox pictureBox = new PictureBox
                {
                    Width = 1300,
                    Height = 1500,
                    SizeMode = PictureBoxSizeMode.Zoom,
                    Anchor = AnchorStyles.None,
                    Margin = new Padding(0),
                    Tag = selectedFile.FullName,
                    Cursor = Cursors.Hand,


                };

                // Load the thumbnail
                try
                {
                    Bitmap rhinoThumbnail = WindowsThumbnailProvider.GetThumbnail(selectedFile.FullName, 400, 600, ThumbnailOptions.InCacheOnly);
                    if (rhinoThumbnail != null)
                    {
                        pictureBox.Image = rhinoThumbnail;
                    }
                    else
                    {
                        // If no thumbnail is available, you can display a placeholder image
                        // pictureBox.Image = Properties.Resources.PlaceholderImage;
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error loading thumbnail for {selectedFile.FullName}: {ex.Message}");
                    // Optionally display an error image
                    // pictureBox.Image = Properties.Resources.ErrorImage;
                }

                // Add the PictureBox to the tableLayoutPanel in the center cell
                tableLayoutPanel01.Controls.Add(pictureBox, 0, 0);

                pictureBoxes.Add(pictureBox);
            }

            // Remove existing event handlers to prevent multiple subscriptions
            foreach (Control control in tableLayoutPanel01.Controls)
            {
                if (control is PictureBox pictureBox)
                {
                    pictureBox.Click += PictureBox_Click;

                    pictureBox.Paint += PictureBox_Paint;
                }
            }
        }

        /// <summary>
        /// Event handler for the "Browse" button click to select a custom directory.
        /// </summary>
        private void button1_Click(object sender, EventArgs e)
        {
            if (fbd1.ShowDialog() == DialogResult.OK)
            {
                UserPath = fbd1.SelectedPath;
                treeView1.Nodes.Clear();
                PopulateTreeView(UserPath);
            }
        }

        /// <summary>
        /// Populates the TableLayoutPanel with thumbnails of the files in the given directory.
        /// </summary>
        public void PopulateTableWithThumbnails(DirectoryInfo directory)
        {
            // Clear existing data
            pictureBoxes.Clear();
            tableLayoutPanel01.Controls.Clear();
            tableLayoutPanel01.RowStyles.Clear();
            tableLayoutPanel01.ColumnStyles.Clear();

            tableLayoutPanel01.RowCount = 0;
            tableLayoutPanel01.ColumnCount = 0;

            // Begin layout suspension
            tableLayoutPanel01.SuspendLayout();

            int fixedColumnCount = 3;
            int pictureBoxSize = 250;

            // Get files with .3dm extension
            FileInfo[] files = directory.GetFiles()
                .Where(f => f.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase))
                .ToArray();

            int rowCount = (int)Math.Ceiling((double)files.Length / fixedColumnCount);

            tableLayoutPanel01.RowCount = rowCount;
            tableLayoutPanel01.ColumnCount = fixedColumnCount;

            // Set up styles
            for (int i = 0; i < fixedColumnCount; i++)
            {
                tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / fixedColumnCount));
            }

            for (int i = 0; i < rowCount; i++)
            {
                tableLayoutPanel01.RowStyles.Add(new RowStyle(SizeType.Absolute, pictureBoxSize));
            }

            tableLayoutPanel01.AutoSize = false;
            tableLayoutPanel01.AutoScroll = true;

            // Populate controls
            for (int i = 0; i < files.Length; i++)
            {
                FileInfo file = files[i];
                PictureBox pictureBox = CreatePictureBoxForFile(file, pictureBoxSize);

                int row = i / fixedColumnCount;
                int col = i % fixedColumnCount;
                tableLayoutPanel01.Controls.Add(pictureBox, col, row);
                pictureBoxes.Add(pictureBox);
            }

            // End layout suspension and refresh
            tableLayoutPanel01.ResumeLayout();
            tableLayoutPanel01.Refresh();
        }

        /// <summary>
        /// Creates a PictureBox for the given file.
        /// </summary>
        private PictureBox CreatePictureBoxForFile(FileInfo file, int pictureBoxSize)
        {
            PictureBox pictureBox = new PictureBox
            {
                Width = pictureBoxSize,
                Height = pictureBoxSize,
                BorderStyle = BorderStyle.None,
                SizeMode = PictureBoxSizeMode.StretchImage,
                Cursor = Cursors.Hand,
                BackColor = Color.LightGray,
                Tag = file.FullName
            };


            toolTip.SetToolTip(pictureBox, file.Name);

            // Load thumbnail image
            try
            {
                Bitmap rhinoThumbnail = WindowsThumbnailProvider.GetThumbnail(file.FullName, 200, 200, ThumbnailOptions.InCacheOnly);
                if (rhinoThumbnail != null)
                {
                    pictureBox.Image = rhinoThumbnail;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error loading thumbnail for {file.FullName}: {ex.Message}");
            }

            return pictureBox;
        }

        /// <summary>
        /// Click event handler for PictureBox.
        /// </summary>
        private void PictureBox_Click(object sender, EventArgs e)
        {
            if (sender is PictureBox clickedPictureBox)
            {
                if ((DateTime.Now.Ticks - selectTime.Ticks) / 10000 < 300 && selectedPictureBox == clickedPictureBox)
                {
                    //PictureBox_DoubleClick(sender, e);
                    // Raise the custom event
                    SelectedFilePath = clickedPictureBox.Tag.ToString();
                    FileName = Path.GetFileNameWithoutExtension(clickedPictureBox.Tag.ToString());
                    OnPictureBoxClicked(clickedPictureBox);
                }
                selectTime = DateTime.Now;
                if (selectedPictureBox != null)
                {
                    selectedPictureBox.BorderStyle = BorderStyle.None;
                    //selectedPictureBox.Invalidate();
                }

                selectedPictureBox = clickedPictureBox;

                selectedPictureBox.BorderStyle = BorderStyle.FixedSingle;
                //selectedPictureBox.Invalidate();
            }
        }

        /// <summary>
        /// Double-click event handler for PictureBox.
        /// </summary>
        private void PictureBox_DoubleClick(object sender, EventArgs e)
        {
            if (sender is PictureBox doubleClickedPictureBox)
            {
                SelectedFilePath = doubleClickedPictureBox.Tag.ToString();
                // Raise the custom event
                //OnPictureBoxDoubleClicked(doubleClickedPictureBox);
            }
            
        }

        protected virtual void OnPictureBoxDoubleClicked(PictureBox clickedPictureBox)
        {
            PictureBoxDoubleClicked?.Invoke(this, new PictureBoxEventArgs(clickedPictureBox));
        }


        protected virtual void OnPictureBoxClicked(PictureBox clickedPictureBox)
        {
            PictureBoxClicked?.Invoke(this, new PictureBoxEventArgs(clickedPictureBox));
        }




        /// <summary>
        /// Paint event handler for PictureBox to draw a border around the selected PictureBox.
        /// </summary>
        private void PictureBox_Paint(object sender, PaintEventArgs e)
        {
            if (sender is PictureBox picBox && picBox == selectedPictureBox)
            {
                var borderWidth = 10;
                var borderColor = Color.CadetBlue;

                Rectangle rect = new Rectangle(0, 0, picBox.Width - 3, picBox.Height - 3);

                using (Pen pen = new Pen(borderColor, borderWidth))
                {
                    e.Graphics.DrawRectangle(pen, rect);
                }
            }
        }

        private async void SearchTimer_Tick(object sender, EventArgs e)
        {
            // Stop the timer
            searchTimer.Stop();

            // Perform the search asynchronously
            await PerformSearchAsync();
        }

        private async Task PerformSearchAsync()
        {
            if (InvokeRequired)
            {
                Invoke(new Action(async () => await PerformSearchAsync()));
                return;
            }

            // Clear existing controls and display "Searching..." message
            tableLayoutPanel01.Controls.Clear();
            tableLayoutPanel01.RowStyles.Clear();
            tableLayoutPanel01.ColumnStyles.Clear();
            tableLayoutPanel01.RowCount = 1;
            tableLayoutPanel01.ColumnCount = 1;
            tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
            tableLayoutPanel01.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

            Label searchingLabel = new Label()
            {
                Text = "Searching...",
                AutoSize = true,
                Dock = DockStyle.Fill,
                TextAlign = ContentAlignment.MiddleCenter
            };
            tableLayoutPanel01.Controls.Add(searchingLabel, 0, 0);

            string searchText = txtSearch.Text.ToLower();
            bool searchSubdirectories = chbSearchAll.Checked;
            List<FileInfo> matchingFiles = null;

            if (searchSubdirectories)
            {
                if (cachedFiles == null || cachedFiles.Count == 0)
                {
                    await CacheAllFilesAsync(); // Build the cache if not already done
                }

                // Perform search on cached files asynchronously
                matchingFiles = await Task.Run(() =>
                    cachedFiles.Where(file => file.Name.ToLower().Contains(searchText)).ToList()
                );
            }
            else
            {
                DirectoryInfo currentDirectory = GetCurrentSelectedDirectory();

                if (currentDirectory == null)
                {
                    // Remove "Searching..." label
                    tableLayoutPanel01.Controls.Clear();
                    return;
                }

                matchingFiles = await Task.Run(() =>
                    SearchInCurrentDirectory(currentDirectory, searchText)
                );
            }

            // Update the UI with the matching files
            UpdatePictureBoxes(matchingFiles);
        }

        private DirectoryInfo GetCurrentSelectedDirectory()
        {
            if (treeView1.SelectedNode?.Tag is DirectoryInfo dir)
            {
                return dir;
            }
            else if (treeView1.SelectedNode?.Tag is FileInfo file)
            {
                return file.Directory;
            }
            else
            {
                return null;
            }
        }



        private async void chbSearchAll_CheckedChanged(object sender, EventArgs e)
        {
            if (chbSearchAll.Checked)
            {
                // Rebuild the cache asynchronously
                await CacheAllFilesAsync();
            }
            else
            {
                // If the checkbox is unchecked, clear the cache to free memory
                cachedFiles.Clear();
            }
        }


        private async Task CacheAllFilesAsync()
        {
            DirectoryInfo rootDirectory = new DirectoryInfo(initialPath);

            // Begin caching files
            cachedFiles = await Task.Run(() =>
            {
                List<FileInfo> allFiles = new List<FileInfo>();
                GetAllFilesRecursive(rootDirectory, allFiles);
                return allFiles;
            });
        }

        // Recursive method to get all files
        private void GetAllFilesRecursive(DirectoryInfo directory, List<FileInfo> allFiles)
        {
            try
            {
                allFiles.AddRange(directory.GetFiles()
                    .Where(file => file.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase)));

                foreach (DirectoryInfo subDir in directory.GetDirectories())
                {
                    GetAllFilesRecursive(subDir, allFiles);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error accessing {directory.FullName}: {ex.Message}");
            }
        }

        private void ResetPictureBoxesToCurrentDirectory()
        {
            if (treeView1.SelectedNode?.Tag is DirectoryInfo selectedDirectory)
            {
                pictureBoxes.Clear();
                PopulateTableWithThumbnails(selectedDirectory);
            }
            else if (treeView1.SelectedNode?.Tag is FileInfo selectedFile)
            {
                pictureBoxes.Clear();
                PopulateTableWithThumbnails(selectedFile.Directory);
            }
            else
            {
                // Handle case when nothing is selected
                pictureBoxes.Clear();
                tableLayoutPanel01.Controls.Clear();
            }
        }

        /// <summary>
        /// Event handler for text changes in the search TextBox.
        /// </summary>
        private void txtSearch_TextChanged(object sender, EventArgs e)
        {
            string searchText = txtSearch.Text.ToLower();

            if (searchText == "search..." || string.IsNullOrWhiteSpace(searchText))
            {
                // Reset the displayed PictureBoxes
                ResetPictureBoxesToCurrentDirectory();
                return;
            }

            // Restart the timer for debouncing
            searchTimer.Stop();
            searchTimer.Start();
        }

        /// <summary>
        /// Searches for files containing the search text in the current directory.
        /// </summary>
        // Search only in the current directory
        private List<FileInfo> SearchInCurrentDirectory(DirectoryInfo directory, string searchText)
        {
            try
            {
                var matchingFiles = directory.GetFiles()
                    .Where(file => file.Name.ToLower().Contains(searchText) &&
                                   file.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase))
                    .ToList();
                return matchingFiles;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred during search: {ex.Message}");
                return new List<FileInfo>();
            }
        }

        /// <summary>
        /// Searches for files containing the search text recursively in all subdirectories.
        /// </summary>
        // Search recursively through all subdirectories
        private List<FileInfo> SearchInAllDirectories(DirectoryInfo directory, string searchText)
        {
            List<FileInfo> matchingFiles = new List<FileInfo>();
            GetMatchingFilesRecursive(directory, searchText, matchingFiles);

            //UpdatePictureBoxes(matchingFiles);
            return matchingFiles;
        }


        /// <summary>
        /// Recursively gets matching files containing the search text.
        /// </summary>
        // Recursive method to get all matching files
        private void GetMatchingFilesRecursive(DirectoryInfo directory, string searchText, List<FileInfo> matchingFiles)
        {
            try
            {
                matchingFiles.AddRange(directory.GetFiles()
                    .Where(file => file.Name.ToLower().Contains(searchText) &&
                                   file.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase)));

                foreach (DirectoryInfo subDir in directory.GetDirectories())
                {
                    GetMatchingFilesRecursive(subDir, searchText, matchingFiles);
                }
            }
            catch (UnauthorizedAccessException ex)
            {
                Console.WriteLine($"Access denied to directory: {directory.FullName}, {ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred accessing {directory.FullName}: {ex.Message}");
            }
        }

        /// <summary>
        /// Updates the PictureBox controls with the matching files.
        /// </summary>
        // Updates the PictureBox controls with the matching files
        private void UpdatePictureBoxes(List<FileInfo> matchingFiles)
        {
            if (InvokeRequired)
            {
                Invoke(new Action(() => UpdatePictureBoxes(matchingFiles)));
                return;
            }

            // Clear existing controls and styles
            pictureBoxes.Clear();
            tableLayoutPanel01.Controls.Clear();
            tableLayoutPanel01.RowStyles.Clear();
            tableLayoutPanel01.ColumnStyles.Clear();
            tableLayoutPanel01.RowCount = 0;
            tableLayoutPanel01.ColumnCount = 0;

            // Begin layout suspension
            tableLayoutPanel01.SuspendLayout();

            if (matchingFiles.Count == 0)
            {
                // Display "No files found" message
                tableLayoutPanel01.RowCount = 1;
                tableLayoutPanel01.ColumnCount = 1;
                tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
                tableLayoutPanel01.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

                Label noResultsLabel = new Label()
                {
                    Text = "No files found.",
                    AutoSize = true,
                    Dock = DockStyle.Fill,
                    TextAlign = ContentAlignment.MiddleCenter
                };
                tableLayoutPanel01.Controls.Add(noResultsLabel, 0, 0);
            }
            else
            {
                int fixedColumnCount = 3;
                int pictureBoxSize = 250;

                int rowCount = (int)Math.Ceiling((double)matchingFiles.Count / fixedColumnCount);

                tableLayoutPanel01.RowCount = rowCount;
                tableLayoutPanel01.ColumnCount = fixedColumnCount;

                // Set up styles
                for (int i = 0; i < fixedColumnCount; i++)
                {
                    tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100f / fixedColumnCount));
                }

                for (int i = 0; i < rowCount; i++)
                {
                    tableLayoutPanel01.RowStyles.Add(new RowStyle(SizeType.Absolute, pictureBoxSize));
                }

                tableLayoutPanel01.AutoSize = false;
                tableLayoutPanel01.AutoScroll = true;

                // Populate controls
                for (int i = 0; i < matchingFiles.Count; i++)
                {
                    FileInfo file = matchingFiles[i];
                    PictureBox pictureBox = CreatePictureBoxForFile(file, pictureBoxSize);

                    int row = i / fixedColumnCount;
                    int col = i % fixedColumnCount;
                    tableLayoutPanel01.Controls.Add(pictureBox, col, row);
                    pictureBoxes.Add(pictureBox);
                }
            }

            // End layout suspension and refresh
            tableLayoutPanel01.ResumeLayout();
            tableLayoutPanel01.Refresh();
        }

        /// <summary>
        /// Custom event arguments for PictureBox events.
        /// </summary>
        public class PictureBoxEventArgs : EventArgs
        {
            public PictureBox ClickedPictureBox { get; set; }

            public PictureBoxEventArgs(PictureBox pictureBox)
            {
                ClickedPictureBox = pictureBox;
            }
        }
    }
}