/// <summary>
 /// Constructor for an actual valid item.
 /// </summary>
 /// <param name="i"></param>
 public ItemTreeElement(ItemTreeElement itemParent, ItemTreeElement depencencyParent, IItem i, bool searchable = true)
 {
     CategoryParent = itemParent;
     SetParent      = depencencyParent;
     Item           = i;
     Searchable     = searchable;
     Children       = new ObservableCollection <ItemTreeElement>();
 }
 /// <summary>
 /// Constructor for an empty header cateogory
 /// </summary>
 /// <param name="name"></param>
 /// <param name="children"></param>
 public ItemTreeElement(ItemTreeElement itemParent, ItemTreeElement depencencyParent, string name, bool searchable = false)
 {
     CategoryParent = itemParent;
     SetParent      = depencencyParent;
     _backupName    = name;
     Searchable     = searchable;
     Children       = new ObservableCollection <ItemTreeElement>();
 }
        private async Task <List <IItem> > BuildCategoryTree()
        {
            foreach (var kv in _categoryStructure)
            {
                // Make the top level node.
                var e = new ItemTreeElement(null, null, kv.Key);

                foreach (var secondary in kv.Value)
                {
                    var e2 = new ItemTreeElement(e, null, secondary);
                    e.Children.Add(e2);
                }
                CategoryElements.Add(e);
            }

            var gameDir  = XivCache.GameInfo.GameDirectory;
            var language = XivCache.GameInfo.GameLanguage;

            var items = await XivCache.GetFullItemList();

            foreach (var item in items)
            {
                // Find what node we should be attached to.
                ItemTreeElement catParent = null;
                var             topLevel  = CategoryElements.FirstOrDefault(x => x.DisplayName == item.PrimaryCategory);
                if (topLevel == null)
                {
                    topLevel = new ItemTreeElement(null, null, item.PrimaryCategory);
                    CategoryElements.Add(topLevel);
                }

                var secondLevel = topLevel.Children.FirstOrDefault(x => x.DisplayName == item.SecondaryCategory);
                if (secondLevel == null)
                {
                    if (item.SecondaryCategory == item.Name)
                    {
                        // These are a special snowflake case.
                        secondLevel = topLevel;
                    }
                    else
                    {
                        secondLevel = new ItemTreeElement(topLevel, null, item.SecondaryCategory);
                        topLevel.Children.Add(secondLevel);
                    }
                }

                catParent = secondLevel;

                ItemTreeElement setParent = null;
                // Try and see if we have a valid root parent to attach to in the sets tree.
                try
                {
                    var type = item.GetType();
                    // Perf.  Much faster to just not test those types at all, as we know they won't resolve.
                    if (type != typeof(XivUi))
                    {
                        var itemRoot = item.GetRootInfo();
                        if (itemRoot.PrimaryType != XivItemType.unknown)
                        {
                            var st = itemRoot.ToString();
                            if (DependencyRootNodes.ContainsKey(st))
                            {
                                setParent = DependencyRootNodes[st];
                            }
                        }
                    }
                } catch (Exception ex)
                {
                    throw;
                }

                var e2 = new ItemTreeElement(catParent, setParent, item);
                if (catParent != null)
                {
                    catParent.Children.Add(e2);
                }
                if (setParent != null)
                {
                    setParent.Children.Add(e2);
                }
            }
            return(items);
        }
        /// <summary>
        /// This should only really be called directly if the control was created with DeferLoading set to true.
        /// </summary>
        /// <returns></returns>
        public async Task LoadItems()
        {
            if (_READY)
            {
                SearchTimer.Stop();
                SearchTimer.Dispose();
                ClearSelection();
            }

            if (LockUiFunction != null)
            {
                await LockUiFunction(UIStrings.Loading_Items, null, this);
            }


            // Pump us into another thread so the UI stays nice and fresh.
            await Task.Run(async() =>
            {
                CategoryElements    = new ObservableCollection <ItemTreeElement>();
                SetElements         = new ObservableCollection <ItemTreeElement>();
                DependencyRootNodes = new Dictionary <string, ItemTreeElement>();

                try
                {
                    // Gotta build set tree first, so the items from the item list can latch onto the nodes there.
                    BuildSetTree();
                    await BuildCategoryTree();
                }
                catch (Exception ex)
                {
                    FlexibleMessageBox.Show("An error occurred while loading the item list.\n" + ex.Message, "Item List Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Warning);
                    return;
                }


                var toAdd = new List <(ItemTreeElement parent, ItemTreeElement child)>();
                foreach (var kv in DependencyRootNodes)
                {
                    // This dependency root had no EXD-Items associated with it.
                    // Gotta make a generic item for it.
                    if (kv.Value.Children.Count == 0)
                    {
                        // See if we can actually turn this root into a fully fledged item.
                        try
                        {
                            var root = await XivCache.GetFirstRoot(kv.Key);
                            if (root != null)
                            {
                                // If we can, add it into the list.
                                var item = root.ToRawItem();
                                var e    = new ItemTreeElement(null, kv.Value, item);
                                toAdd.Add((kv.Value, e));
                            }
                            else
                            {
                                var e = new ItemTreeElement(null, kv.Value, "[Unsupported]");
                                toAdd.Add((kv.Value, e));
                            }
                        }
                        catch (Exception ex)
                        {
                            throw;
                        }
                    }
                }

                // Loop back through to add the new items, so we're not affecting the previous iteration.
                foreach (var tup in toAdd)
                {
                    tup.parent.Children.Add(tup.child);
                }
            });

            var view = (CollectionView)CollectionViewSource.GetDefaultView(CategoryElements);

            view.Filter = SearchFilter;


            view        = (CollectionView)CollectionViewSource.GetDefaultView(SetElements);
            view.Filter = SearchFilter;

            SearchTimer          = new Timer(300);
            SearchTimer.Elapsed += Search;

            CategoryTree.ItemsSource = CategoryElements;
            SetTree.ItemsSource      = SetElements;

            _READY = true;

            Search(this, null);

            if (UnlockUiFunction != null)
            {
                await UnlockUiFunction(this);
            }

            if (ItemsLoaded != null)
            {
                ItemsLoaded.Invoke(this, null);
            }
        }
        /// <summary>
        /// Builds the set tree from XivCache.GetAllRoots().
        /// </summary>
        private void BuildSetTree()
        {
            // First we must generate all the dependency root nodes.
            var roots               = XivCache.GetAllRootsDictionary();
            var primaryTypeGroups   = new Dictionary <XivItemType, ItemTreeElement>();
            var primaryIdGroups     = new Dictionary <XivItemType, Dictionary <int, ItemTreeElement> >();
            var secondaryTypeGroups = new Dictionary <XivItemType, Dictionary <int, Dictionary <XivItemType, ItemTreeElement> > >();
            var secondaryIdGroups   = new Dictionary <XivItemType, Dictionary <int, Dictionary <XivItemType, Dictionary <int, ItemTreeElement> > > >();

            // This giant for loop monstrosity builds the actual root nodes based on the dictionaries returned by XivCache.GetAllRoots()
            foreach (var kvPrimaryType in roots)
            {
                var primaryType = kvPrimaryType.Key;

                // Create the new node.
                primaryTypeGroups.Add(primaryType, new ItemTreeElement(null, null, XivItemTypes.NiceNames[primaryType], true));

                // Add us to parent.
                SetElements.Add(primaryTypeGroups[primaryType]);

                // Ensure the other lists have our primary type reference.
                primaryIdGroups.Add(primaryType, new Dictionary <int, ItemTreeElement>());
                secondaryTypeGroups.Add(primaryType, new Dictionary <int, Dictionary <XivItemType, ItemTreeElement> >());
                secondaryIdGroups.Add(primaryType, new Dictionary <int, Dictionary <XivItemType, Dictionary <int, ItemTreeElement> > >());

                foreach (var kvPrimaryId in kvPrimaryType.Value)
                {
                    var primaryId = kvPrimaryId.Key;

                    // Create the new node.
                    primaryIdGroups[primaryType].Add(primaryId, new ItemTreeElement(null, primaryTypeGroups[primaryType], XivItemTypes.GetSystemPrefix(primaryType) + primaryId.ToString().PadLeft(4, '0'), true));

                    // Add us to parent.
                    primaryTypeGroups[primaryType].Children.Add(primaryIdGroups[primaryType][primaryId]);

                    // Ensure the other lists have our primary id reference.
                    secondaryTypeGroups[primaryType].Add(primaryId, new Dictionary <XivItemType, ItemTreeElement>());
                    secondaryIdGroups[primaryType].Add(primaryId, new Dictionary <XivItemType, Dictionary <int, ItemTreeElement> >());

                    foreach (var kvSecondaryType in kvPrimaryId.Value)
                    {
                        var secondaryType = kvSecondaryType.Key;

                        if (secondaryType != XivItemType.none)
                        {
                            // Create the new node.
                            secondaryTypeGroups[primaryType][primaryId].Add(secondaryType, new ItemTreeElement(null, primaryIdGroups[primaryType][primaryId], XivItemTypes.NiceNames[secondaryType], true));

                            // Add us to parent.
                            primaryIdGroups[primaryType][primaryId].Children.Add(secondaryTypeGroups[primaryType][primaryId][secondaryType]);

                            // Ensure the other lists have our secondary type reference.
                            secondaryIdGroups[primaryType][primaryId].Add(secondaryType, new Dictionary <int, ItemTreeElement>());
                        }

                        foreach (var kvSecondaryId in kvSecondaryType.Value)
                        {
                            var secondaryId = kvSecondaryId.Key;

                            if (secondaryType != XivItemType.none)
                            {
                                // Create the new node.
                                secondaryIdGroups[primaryType][primaryId][secondaryType].Add(secondaryId, new ItemTreeElement(null, secondaryTypeGroups[primaryType][primaryId][secondaryType], XivItemTypes.GetSystemPrefix(secondaryType) + secondaryId.ToString().PadLeft(4, '0'), true));

                                // Add us to parent.
                                secondaryTypeGroups[primaryType][primaryId][secondaryType].Children.Add(secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId]);
                            }

                            foreach (var kvSlot in kvSecondaryId.Value)
                            {
                                var root = kvSlot.Value;


                                var slotName = Mdl.SlotAbbreviationDictionary.FirstOrDefault(x => x.Value == root.Slot).Key;

                                if (secondaryType != XivItemType.none)
                                {
                                    // This root has no slots, just list the parent as the root element.
                                    if (String.IsNullOrWhiteSpace(slotName))
                                    {
                                        DependencyRootNodes.Add(root.ToString(), secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId]);
                                        break;
                                    }

                                    // Create the new node.
                                    var elem = new ItemTreeElement(null, secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId], slotName);

                                    // Add us to parent.
                                    secondaryIdGroups[primaryType][primaryId][secondaryType][secondaryId].Children.Add(elem);

                                    // Save us to the primary listing so the items can list themselves under us.
                                    DependencyRootNodes.Add(root.ToString(), elem);
                                }
                                else
                                {
                                    // This root has no slots, just list the parent as the root element.
                                    if (String.IsNullOrWhiteSpace(slotName))
                                    {
                                        DependencyRootNodes.Add(root.ToString(), primaryIdGroups[primaryType][primaryId]);
                                        break;
                                    }

                                    // Create the new node.
                                    var elem = new ItemTreeElement(null, primaryIdGroups[primaryType][primaryId], slotName);

                                    // Add us to parent.
                                    primaryIdGroups[primaryType][primaryId].Children.Add(elem);

                                    // Save us to the primary listing so the items can list themselves under us.
                                    DependencyRootNodes.Add(root.ToString(), elem);
                                }
                            }
                        }
                    }
                }
            }
        }