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;
using Font = System.Drawing.Font;
using Point = System.Drawing.Point;

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

        private volatile bool isCacheReady = false;
        private PictureBox selectedPictureBox;
        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; } = @"C:\PARDUS_CEPHE_YAZILIMI\Pardus_CY\01_BLOK\";
        public string FileName { get; private set; }
        public string UserPath { get; private set; }

        int ColumnCount = 4;
        static int pictureBoxHeightSize = 150;
        int pictureBoxWidthSize = 150;
        static int labelHeight = 45; // Adjust the height based on your font size and padding needs
        static int fixedSpacing = 10;  // Fixed spacing between columns and rows
        int totalItemHeight = pictureBoxHeightSize + labelHeight + fixedSpacing; // fixedSpacing is to maintain a consistent spacing between rows


        private ToolTip toolTip;
        private string initialPath;
        private Timer searchTimer;
        private int currentSearchToken = 0;
        public CustomLibraryFrm2()
        {
            InitializeComponent();
            this.Resize += TableLayoutPanel01_Resize;
            tableLayoutPanel01.Resize += TableLayoutPanel01_Resize;
            initialPath = @"C:\PARDUS_CEPHE_YAZILIMI\Pardus_CY\01_BLOK";

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

            lblSearching.Visible = false;
            pictureBoxSearch.Visible = false;

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

            // Initialize ImageList for the TreeView
            InitializeImageList();
            SetPlaceholderLogin();
            // Populate the TreeView
            PopulateTreeView(initialPath);

            // Initialize the search timer (set to 2 seconds)
            searchTimer = new System.Windows.Forms.Timer();
            searchTimer.Interval = 2000; // Set the debounce delay to 2000 milliseconds
            searchTimer.Tick += SearchTimer_Tick;
            // Start building the cache in the background
            Task.Run(() => CacheAllFiles());

        }
        // Resize event handler
        private void TableLayoutPanel01_Resize(object sender, EventArgs e)
        {
            AdjustColumnCount();
            PopulateTableWithThumbnails(GetCurrentSelectedDirectory());
        }

        private void AdjustColumnCount()
        {
            // Calculate how many columns can fit in the current width of the TableLayoutPanel
            if (tableLayoutPanel01.Width > 0)
            {
                int availableWidth = tableLayoutPanel01.Width - tableLayoutPanel01.Padding.Horizontal;
                int pictureBoxWithSpacing = pictureBoxWidthSize + fixedSpacing;
                int newColumnCount = Math.Max(1, availableWidth / pictureBoxWithSpacing);
                ColumnCount = newColumnCount;

                // Update the TableLayoutPanel and repopulate
                UpdateTableLayout(newColumnCount);
            }
        }

        private void UpdateTableLayout(int newColumnCount)
        {
            tableLayoutPanel01.Controls.Clear();
            tableLayoutPanel01.RowStyles.Clear();
            tableLayoutPanel01.ColumnStyles.Clear();

            if (pictureBoxes.Count == 0) return;

            int rowCount = (int)Math.Ceiling((double)pictureBoxes.Count / newColumnCount);

            tableLayoutPanel01.RowCount = rowCount;
            tableLayoutPanel01.ColumnCount = newColumnCount;

            for (int i = 0; i < newColumnCount; i++)
            {
                tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, pictureBoxWidthSize));
            }

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

            // Populate controls with spacing
            //for (int i = 0; i < pictureBoxes.Count; i++)
            //{
            //    int row = i / newColumnCount;
            //    int col = i % newColumnCount;
            //    tableLayoutPanel01.Controls.Add(pictureBoxes[i], col, row);
            //}


            if (treeView1.SelectedNode?.Tag is DirectoryInfo selectedDirectory)
            {
                FileInfo[] files = selectedDirectory.GetFiles()
                    .Where(f => f.Extension.Equals(".3dm", StringComparison.OrdinalIgnoreCase))
                    .ToArray();


                // Populate controls
                for (int i = 0; i < files.Length; i++)
                {
                    FileInfo file = files[i];
                    Panel panel = CreatePictureBoxWithLabel(file, pictureBoxWidthSize, pictureBoxHeightSize);

                    int row = i / newColumnCount;
                    int col = i % newColumnCount;
                    tableLayoutPanel01.Controls.Add(panel, col, row);
                    pictureBoxes.Add(panel.Controls.OfType<PictureBox>().FirstOrDefault());
                }
            }
            
        }

        private void CacheAllFiles()
        {
            DirectoryInfo rootDirectory = new DirectoryInfo(initialPath);

            // Begin caching files
            List<FileInfo> allFiles = new List<FileInfo>();
            GetAllFilesRecursive(rootDirectory, allFiles);

            cachedFiles = allFiles;

            // Set the cache ready flag
            isCacheReady = true;
        }


        /// <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(25, 25);

            // Add images to the image list
            imageList.Images.Add("folder", Properties.Resources.noun_folder);  // folder icon
            imageList.Images.Add("file", Properties.Resources.kutuphane);    // file icon
            imageList.Images.Add("file3dm", Properties.Resources.PardusLogo1); // .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;
        }
        private void SetPlaceholderLogin()
        {
            txtUser.Text = "Staff ID...";
            txtPass.Text = "Password...";
            txtUser.ForeColor = Color.FromArgb(170, 170, 170);
            txtPass.ForeColor = Color.FromArgb(170, 170, 170);
        }

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

            // Set the padding of the TableLayoutPanel
            tableLayoutPanel01.Padding = new Padding(10); // Adjust the padding value as needed
            tableLayoutPanel01.BackColor = Color.White; // Ensure the background is white
        }

        /// <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, 800));
                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)
        {
            if (directory == null) return; // Ensure a directory is selected
            // 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();
            // Set column count based on available width
            AdjustColumnCount();

            // 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 / ColumnCount);
            tableLayoutPanel01.RowCount = rowCount;
            tableLayoutPanel01.ColumnCount = ColumnCount;

            // Set up styles
            for (int i = 0; i < ColumnCount; i++)
            {
                tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, pictureBoxWidthSize + fixedSpacing));
            }


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

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

            // Populate controls
            for (int i = 0; i < files.Length; i++)
            {
                FileInfo file = files[i];
                Panel panel = CreatePictureBoxWithLabel(file, pictureBoxWidthSize, pictureBoxHeightSize);

                int row = i / ColumnCount;
                int col = i % ColumnCount;
                tableLayoutPanel01.Controls.Add(panel, col, row);
                pictureBoxes.Add(panel.Controls.OfType<PictureBox>().FirstOrDefault());
            }

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

        /// <summary>
        /// Creates a PictureBox for the given file.
        /// </summary>
        private Panel CreatePictureBoxWithLabel(FileInfo file, int pictureBoxWidth, int pictureBoxHeight)
        {
            // Create Panel to hold PictureBox and Label
            Panel panel = new Panel
            {
                Width = pictureBoxWidth,
                Height = pictureBoxHeight + labelHeight, // Adjust height to include space for label
                Margin = new Padding(5),
                AutoSize = false
            };

            // Create PictureBox
            PictureBox pictureBox = new PictureBox
            {
                Width = pictureBoxWidth,
                Height = pictureBoxHeight,
                BorderStyle = BorderStyle.None,
                SizeMode = PictureBoxSizeMode.StretchImage,
                Cursor = Cursors.Hand,
                BackColor = Color.LightGray,
                Tag = file.FullName,
                Dock = DockStyle.Top // Dock to top to place Label at bottom
            };
            // Event handlers for PictureBox
            pictureBox.Click += PictureBox_Click;
            pictureBox.DoubleClick += PictureBox_DoubleClick;
            pictureBox.Paint += PictureBox_Paint;
            // Load the image thumbnail
            try
            {
                Bitmap rhinoThumbnail = File3dm.ReadPreviewImage(file.FullName);
                pictureBox.Image = rhinoThumbnail ?? Properties.Resources.kutuphane; // Use a placeholder if no image
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error loading thumbnail for {file.FullName}: {ex.Message}");
            }

            // Create Label for file name
            Label label = new Label
            {
                Text = GetMultilineTruncatedText(file.Name, pictureBoxWidth),
                Dock = DockStyle.Bottom, // Dock to bottom of the panel
                TextAlign = ContentAlignment.MiddleCenter,
                AutoSize = false,
                Width = pictureBoxWidth,
                Height = labelHeight,
                MaximumSize = new Size(pictureBoxWidth, labelHeight), // Limit to two lines
                AutoEllipsis = true,
                Font = new Font("Microsoft Sans Serif", 8.25F), // Set desired font
                Padding = new Padding(0, 3, 0, 0) // Optional padding for better alignment
            };



            // Add PictureBox and Label to Panel
            panel.Controls.Add(pictureBox);
            panel.Controls.Add(label);

            // Add tooltip to PictureBox for full filename
            toolTip.SetToolTip(pictureBox, file.Name);

            return panel;
        }
        // Helper function to handle multiline text with truncation
        private string GetMultilineTruncatedText(string text, int maxWidth)
        {
            if (text.EndsWith(".3dm", StringComparison.OrdinalIgnoreCase))
            {
                text = text.Substring(0, text.Length - 4);
            }

            using (Graphics g = CreateGraphics())
            {
                Font font = new Font("Microsoft Sans Serif", 8.25F);
                StringFormat format = new StringFormat
                {
                    Trimming = StringTrimming.EllipsisCharacter,
                    FormatFlags = StringFormatFlags.LineLimit // Limits text to line limits
                };

                // Set maximum size to two lines worth of height
                SizeF size = g.MeasureString(text, font, new SizeF(maxWidth, font.Height * 2), format);

                // If it fits, return text as is
                if (size.Height <= font.Height * 2)
                    return text;

                // Else, truncate to fit in two lines
                int fittingChars = g.MeasureString(text, font, new SizeF(maxWidth, font.Height * 2), format).ToString().Length;
                return text.Substring(0, fittingChars) + "...";
            }
        }
        // Helper method to truncate file name if too long
        private string GetTruncatedFileName(string fileName, int maxWidth)
        {
            using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
            {
                Font font = new Font("Microsoft Sans Serif", 8.25F);
                SizeF size = g.MeasureString(fileName, font);
                if (size.Width > maxWidth)
                {
                    // Calculate how many characters fit in the maxWidth minus some buffer for the "..."
                    int charCount = (int)(fileName.Length * (maxWidth / size.Width)) - 3;
                    return fileName.Substring(0, charCount) + "...";
                }
                return fileName;
            }
        }
        /// <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();

            // Capture the current search token
            int searchToken = currentSearchToken;

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

        private async Task PerformSearchAsync(int searchToken)
        {
            // Ensure we're on the UI thread before updating UI controls
            if (InvokeRequired)
            {
                Invoke(new Action(async () => await PerformSearchAsync(searchToken)));
                return;
            }

            // Show the searching indicators
            lblSearching.Visible = true;
            pictureBoxSearch.Visible = true;

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

            
            if (searchSubdirectories)
            {
                if (!isCacheReady)
                {
                    // Cache is not ready
                    lblSearching.Text = "Building file cache, please wait...";
                    return;
                }

                // Perform search in the cache list on a background thread
                matchingFiles = await Task.Run(() =>
                {
                    return cachedFiles.Where(file => file.Name.ToLower().Contains(searchText)).ToList();
                });
            }
            else
            {
                DirectoryInfo currentDirectory = GetCurrentSelectedDirectory();

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

                    // Only hide the indicators if this is the latest search
                    if (searchToken == currentSearchToken)
                    {
                        lblSearching.Visible = false;
                        pictureBoxSearch.Visible = false;
                    }
                    return;
                }

                // Perform search in current directory on a background thread
                matchingFiles = await Task.Run(() =>
                {
                    return SearchInCurrentDirectory(currentDirectory, searchText);
                });
            }

            // Before updating the UI, check if this is still the latest search
            if (searchToken != currentSearchToken)
            {
                // A newer search has started, do not update the UI or hide the indicators
                return;
            }

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

            // Hide the searching indicators
            lblSearching.Visible = false;
            pictureBoxSearch.Visible = false;
        }

        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;
            }
        }
        // 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();

                // Optionally, reset the search token
                // currentSearchToken = 0;

                return;
            }

            if (chbSearchAll.Checked && !isCacheReady)
            {
                // Show a message indicating that cache is being built
                lblSearching.Text = "Building file cache, please wait...";
                lblSearching.Visible = true;
                pictureBoxSearch.Visible = true;

                // Do not proceed with search until cache is ready
                return;
            }

            // Clear the tableLayoutPanel01
            tableLayoutPanel01.Controls.Clear();

            lblSearching.Text = "Searching...";
            lblSearching.Visible = true;
            pictureBoxSearch.Visible = true;

            // Increment the search token
            currentSearchToken++;

            // 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, int searchToken)
        {
            if (searchToken != currentSearchToken)
            {
                // A newer search has started, do not update the UI
                return;
            }

            if (InvokeRequired)
            {
                Invoke(new Action(() => UpdatePictureBoxes(matchingFiles, searchToken)));
                return;
            }

            lblSearching.Visible = false;
            pictureBoxSearch.Visible = false;

            // 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, 800));

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

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

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

                // Set up styles
                for (int i = 0; i < ColumnCount; i++)
                {
                    tableLayoutPanel01.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, pictureBoxWidthSize));
                }

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

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

                // Populate controls
                for (int i = 0; i < matchingFiles.Count; i++)
                {
                    FileInfo file = matchingFiles[i];
                    Panel panel = CreatePictureBoxWithLabel(file, pictureBoxWidthSize, pictureBoxHeightSize);

                    int row = i / ColumnCount;
                    int col = i % ColumnCount;
                    tableLayoutPanel01.Controls.Add(panel, col, row);
                    pictureBoxes.Add(panel.Controls.OfType<PictureBox>().FirstOrDefault());
                }
            }

            // 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;
                }
            }

            // 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;
            }
        }



        private void txtUser_Enter(object sender, EventArgs e)
        {
            if (txtUser.Text == "Staff ID...")
            {
                txtUser.Text = "";
                txtUser.ForeColor = Color.FromArgb(86, 106, 127);
            }
        }

        private void txtUser_Leave(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtUser.Text))
            {
                txtUser.Text = "Staff ID...";
                txtUser.ForeColor = Color.FromArgb(170, 170, 170);
            }
        }


        private void txtPass_Enter(object sender, EventArgs e)
        {
            if (txtPass.Text == "Password...")
            {
                txtPass.Text = "";
                txtPass.ForeColor = Color.FromArgb(86, 106, 127);
            }
        }

        private void txtPass_Leave(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(txtPass.Text))
            {
                txtPass.Text = "Password...";
                txtPass.ForeColor = Color.FromArgb(170, 170, 170);
            }
        }

        private void lblSearching_Click(object sender, EventArgs e)
        {

        }

     
    }
}

instead of one line label and puting "..." at the end of it
I want to make it two line and if still at the end of picture box with there is anything left of the text replace it with "..."