/// <summary>
        /// Refreshes the available mass of each element in the POI.
        /// </summary>
        /// <param name="panel">The parent info panel.</param>
        /// <param name="harvestable">The POI to be harvested.</param>
        private void RefreshElements(SpacePOISimpleInfoPanel panel, HarvestablePOIStates.
                                     Instance harvestable)
        {
            var elementRows = panel.elementRows;

            if (harvestable != null)
            {
                // Shared dictionary, does not allocate
                var   weightedElements = harvestable.configuration.GetElementsWithWeights();
                float total            = 0.0f;
                foreach (var pair in weightedElements)
                {
                    float mass = pair.Value;
                    total += mass;
                }
                // Avoid NaN
                total = Math.Max(total, 0.01f);
                foreach (var pair in weightedElements)
                {
                    Tag elementTag = new Tag(pair.Key.ToString());
                    // It is already visible, and in the correct order
                    if (elementRows.TryGetValue(elementTag, out GameObject row) && row.
                        TryGetComponent(out HierarchyReferences hr))
                    {
                        string text = GameUtil.GetFormattedPercent(pair.Value * 100.0f /
                                                                   total);
                        var label = hr.GetReference <LocText>("ValueLabel");
                        if (label.text != text)
                        {
                            label.SetText(text);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Initializes the available mass by element display.
        /// </summary>
        /// <param name="panel">The parent info panel.</param>
        /// <param name="harvestable">The POI to be harvested.</param>
        /// <param name="details">The panel to refresh.</param>
        private void InitElements(SpacePOISimpleInfoPanel panel, HarvestablePOIStates.
                                  Instance harvestable, CollapsibleDetailContentPanel details)
        {
            var elementRows  = panel.elementRows;
            var existingRows = DictionaryPool <Tag, GameObject, SpacePOISimpleInfoPanel> .
                               Allocate();

            foreach (var pair in panel.elementRows)
            {
                existingRows[pair.Key] = pair.Value;
            }
            if (harvestable != null)
            {
                // Shared dictionary, does not allocate
                var sortedElements = ListPool <ElementWeight, SpacePOISimpleInfoPanel> .
                                     Allocate();

                sortedElements.AddRange(harvestable.configuration.GetElementsWithWeights());
                sortedElements.Sort(SortElementsByMassComparer.Instance);
                int n = sortedElements.Count;
                for (int i = 0; i < n; i++)
                {
                    var       pair       = sortedElements[i];
                    SimHashes element    = pair.Key;
                    Tag       elementTag = new Tag(element.ToString());
                    if (!elementRows.TryGetValue(elementTag, out GameObject row))
                    {
                        row = Util.KInstantiateUI(panel.simpleInfoRoot.iconLabelRow, details.
                                                  Content.gameObject, true);
                        elementRows[elementTag] = row;
                    }
                    else
                    {
                        row.SetActive(true);
                    }
                    if (row.TryGetComponent(out HierarchyReferences hr))
                    {
                        var uiSprite = Def.GetUISprite(elementTag, "ui", false);
                        var icon     = hr.GetReference <Image>("Icon");
                        if (uiSprite != null)
                        {
                            icon.sprite = uiSprite.first;
                            icon.color  = uiSprite.second;
                        }
                        hr.GetReference <LocText>("NameLabel").SetText(ElementLoader.
                                                                       FindElementByHash(element).name);
                        hr.GetReference <LocText>("ValueLabel").alignment = TMPro.
                                                                            TextAlignmentOptions.MidlineRight;
                    }
                    existingRows.Remove(elementTag);
                }
                sortedElements.Recycle();
            }
            // Turn off all rows that were not turned on
            foreach (var pair in existingRows)
            {
                pair.Value.SetActive(false);
            }
            existingRows.Recycle();
        }
        /// <summary>
        /// Refreshes the space POI info panel.
        /// </summary>
        /// <param name="panel">The parent info panel.</param>
        /// <param name="details">The panel to refresh.</param>
        /// <param name="target">The currently selected object.</param>
        public void Refresh(SpacePOISimpleInfoPanel panel,
                            CollapsibleDetailContentPanel details, GameObject target)
        {
            bool active = false;

            if (target != null)
            {
                bool changed = lastSelection.lastTarget != target;
                if (changed)
                {
                    lastSelection = new LastSelectionDetails(target);
                    if (panel.massHeader == null)
                    {
                        Init(panel, details);
                    }
                }
                var asteroidField = lastSelection.asteroidField;
                var artifact      = lastSelection.artifact;
                var harvestable   = lastSelection.harvestable;
                if (asteroidField != null || artifact != null)
                {
                    if (changed)
                    {
                        InitElements(panel, harvestable, details);
                        panel.artifactsSpacer.transform.SetAsLastSibling();
                        panel.artifactRow.transform.SetAsLastSibling();
                    }
                    if (harvestable != null && massLabel != null)
                    {
                        RefreshMassHeader(harvestable);
                    }
                    RefreshElements(panel, harvestable);
                    if (artifact != null)
                    {
                        RefreshArtifacts(panel, artifact);
                    }
                    if (!wasActive)
                    {
                        details.gameObject.SetActive(true);
                    }
                    active    = true;
                    wasActive = true;
                }
            }
            if (!active && wasActive)
            {
                details.gameObject.SetActive(false);
                wasActive = false;
            }
        }
        /// <summary>
        /// Performs one-time initialization.
        /// </summary>
        /// <param name="panel">The parent info panel.</param>
        /// <param name="details">The panel to initialize.</param>
        private void Init(SpacePOISimpleInfoPanel panel, CollapsibleDetailContentPanel details)
        {
            var root   = panel.simpleInfoRoot;
            var parent = details.Content.gameObject;
            var header = Util.KInstantiateUI(root.iconLabelRow, parent, true);

            panel.massHeader = header;
            details.SetTitle(STRINGS.UI.CLUSTERMAP.POI.TITLE);
            // Set the icon, name and alignment once
            if (header.TryGetComponent(out HierarchyReferences hr))
            {
                var sprite = Assets.GetSprite("icon_asteroid_type");
                if (sprite != null)
                {
                    hr.GetReference <Image>("Icon").sprite = sprite;
                }
                hr.GetReference <LocText>("NameLabel").SetText(STRINGS.UI.CLUSTERMAP.POI.
                                                               MASS_REMAINING);
                massLabel           = hr.GetReference <LocText>("ValueLabel");
                massLabel.alignment = TMPro.TextAlignmentOptions.MidlineRight;
            }
            else
            {
                massLabel = null;
            }
            if (panel.artifactsSpacer == null)
            {
                var ar = Util.KInstantiateUI(root.iconLabelRow, parent, true);
                panel.artifactsSpacer = Util.KInstantiateUI(root.spacerRow, parent, true);
                panel.artifactRow     = ar;
                if (ar.TryGetComponent(out hr))
                {
                    var icon = hr.GetReference <Image>("Icon");
                    // Triggers an Init if necessary
                    hr.GetReference <LocText>("NameLabel").SetText(STRINGS.UI.CLUSTERMAP.POI.
                                                                   ARTIFACTS);
                    hr.GetReference <LocText>("ValueLabel").alignment = TMPro.
                                                                        TextAlignmentOptions.MidlineRight;
                    icon.sprite = Assets.GetSprite("ic_artifacts");
                    icon.color  = Color.black;
                }
            }
        }
 /// <summary>
 /// Refreshes the available artifact.
 /// </summary>
 /// <param name="panel">The parent info panel.</param>
 /// <param name="harvestable">The artifact to be harvested.</param>
 private void RefreshArtifacts(SpacePOISimpleInfoPanel panel, ArtifactPOIStates.
                               Instance artifact)
 {
     if (panel.artifactRow.TryGetComponent(out HierarchyReferences hr))
     {
         string text;
         var    label = hr.GetReference <LocText>("ValueLabel");
         // Triggers an Init if necessary
         artifact.configuration.GetArtifactID();
         if (artifact.CanHarvestArtifact())
         {
             text = STRINGS.UI.CLUSTERMAP.POI.ARTIFACTS_AVAILABLE;
         }
         else
         {
             text = STRINGS.UI.CLUSTERMAP.POI.ARTIFACTS_DEPLETED.Format(GameUtil.
                                                                        GetFormattedCycles(artifact.RechargeTimeRemaining(), "F1", true));
         }
         if (label.text != text)
         {
             label.SetText(text);
         }
     }
 }