/// <summary>
        /// Needs to be called to update selections that depend on the available logs, such as
        /// filtering by encounter name.
        /// </summary>
        /// <param name="logs">Logs to be available for filtering.</param>
        public void UpdateLogs(IReadOnlyList <LogData> logs)
        {
            // We construct a tree of groups. Everything is under an "All logs" node that can be used to easily select all.
            // All raid wings which have separate categories are within a meta group "Raids". Other than that, only
            // Encounter.Other logs have special handling - each unique target getting its own group.

            var rootGroup = new RootLogGroup(logs);
            var rootItem  = new GroupFilterTreeItem(rootGroup);

            void UpdateIcons(GroupFilterTreeItem item)
            {
                item.Icon = item.LogGroup switch
                {
                    RootLogGroup => imageProvider.GetTinyLogIcon(),
                    RaidLogGroup => imageProvider.GetTinyRaidIcon(),
                    CategoryLogGroup categoryGroup => GetCategoryIcon(categoryGroup.Category),
                    EncounterLogGroup encounterGroup => GetEncounterIcon(encounterGroup.Encounter),
                    MapLogGroup mapGroup => imageProvider.GetWvWMapIcon(mapGroup.Map),
                    _ => item.Icon
                };

                foreach (var child in item.Children)
                {
                    UpdateIcons((GroupFilterTreeItem)child);
                }
            }

            void UpdateCounts(GroupFilterTreeItem item)
            {
                item.LogCount = logs.Count(log => item.LogGroup.IsInGroup(log));

                foreach (var child in item.Children)
                {
                    UpdateCounts((GroupFilterTreeItem)child);
                }
            }

            void PruneEmptyHiddenItems(GroupFilterTreeItem item)
            {
                // Removes items that have no logs associated with them and are undesirable for some reason.

                var toRemove = new List <GroupFilterTreeItem>();

                foreach (var child in item.Children)
                {
                    var  groupTreeItem = (GroupFilterTreeItem)child;
                    bool removed       = false;

                    if (groupTreeItem.LogGroup is EncounterLogGroup encounterLogGroup)
                    {
                        if (HiddenEncounters.Contains(encounterLogGroup.Encounter) && groupTreeItem.LogCount == 0)
                        {
                            toRemove.Add(groupTreeItem);
                            removed = true;
                        }
                    }
                    else if (groupTreeItem.LogGroup is CategoryLogGroup categoryLogGroup)
                    {
                        if (HiddenCategories.Contains(categoryLogGroup.Category) && groupTreeItem.LogCount == 0)
                        {
                            toRemove.Add(groupTreeItem);
                            removed = true;
                        }
                    }

                    if (!removed)
                    {
                        PruneEmptyHiddenItems(groupTreeItem);
                    }
                }

                foreach (var childToRemove in toRemove)
                {
                    item.Children.Remove(childToRemove);
                }
            }

            UpdateIcons(rootItem);
            UpdateCounts(rootItem);
            PruneEmptyHiddenItems(rootItem);

            DataStore = new TreeGridItemCollection {
                rootItem
            };
        }