// TODO de werkwijze van het aanpassen van de autocomplete source veroorzaakt soms een access violation. Bijv. snel typen na opstarten.
        // TODO soms triggert de autoselect en wordt je tekst vanzelf geselecteerd, dat is irritant.
        // TODO er lijkt een memoryleak te zijn. Geheugengebruik loopt op als je snel wisselt tussen bijv. 'psalmen ' en 'psalmen 1'. Vermoedelijk de database.
        private void TriggerZoeklijstVeranderd()
        {
            _huidigZoekresultaat = _liturgieZoeker.VrijZoeken(textBoxZoek.Text, checkBoxAlsBijbeltekst.Checked, _huidigZoekresultaat);
            if (_huidigZoekresultaat.ZoeklijstAanpassing == VrijZoekresultaatAanpassingType.Geen)
            {
                return;
            }

            // We gaan kijken wat de verandering is.
            // Dit moet wat slimmer dan gewoon verwijderen/toevoegen omdat deze lijst zich instabiel gedraagt
            textBoxZoek.SuspendLayout();
            lock (_dropdownLocker)  // Lock om te voorkomen dat werk nog niet af is als we er nog een x in komen (lijkt namelijk te gebeuren)
            {
                if (_huidigZoekresultaat == null || _huidigZoekresultaat.ZoeklijstAanpassing == VrijZoekresultaatAanpassingType.Alles || _huidigZoekresultaat.DeltaMogelijkhedenVerwijderd.Count() > 50)
                {
                    textBoxZoek.AutoCompleteCustomSource.Clear();
                    textBoxZoek.AutoCompleteCustomSource.AddRange(_huidigZoekresultaat.AlleMogelijkheden.Select(m => m.Weergave).ToArray());
                }
                else if (_huidigZoekresultaat.ZoeklijstAanpassing == VrijZoekresultaatAanpassingType.Deel)
                {
                    textBoxZoek.AutoCompleteCustomSource.AddRange(_huidigZoekresultaat.DeltaMogelijkhedenToegevoegd.Select(m => m.Weergave).ToArray());
                    foreach (var item in _huidigZoekresultaat.DeltaMogelijkhedenVerwijderd)
                    {
                        textBoxZoek.AutoCompleteCustomSource.Remove(item.Weergave);
                    }
                }
            }
            textBoxZoek.ResumeLayout();
        }
        /// <summary>
        /// Dit maakt een lijst van resultaten die voldoen aan de zoektekst. Filteren gebeurd hier niet maar in de UI zelf.
        /// We helpen de UX beleving door de UI niet direct alle mogelijkheden meteen terug te geven, alleen te verdiepen waar de gebruiker
        /// echt naar zoekt.
        /// Verder wordt aangegeven welke zoekresultaten veranderd zijn zodat de UI de verwijderde of toegevoegde elementen kan animeren
        /// of iets dergelijks.
        /// </summary>
        public IVrijZoekresultaat VrijZoeken(string zoekTekst, bool alsBijbeltekst = false, IVrijZoekresultaat vorigResultaat = null)
        {
            var veiligeZoekTekst   = (zoekTekst ?? "").TrimStart();
            var veranderingGemaakt = vorigResultaat == null;
            var zoekRestricties    = new ZoekRestricties(alsBijbeltekst);
            var aanname            = vorigResultaat?.Aanname;
            var laatsteZoektekenIsFragmentWissel = veiligeZoekTekst.Length > 0 ? LiturgieInterpretator.LiturgieTekstNaarObject.BenamingDeelScheidingstekens.Contains(veiligeZoekTekst.Last()) : false;

            var onderdeelLijst       = KrijgBasisDatabaseLijst(zoekRestricties, true);
            var fragmentLijst        = Enumerable.Empty <IVrijZoekresultaatMogelijkheid>();
            var vorigeZoektermSplit  = _liturgieTekstNaarObject.VanTekstregel(vorigResultaat == null ? "" : vorigResultaat.ZoekTerm);
            var huidigeZoektermSplit = _liturgieTekstNaarObject.VanTekstregel(veiligeZoekTekst);

            // Wisselen tussen bijbeltekst vinkje of niet geeft natuurlijk grote wijziging
            if (vorigResultaat != null && vorigResultaat.AlsBijbeltekst != alsBijbeltekst)
            {
                veranderingGemaakt = true;
            }

            // Let op: Omdat de UI zelf filtert detecteren we hier alleen overgangen.

            // Kijk of er in de zoektekst een spatie is gebruikt, dan komt er nu een overgang aan
            if ((veiligeZoekTekst.Length > 0 && laatsteZoektekenIsFragmentWissel) || (string.IsNullOrWhiteSpace(vorigeZoektermSplit.Deel) && !string.IsNullOrWhiteSpace(huidigeZoektermSplit.Deel)))
            {
                // Fragment is er bij gekomen
                veranderingGemaakt = true;
                fragmentLijst      = ZoekFragmenten(zoekRestricties, huidigeZoektermSplit.Benaming).Select(t => new ZoekresultaatItem()
                {
                    Weergave    = $"{huidigeZoektermSplit.Benaming} {t.Resultaat.Weergave}",
                    UitDatabase = t.Database.Weergave,
                }).ToList();

                // Geen aannames meer
                aanname = null;
            }
            // Zo gauw je de spatie weghaald is de overgang weer weg
            else if ((veiligeZoekTekst.Length == 0 || !laatsteZoektekenIsFragmentWissel) && string.IsNullOrWhiteSpace(huidigeZoektermSplit.Deel) && (vorigResultaat != null && vorigResultaat.ZoekTerm.Length > 0 && LiturgieInterpretator.LiturgieTekstNaarObject.BenamingDeelScheidingstekens.Contains(vorigResultaat.ZoekTerm.Last())))
            {
                // Fragment is weer weg gehaald
                veranderingGemaakt = true;
            }

            // Bepaal het nieuwe zoekresultaat
            var nieuwResultaat = ZoekresultaatSamenstellen(veiligeZoekTekst, alsBijbeltekst, vorigResultaat, onderdeelLijst.Union(fragmentLijst), aanname, veranderingGemaakt);

            if (vorigResultaat == null)
            {
                return(nieuwResultaat);
            }

            // Het kan zijn dat bij het vorige zoekresultaat nog meerdere opties mogelijk waren, maar dat er nu nog maar 1 optie mogelijk is,
            // terwijl je nog wel een paar tekens moet typen. Dat kan handiger,  we maken de aanname dat je deze ene optie bedoelt:
            if (!laatsteZoektekenIsFragmentWissel && vorigResultaat.Aanname == null && VoorspelZoekresultaat(vorigResultaat.AlleMogelijkheden, vorigResultaat.ZoekTerm).Count() > 1 && VoorspelZoekresultaat(nieuwResultaat.AlleMogelijkheden, nieuwResultaat.ZoekTerm).Count() == 1)
            {
                veranderingGemaakt = true;
                aanname            = VoorspelZoekresultaat(nieuwResultaat.AlleMogelijkheden, nieuwResultaat.ZoekTerm).First().Weergave;

                // Fragment toevoegen op basis van aanname
                fragmentLijst = ZoekFragmenten(zoekRestricties, aanname).Select(t => new ZoekresultaatItem()
                {
                    Weergave    = $"{aanname} {t.Resultaat.Weergave}",
                    UitDatabase = t.Database.Weergave,
                }).ToList();

                nieuwResultaat = ZoekresultaatSamenstellen(veiligeZoekTekst, alsBijbeltekst, vorigResultaat, onderdeelLijst.Union(fragmentLijst), aanname, veranderingGemaakt);
            }
            // Als de aanname niet meer gemaakt kan worden
            else if (!string.IsNullOrEmpty(vorigResultaat.Aanname) && VoorspelZoekresultaat(nieuwResultaat.AlleMogelijkheden, nieuwResultaat.ZoekTerm).Count() > VoorspelZoekresultaat(vorigResultaat.AlleMogelijkheden, vorigResultaat.ZoekTerm).Count())
            {
                veranderingGemaakt = true;
                aanname            = null;

                nieuwResultaat = ZoekresultaatSamenstellen(veiligeZoekTekst, alsBijbeltekst, vorigResultaat, onderdeelLijst.Union(fragmentLijst), aanname, veranderingGemaakt);
            }

            return(nieuwResultaat);
        }
        public string MaakTotTekst(string invoerTekst, LiturgieOptiesGebruiker opties, IVrijZoekresultaat zoekresultaat)
        {
            var tekstUitOpties        = _liturgieTekstNaarObject.MaakTekstVanOpties(opties);
            var gebruiktZoekresultaat = zoekresultaat.AlleMogelijkheden
                                        .Where(w => invoerTekst.StartsWith(w.Weergave))
                                        .OrderByDescending(w => w.Weergave.Length)
                                        .FirstOrDefault();

            if (gebruiktZoekresultaat == null)
            {
                return($"{invoerTekst.Trim()} {tekstUitOpties.Trim()}".Trim());
            }
            else
            {
                var resterend = invoerTekst.Substring(gebruiktZoekresultaat.Weergave.Length);
                return($"{gebruiktZoekresultaat.VeiligeNaam} {resterend.Trim()} {tekstUitOpties.Trim()}".Trim());
            }
        }
        public LiturgieOptiesGebruiker ZoekStandaardOptiesUitZoekresultaat(string invoerTekst, IVrijZoekresultaat zoekresultaat)
        {
            if (string.IsNullOrWhiteSpace(invoerTekst))
            {
                return(null);
            }
            var databaseNaam = string.Empty;

            if (zoekresultaat != null)
            {
                var invoerTekstSplitsing = _liturgieTekstNaarObject.VanTekstregel(invoerTekst);
                var teZoekenTekst        = $"{invoerTekstSplitsing.Benaming} {invoerTekstSplitsing.Deel}";
                var itemInZoeklijst      = zoekresultaat.AlleMogelijkheden.FirstOrDefault(z => z.Weergave == teZoekenTekst);
                if (itemInZoeklijst != null)
                {
                    databaseNaam = itemInZoeklijst.UitDatabase;
                }
                if (string.IsNullOrWhiteSpace(databaseNaam))
                {
                    databaseNaam = zoekresultaat.VermoedelijkeDatabase;
                }
            }
            return(_liturgieTekstNaarObject.BepaalBasisOptiesTekstinvoer(invoerTekst, databaseNaam));
        }
        private Zoekresultaat ZoekresultaatSamenstellen(string zoekTekst, bool alsBijbeltekst, IVrijZoekresultaat vorigResultaat, IEnumerable <IVrijZoekresultaatMogelijkheid> lijst, string aanname, bool lijstIsGewijzigd)
        {
            var zoekLijst = Enumerable.Empty <IVrijZoekresultaatMogelijkheid>();
            var zoekLijstDeltaToegevoegd = Enumerable.Empty <IVrijZoekresultaatMogelijkheid>();
            var zoekLijstDeltaVerwijderd = Enumerable.Empty <IVrijZoekresultaatMogelijkheid>();
            var aanpassing            = VrijZoekresultaatAanpassingType.Alles;
            var vermoedelijkeDatabase = string.Empty;

            if (!lijstIsGewijzigd && vorigResultaat != null)
            {
                aanpassing            = VrijZoekresultaatAanpassingType.Geen;
                zoekLijst             = vorigResultaat.AlleMogelijkheden.ToList();
                vermoedelijkeDatabase = vorigResultaat.VermoedelijkeDatabase;
            }
            else
            {
                zoekLijst = lijst.Distinct().OrderBy(z => z.Weergave).ToList();
                if (aanpassing == VrijZoekresultaatAanpassingType.Alles && vorigResultaat != null)
                {
                    zoekLijstDeltaToegevoegd = zoekLijst.Where(z => !vorigResultaat.AlleMogelijkheden.Contains(z)).ToList();
                    zoekLijstDeltaVerwijderd = vorigResultaat.AlleMogelijkheden.Where(z => !zoekLijst.Contains(z)).ToList();
                    if (zoekLijstDeltaVerwijderd.Count() != vorigResultaat.AlleMogelijkheden.Count())
                    {
                        aanpassing = VrijZoekresultaatAanpassingType.Deel;
                    }
                }

                var databases = zoekLijst.Select(z => z.UitDatabase).Distinct().ToList();
                if (databases.Count == 1)
                {
                    vermoedelijkeDatabase = databases.First();
                }
            }

            return(new Zoekresultaat()
            {
                ZoekTerm = zoekTekst,
                AlsBijbeltekst = alsBijbeltekst,
                Aanname = aanname,
                VermoedelijkeDatabase = vermoedelijkeDatabase,
                AlleMogelijkheden = zoekLijst.ToList(),
                DeltaMogelijkhedenToegevoegd = zoekLijstDeltaToegevoegd,
                DeltaMogelijkhedenVerwijderd = zoekLijstDeltaVerwijderd,
                ZoeklijstAanpassing = aanpassing,
            });
        }