﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using Microsoft.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell.TableControl;
using System.Collections.Generic;

namespace Microsoft.VisualStudio.LanguageServices.EditorConfigSettings
{
    internal sealed partial class SettingsEditorPane
    {
        internal class SearchFilter : IEntryFilter
        {
            private readonly IEnumerable<IVsSearchToken> _searchTokens;
            private readonly IReadOnlyList<ITableColumnDefinition>? _visibleColumns;

            public SearchFilter(IVsSearchQuery searchQuery, IWpfTableControl control)
            {
                _searchTokens = SearchUtilities.ExtractSearchTokens(searchQuery);
                _searchTokens ??= Array.Empty<IVsSearchToken>();

                var newVisibleColumns = new List<ITableColumnDefinition>();
                foreach (var c in control.ColumnStates)
                {
                    if (c.IsVisible || ((c as ColumnState2)?.GroupingPriority > 0))
                    {
                        var definition = control.ColumnDefinitionManager.GetColumnDefinition(c.Name);
                        if (definition != null)
                        {
                            newVisibleColumns.Add(definition);
                        }
                    }
                }

                _visibleColumns = newVisibleColumns;
            }

            public bool Match(ITableEntryHandle entry)
            {
                if (_visibleColumns is null)
                {
                    return false;
                }

                // An entry is considered matching a search query if all tokens in the search query are matching at least one of entry's columns.
                // Reserve one more column for details content
                var cachedColumnValues = new string[_visibleColumns.Count + 1];

                foreach (var searchToken in _searchTokens)
                {
                    // No support for filters yet
                    if (searchToken is IVsSearchFilterToken)
                    {
                        continue;
                    }

                    if (!AtLeastOneColumnOrDetailsContentMatches(entry, searchToken, cachedColumnValues))
                    {
                        return false;
                    }
                }

                return true;
            }

            private bool AtLeastOneColumnOrDetailsContentMatches(ITableEntryHandle entry, IVsSearchToken searchToken, string[] cachedColumnValues)
            {
                // Check details content for any matches
                if (cachedColumnValues[0] == null)
                {
                    cachedColumnValues[0] = GetDetailsContentAsString(entry);
                }

                var detailsContent = cachedColumnValues[0];
                if (detailsContent != null && Match(detailsContent, searchToken))
                {
                    // Found match in details content
                    return true;
                }

                if (_visibleColumns is null)
                {
                    return false;
                }

                // Check each column for any matches
                for (var i = 0; i < _visibleColumns.Count; i++)
                {
                    if (cachedColumnValues[i + 1] == null)
                    {
                        cachedColumnValues[i + 1] = GetColumnValueAsString(entry, _visibleColumns[i]);
                    }

                    var columnValue = cachedColumnValues[i + 1];

                    if (columnValue != null && Match(columnValue, searchToken))
                    {
                        // Found match in this column
                        return true;
                    }
                }

                // No match found in this entry
                return false;
            }

            private static string GetColumnValueAsString(ITableEntryHandle entry, ITableColumnDefinition column)
                => (entry.TryCreateStringContent(column, truncatedText: false, singleColumnView: false, content: out var columnValue) && columnValue is not null)
                    ? columnValue
                    : string.Empty;

            private static string GetDetailsContentAsString(ITableEntryHandle entry)
            {
                string? detailsString = null;

                if (entry.CanShowDetails)
                {
                    if (entry is IWpfTableEntry wpfEntry)
                    {
                        _ = wpfEntry.TryCreateDetailsStringContent(out detailsString);
                    }
                }

                return detailsString ?? string.Empty;
            }

            private static bool Match(string columnValue, IVsSearchToken searchToken)
                => (columnValue is not null) && (columnValue.IndexOf(searchToken.ParsedTokenText, StringComparison.OrdinalIgnoreCase) >= 0);
        }
    }
}
