// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

#include "FilteredSearchResult.g.h"
#include "GeneratedSettingsIndex.g.h"
#include <til/generational.h>
#include "..\fzf\fzf.h"

class ScopedResourceLoader;

namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
{
    const ScopedResourceLoader& EnglishOnlyResourceLoader() noexcept;

    // Wrapper for IndexEntry objects.
    // Holds localized display text from entry's ResourceName
    struct LocalizedIndexEntry
    {
        winrt::hstring DisplayTextLocalized;
        std::optional<winrt::hstring> DisplayTextNeutral = std::nullopt;
        const IndexEntry* Entry = nullptr;

        std::array<std::pair<std::optional<winrt::hstring>, int>, 2> GetSearchableFields() const;
    };

    // Data backing the search index. Shared via shared_ptr so that
    // in-flight search results keep their snapshot alive even after
    // the index is rebuilt by Reset().
    struct IndexData
    {
        // Basic index data loaded from GeneratedSettingsIndex.g.h and wrapped as LocalizedIndexEntry objects.
        // Non-main indices are duplicated at runtime when searching for runtime objects.
        std::vector<LocalizedIndexEntry> mainIndex;
        std::vector<LocalizedIndexEntry> profileIndex;
        std::vector<LocalizedIndexEntry> ntmFolderIndex;
        std::vector<LocalizedIndexEntry> colorSchemeIndex;

        // Links to main page; used when searching runtime objects
        LocalizedIndexEntry profileIndexEntry;
        LocalizedIndexEntry ntmFolderIndexEntry;
        LocalizedIndexEntry colorSchemeIndexEntry;
        LocalizedIndexEntry extensionIndexEntry;
        LocalizedIndexEntry actionIndexEntry;
    };

    // WinRT object representing a single filtered search result
    struct FilteredSearchResult : FilteredSearchResultT<FilteredSearchResult>
    {
        FilteredSearchResult(std::shared_ptr<const IndexData> indexData, const LocalizedIndexEntry* entry, const Windows::Foundation::IInspectable& navigationArgOverride = nullptr, const std::optional<hstring>& label = std::nullopt, const hstring secondaryLabel = {}) :
            _indexData{ std::move(indexData) },
            _SearchIndexEntry{ entry },
            _NavigationArgOverride{ navigationArgOverride },
            _overrideLabel{ label },
            _secondaryLabel{ secondaryLabel } {}

        static Editor::FilteredSearchResult CreateNoResultsItem(const winrt::hstring& query);
        static Editor::FilteredSearchResult CreateRuntimeObjectItem(std::shared_ptr<const IndexData> indexData, const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj);

        hstring ToString() { return AccessibleName(); }
        winrt::hstring AccessibleName() const;
        winrt::hstring Label() const;
        winrt::hstring SecondaryLabel() const { return _secondaryLabel; };
        bool IsNoResultsPlaceholder() const;
        const LocalizedIndexEntry& SearchIndexEntry() const noexcept { return *_SearchIndexEntry; }
        Windows::Foundation::IInspectable NavigationArg() const;
        Windows::UI::Xaml::Controls::IconElement Icon() const;

    private:
        // Prevent the IndexData (and the LocalizedIndexEntry objects within)
        // from being freed while this search result is still alive.
        const std::shared_ptr<const IndexData> _indexData;
        const std::optional<winrt::hstring> _overrideLabel{ std::nullopt };
        const winrt::hstring _secondaryLabel{};
        const Windows::Foundation::IInspectable _NavigationArgOverride{ nullptr };
        const LocalizedIndexEntry* _SearchIndexEntry{ nullptr };
    };

    // The main search index implemented as a singleton.
    // The index loads data generated by tools\GenerateSettingsIndex.ps1 (outputs "Generated Files\GeneratedSettingsIndex.g.h" and cpp).
    // Outputs FilteredSearchResult objects for UI display.
    class SearchIndex final
    {
    public:
        static SearchIndex& Instance() noexcept
        {
            static SearchIndex instance;
            return instance;
        }

        SearchIndex(const SearchIndex&) = delete;
        SearchIndex& operator=(const SearchIndex&) = delete;
        SearchIndex(SearchIndex&&) = delete;
        SearchIndex& operator=(SearchIndex&&) = delete;

        void Reset();
        Windows::Foundation::IAsyncOperation<Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable>> SearchAsync(const winrt::hstring& query,
                                                                                                                                                 const Windows::Foundation::Collections::IVectorView<Editor::ProfileViewModel> profileVMs,
                                                                                                                                                 const Windows::Foundation::Collections::IVectorView<Editor::FolderEntryViewModel> ntmFolderVMs,
                                                                                                                                                 const Windows::Foundation::Collections::IVectorView<Editor::ColorSchemeViewModel> colorSchemeVMs,
                                                                                                                                                 const Windows::Foundation::Collections::IVectorView<Editor::ExtensionPackageViewModel> extensionPkgVMs,
                                                                                                                                                 const Windows::Foundation::Collections::IVectorView<Editor::CommandViewModel> commandVMs);

    private:
        SearchIndex() = default;
        std::atomic<std::shared_ptr<const IndexData>> _index;
    };
}
