@inherits ViewBase

@code {
    private Dictionary<string, string> AllTrackSet;

    private string searchName { get => LocalStorage.GetByCallerMemberName<string>(); set => LocalStorage.SetByCallerMemberName(value); }
    private string searchArtist { get => LocalStorage.GetByCallerMemberName<string>(); set => LocalStorage.SetByCallerMemberName(value); }
    private string searchFind { get => LocalStorage.GetByCallerMemberName<string>() ?? "Track"; set => LocalStorage.SetByCallerMemberName(value); }
    private string searchTrackSetKey { get => LocalStorage.GetByCallerMemberName<string>() ?? "main"; set => LocalStorage.SetByCallerMemberName(value); }

    private int searchLimitCount { get => LocalStorage.GetByCallerMemberName<int?>() ?? 20; set => LocalStorage.SetByCallerMemberName(value); }

    private bool isSearching = false;
    private ProgressBar searchProgressBar;

    private static string resultText;
    private static IGrouping<FeDisc, FeTrack>[] resultTracks;
    private static FeDisc[] resultDiscs;

    private DiscDialog dlgDisc;
}


<div class="row">
    <div class="col-12">
        <fieldset class="border rounded-3 p-2">
            <legend class="float-none w-auto px-2 d-flex flex-row align-items-center">
                <span>Filter</span>
            </legend>
            <div class="input-group">
                <div class="form-floating">
                    <InputText type="search" class="form-control" placeholder="Name"
                               Value="@searchName"
                               ValueExpression="@(()=>searchName)"
                               @oninput="x=> searchName = x?.Value?.ToString()?.Trim() ?? string.Empty"
                               onsearch="@SearchGo">
                    </InputText>
                    <label>Name</label>
                </div>
            </div>
            <div class="input-group mt-1">
                <div class="form-floating">
                    <InputText type="search" class="form-control" placeholder="Artist"
                               Value="@searchArtist"
                               ValueExpression="@(()=>searchArtist)"
                               @oninput="x=> searchArtist = x?.Value?.ToString()?.Trim() ?? string.Empty"
                               onsearch="@SearchGo">
                    </InputText>
                    <label>Artist</label>
                </div>
            </div>
            <div class="row mt-3">
                <label class="col-sm-2 col-form-label">Find</label>
                <div class="col-sm-10">
                    <FnzRadioGroup TValue="string"
                                   DataSource="@(new[] { "Track", "Disc" })"
                                   Value="searchFind"
                                   ValueChanged="async (v) => {searchFind = v; StateHasChanged(); await SearchGo(); }">
                    </FnzRadioGroup>
                </div>
            </div>
            @if (searchFind == "Track")
            {
                <div class="row mt-3">
                    <label class="col-sm-2 col-form-label">Track set</label>
                    <div class="col-sm-10">
                        <FnzRadioGroup TValue="string"
                                       DataSource="@AllTrackSet?.Keys.KeepNoEmpty()"
                                       Value="searchTrackSetKey"
                                       TextFormat="x=>x.ToUpper()"
                                       ValueChanged="async (v) => {searchTrackSetKey = v; StateHasChanged(); await SearchGo(); }">
                        </FnzRadioGroup>
                    </div>
                </div>
            }
            <div class="row mt-3">
                <label class="col-sm-2 col-form-label">Result count</label>
                <div class="col-sm-10">
                    <FnzRadioGroup TValue="int"
                                   DataSource="@(new [] { 20, 50, 100, 200 })"
                                   Value="searchLimitCount"
                                   ValueChanged="async (v) => {searchLimitCount = v; StateHasChanged(); await SearchGo(); }">
                    </FnzRadioGroup>
                </div>
            </div>
            <div class="row mt-3">
                <div class="col-12 text-end">
                    <button @onclick="@SearchGo" class="btn btn-primary" type="button">Search</button>
                </div>
            </div>
        </fieldset>
    </div>
</div>

@if (isSearching)
{
    <div class="row mt-4">
        <div class="col-12">
            <div class="btn-group w-100">
                <ProgressBar @ref="searchProgressBar" Throttle="333" class="btn border w-75"></ProgressBar>
                <button class="btn border btn-danger" @onclick="()=>isSearching=false">Stop search</button>
            </div>
        </div>
    </div>
}

<div class="row mt-3">
    <div class="col-12">
        <fieldset class="border rounded-3 p-2">
            <legend class="float-none w-auto px-2 d-flex flex-row align-items-center">
                <span>Result</span>
            </legend>

            <div class="mb-3 text-center">@resultText</div>

            @if (searchFind == "Track" && resultTracks?.Length > 0)
            {
                foreach (var item in resultTracks)
                {
                    <SearchResultGroup DataSource="item" SearchName="searchName" SearchArtist="searchArtist"></SearchResultGroup>
                }
            }

            @if (searchFind == "Disc" && resultDiscs?.Length > 0)
            {
                <div class="row">
                    @foreach (var disc in resultDiscs)
                    {
                        <DiscCard Disc="disc"></DiscCard>
                    }
                </div>
            }
        </fieldset>
    </div>
</div>

@code {
    private async Task SearchGo()
    {
        if (isSearching) return;


        isSearching = true;
        StateHasChanged();

        await Task.Delay(1);

        var matchedCount = 0;
        var flagPlus = false;

        //var filterExpression

        if (searchFind == "Track")
        {
            var filteredList = new List<FeTrack>(searchLimitCount);
            var filters = new List<Func<FeTrack, bool>> { p => p.TrackSetKey == searchTrackSetKey };

            if (string.IsNullOrEmpty(searchName) == false)
            {
                filters.Add(p => p.GetTitleOrFilename().Contains(searchName, StringComparison.OrdinalIgnoreCase));
            }

            if (string.IsNullOrEmpty(searchArtist) == false)
            {
                filters.Add(p => p.Tag?.Artist.Contains(searchArtist, StringComparison.OrdinalIgnoreCase) == true);
            }

            for (var i = 0; i < DataSet.AllTracks.Length && isSearching; i++)
            {
                var item = DataSet.AllTracks[i];
                if (filters.All(p => p(item)))
                {
                    ++matchedCount;
                    if (filteredList.Count < searchLimitCount)
                    {
                        filteredList.Add(item);
                    }
                    else if (matchedCount >= searchLimitCount * 2)
                    {
                        flagPlus = true;
                        break;
                    }
                }
                await searchProgressBar.SetProgress((float)i / DataSet.AllTracks.Length, $"Searching {i + 1}/{DataSet.AllTracks.Length} matched:{matchedCount}");
            }

            resultText = $"Result for {searchName.NullOrEmptyEscape("<NONE>")} on {DataSet.AllTracksSet.Where(p => p.Key == searchTrackSetKey).Select(p => p.Name).FirstOrDefault() ?? "?"} limit {searchLimitCount} of {matchedCount}{(flagPlus ? "+" : "")}";

            resultTracks = filteredList
                .GroupBy(p => p.Disc)
                .ToArray();
        }
        else if (searchFind == "Disc")
        {
            var filteredList = new List<FeDisc>(searchLimitCount);
            var filters = new List<Func<FeDisc, bool>> { };

            if (string.IsNullOrEmpty(searchName) == false)
            {
                filters.Add(p => p.Name.Contains(searchName, StringComparison.OrdinalIgnoreCase));
            }

            if (string.IsNullOrEmpty(searchArtist) == false)
            {
                filters.Add(p => p.TrackSets.Any(s => s.Tracks.Any(t => t.Tag?.Artist.Contains(searchArtist, StringComparison.OrdinalIgnoreCase) == true)));
            }

            for (var i = 0; i < DataSet.AllDisc.Length && isSearching; i++)
            {
                var item = DataSet.AllDisc[i];
                if (filters.All(p => p(item)))
                {
                    ++matchedCount;
                    if (filteredList.Count < searchLimitCount)
                    {
                        filteredList.Add(item);
                    }
                    else if (matchedCount >= searchLimitCount * 2)
                    {
                        flagPlus = true;
                        break;
                    }
                }
                await searchProgressBar.SetProgress((float)i / DataSet.AllDisc.Length, $"Searching {i + 1}/{DataSet.AllDisc.Length} matched:{matchedCount}");
            }

            resultText = $"Result for Discs limit {searchLimitCount} of {matchedCount}{(flagPlus ? "+" : "")}";

            resultDiscs = filteredList.ToArray();
        }

        isSearching = false;
        StateHasChanged();
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);
        if (firstRender)
        {
            AllTrackSet = DataSet.AllTracksSet.GroupBy(p => p.Key).ToDictionary(p => p.Key, p => p.First().Name);
            if (searchFind == "Track" && resultTracks == null) await SearchGo();
            if (searchFind == "Disc" && resultDiscs == null) await SearchGo();
            else StateHasChanged();
        }
    }
}
