protected void OnContractOffered(Contract c)
        {
            LoggingUtil.LogVerbose(this, "OnContractOffered: " + c.Title);
            if (MissionControl.Instance == null)
            {
                return;
            }

            // Check we're in the right mode
            if (!displayModeAll || MissionControl.Instance.displayMode != MissionControl.DisplayMode.Available)
            {
                if (MissionControl.Instance.displayMode != MissionControl.DisplayMode.Available)
                {
                    MissionControl.Instance.ClearInfoPanel();
                    MissionControl.Instance.panelView.gameObject.SetActive(false);
                    MissionControl.Instance.RebuildContractList();
                }
                else
                {
                    OnClickAvailable(true);
                    OnSelectContract(selectedButton, UIRadioButton.CallType.USER, null);
                }
                return;
            }

            ConfiguredContract cc = c as ConfiguredContract;
            ContractContainer container = null;
            if (cc != null)
            {
                ContractContainer foundMatch = null;

                List<UIListData<KSP.UI.UIListItem>>.Enumerator enumerator = MissionControl.Instance.scrollListContracts.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    KSP.UI.UIListItem item = enumerator.Current.listItem;
                    container = item.Data as ContractContainer;
                    if (container != null)
                    {
                        if (container.contractType == cc.contractType)
                        {
                            // Upgrade the contract type line item to a contract
                            if (container.contract == null)
                            {
                                container.contract = cc;
                                SetupContractItem(container);

                                // Check if the selection was this contract type
                                if (selectedButton != null)
                                {
                                    ContractContainer contractContainer = selectedButton.GetComponent<KSP.UI.UIListItem>().Data as ContractContainer;
                                    if (contractContainer != null && contractContainer == container)
                                    {
                                        OnSelectContract(selectedButton, UIRadioButton.CallType.USER, null);
                                    }
                                }

                                break;
                            }
                            // Keep track of the list item - we'll add immediately after it
                            else
                            {
                                foundMatch = container;
                            }
                        }
                        continue;
                    }
                }

                // Got a match, do an addition
                if (foundMatch != null)
                {
                    container = new ContractContainer(cc);
                    container.parent = foundMatch.parent;
                    container.parent.childContracts.Add(container);
                    CreateContractItem(container, foundMatch.indent, foundMatch.mcListItem.container);

                    // Show/hide based on state of group line
                    container.mcListItem.gameObject.SetActive(container.parent.expanded && container.parent.mcListItem.gameObject.activeSelf);
                }
            }
            else
            {
                List<UIListData<KSP.UI.UIListItem>>.Enumerator enumerator = MissionControl.Instance.scrollListContracts.GetEnumerator();
                Type contractType = c.GetType();
                GroupContainer closestGroup = null;
                ContractContainer lastEntry = null;
                ContractContainer foundMatch = null;
                while (enumerator.MoveNext())
                {
                    KSP.UI.UIListItem item = enumerator.Current.listItem;
                    // Check for a group to use for the position later if we need to add a group
                    GroupContainer groupContainer = item.Data as GroupContainer;
                    if (groupContainer != null)
                    {
                        if (closestGroup == null || string.Compare(groupContainer.DisplayName(), GroupContainer.DisplayName(contractType)) < 0)
                        {
                            closestGroup = groupContainer;
                        }
                    }

                    ContractContainer otherContainer = item.Data as ContractContainer;
                    if (otherContainer != null)
                    {
                        // Track the last entry in case we need to do a group insert
                        if (otherContainer.root == closestGroup)
                        {
                            lastEntry = otherContainer;
                        }

                        if (otherContainer.parent != null && otherContainer.parent.stockContractType == contractType)
                        {
                            foundMatch = otherContainer;
                        }
                    }
                }

                // Add the new item to the end of the grouping
                if (foundMatch != null)
                {
                    container = new ContractContainer(c);
                    container.parent = foundMatch.parent;
                    container.parent.childContracts.Add(container);
                    CreateContractItem(container, foundMatch.indent, foundMatch.mcListItem.container);

                    // Show/hide based on state of group line
                    container.mcListItem.gameObject.SetActive(container.parent.expanded && container.parent.mcListItem.gameObject.activeSelf);
                }
                // Need to create a group if it wasn't found (this will create the contract too
                else
                {
                    GroupContainer groupContainer = new GroupContainer(contractType);
                    CreateGroupItem(groupContainer, 0, lastEntry.mcListItem.container);
                }
            }

            // Update group details
            if (container != null)
            {
                SetupParentGroups(container);
            }
        }
        protected void CreateContractItem(ContractContainer cc, int indent = 0, KSP.UI.UIListItem previous = null)
        {
            // Set up list item
            MCListItem mcListItem = UnityEngine.Object.Instantiate<MCListItem>(MissionControl.Instance.PrfbMissionListItem);
            mcListItem.logoSprite.gameObject.SetActive(false);
            mcListItem.container.Data = cc;
            cc.mcListItem = mcListItem;
            cc.indent = indent;

            // Set up the radio button to the custom sprites for contracts
            UIRadioButton radioButton = mcListItem.GetComponent<UIRadioButton>();
            radioButton.stateTrue.normal = radioButton.stateTrue.highlight = radioButton.stateTrue.pressed = radioButton.stateTrue.disabled = itemEnabled;
            radioButton.stateFalse.normal = radioButton.stateFalse.highlight = radioButton.stateFalse.pressed = radioButton.stateFalse.disabled = itemDisabled;
            mcListItem.GetComponent<Image>().sprite = itemDisabled;

            // Fix up the position/sizing of the text element
            GameObject textObject = mcListItem.gameObject.GetChild("Text");
            RectTransform textRect = textObject.GetComponent<RectTransform>();
            textRect.anchoredPosition = new Vector2(textRect.anchoredPosition.x - 60, textRect.anchoredPosition.y);
            textRect.sizeDelta = new Vector2(textRect.sizeDelta.x + 60 - 20, textRect.sizeDelta.y);

            // Set up the difficulty/prestige stars
            mcListItem.difficulty.states[0].sprite = prestigeSprites[0];
            mcListItem.difficulty.states[1].sprite = prestigeSprites[1];
            mcListItem.difficulty.states[2].sprite = prestigeSprites[2];

            // Create an icon to show the status
            GameObject statusImage = new GameObject("StatusImage");
            cc.statusRect = statusImage.AddComponent<RectTransform>();
            cc.statusRect.anchoredPosition = new Vector2(16.0f, 0f);
            cc.statusRect.anchorMin = new Vector2(0, 0.5f);
            cc.statusRect.anchorMax = new Vector2(0, 0.5f);
            cc.statusRect.sizeDelta = new Vector2(10f, 10f);
            statusImage.AddComponent<CanvasRenderer>();
            cc.statusImage = statusImage.AddComponent<UIStateImage>();
            cc.statusImage.states = itemStatusStates;
            cc.statusImage.image = statusImage.AddComponent<Image>();
            statusImage.transform.SetParent(mcListItem.transform);

            // Finalize difficulty UI
            RectTransform diffRect = mcListItem.difficulty.GetComponent<RectTransform>();
            diffRect.anchoredPosition = new Vector2(-20.5f, -12.5f);
            diffRect.sizeDelta = new Vector2(35, 11);

            // Set the callbacks
            mcListItem.radioButton.onFalseBtn.AddListener(new UnityAction<UIRadioButton, UIRadioButton.CallType, PointerEventData>(OnDeselectContract));
            mcListItem.radioButton.onTrueBtn.AddListener(new UnityAction<UIRadioButton, UIRadioButton.CallType, PointerEventData>(OnSelectContract));

            // Do other setup
            SetupContractItem(cc);

            // Add the list item to the UI, and add indent
            if (previous == null)
            {
                MissionControl.Instance.scrollListContracts.AddItem(mcListItem.container, true);

                cc.listItemTransform = mcListItem.transform;
                SetIndent(mcListItem, indent);
            }
            else
            {
                InsertIntoList(cc, indent, previous);
            }

            // Create as unexpanded
            if (indent != 0)
            {
                mcListItem.gameObject.SetActive(false);
            }
        }
        public void OnClickAvailable(bool selected)
        {
            LoggingUtil.LogVerbose(this, "OnClickAvailable");

            if (!selected)
            {
                return;
            }

            // Set the state on the MissionControl object
            MissionControl.Instance.displayMode = MissionControl.DisplayMode.Available;
            MissionControl.Instance.toggleArchiveGroup.gameObject.SetActive(false);
            MissionControl.Instance.scrollListContracts.Clear(true);
            for (int i = MissionControl.Instance.scrollListContracts.transform.childCount; i-- > 0;)
            {
                Destroy(MissionControl.Instance.scrollListContracts.transform.GetChild(i).gameObject);
            }

            foreach (Contract contract in ContractSystem.Instance.Contracts.Union(ContractPreLoader.Instance.PendingContracts().OfType<Contract>()).
                Where(c => c.ContractState == Contract.State.Offered).
                OrderBy(c => GroupContainer.OrderKey(c)))
            {
                // Setup list item and contract container
                MCListItem mcListItem = UnityEngine.Object.Instantiate<MCListItem>(MissionControl.Instance.PrfbMissionListItem);
                ContractContainer cc = new ContractContainer(contract);
                mcListItem.container.Data = cc;
                cc.mcListItem = mcListItem;
                cc.missionSelection = new MissionControl.MissionSelection(true, cc.contract, cc.mcListItem.container);
                mcListItem.radioButton.onFalseBtn.AddListener(new UnityAction<UIRadioButton, UIRadioButton.CallType, PointerEventData>(OnDeselectContract));
                mcListItem.radioButton.onTrueBtn.AddListener(new UnityAction<UIRadioButton, UIRadioButton.CallType, PointerEventData>(OnSelectContract));
                mcListItem.Setup(contract, "<color=#fefa87>" + contract.Title + "</color>");
                MissionControl.Instance.scrollListContracts.AddItem(mcListItem.container, true);

                if (MissionControl.Instance.selectedMission != null && MissionControl.Instance.selectedMission.contract == contract)
                {
                    mcListItem.radioButton.SetState(UIRadioButton.State.True, UIRadioButton.CallType.APPLICATION, null, false);
                }
            }

            HighLogic.CurrentGame.Parameters.CustomParams<ContractConfiguratorParameters>().lastMCButton =
                ContractConfiguratorParameters.MissionControlButton.Available;
            displayModeAll = false;
        }
 protected void SetupParentGroups(ContractContainer contractContainer)
 {
     for (GroupContainer groupContainer = contractContainer.parent; groupContainer != null; groupContainer = groupContainer.parent)
     {
         SetupGroupItem(groupContainer);
     }
 }
        protected void SetupContractItem(ContractContainer cc)
        {
            // Set up the list item with the contract details
            SetContractTitle(cc.mcListItem, cc);

            // Add callback data
            cc.missionSelection = new MissionControl.MissionSelection(true, cc.contract, cc.mcListItem.container);

            // Setup with contract
            if (cc.contract != null)
            {
                // Set difficulty
                cc.mcListItem.difficulty.gameObject.SetActive(true);
                cc.mcListItem.difficulty.SetState((int)cc.contract.Prestige);

                // Set status
                cc.statusImage.SetState(cc.contract.ContractState == Contract.State.Active ? "Active" : cc.contract.ContractState == Contract.State.Completed ? "Completed" : "Offered");
            }
            // Setup without contract
            else
            {
                // Set difficulty
                Contract.ContractPrestige? prestige = GetPrestige(cc.contractType);
                if (prestige != null)
                {
                    cc.mcListItem.difficulty.SetState((int)prestige.Value);
                }
                else
                {
                    cc.mcListItem.difficulty.gameObject.SetActive(false);
                }

                // Set status
                cc.statusImage.SetState(cc.contractType.maxCompletions != 0 && cc.contractType.ActualCompletions() >= cc.contractType.maxCompletions ? "Completed" : "Unavailable");
            }
        }
        protected void SetContractTitle(MCListItem mcListItem, ContractContainer cc)
        {
            // Set up the list item with the contract details
            string color = cc.contract == null ? "A9A9A9" : cc.contract.ContractState == Contract.State.Active ? "96df41" : "fefa87";
            string title = "";
            if (cc.contract != null)
            {
                title = cc.contract.Title;
            }
            else
            {
                title = cc.contractType.genericTitle;

                // Special case for one-off contracts
                if (cc.contractType.maxCompletions == 1)
                {
                    foreach (ConfiguredContract c in ConfiguredContract.CompletedContracts)
                    {
                        if (c.contractType != null && c.contractType.name == cc.contractType.name)
                        {
                            title = c.Title;
                            break;
                        }
                    }
                }
            }

            mcListItem.title.text = StringBuilderCache.Format("<color=#{0}>{1}</color>", color, title);
            if (cc.contract != null && cc.contract.ContractViewed != Contract.Viewed.Read)
            {
                mcListItem.title.text = StringBuilderCache.Format("<b>{0}</b>", mcListItem.title.text);
            }

            if (displayModeAll)
            {
                float preferredHeight = mcListItem.title.GetPreferredValues(mcListItem.title.text, 316 - cc.indent * 12 - 64, TMPro.TMP_Math.FLOAT_MAX).y;
                bool twoLines = preferredHeight > 14;
                mcListItem.GetComponent<LayoutElement>().preferredHeight = twoLines ? 38 : 25;
                if (cc.statusRect != null)
                {
                    cc.statusRect.anchoredPosition = new Vector2(16.0f, 0f);
                }
            }

            // Setup prestige
            if (cc.contract != null)
            {
                mcListItem.difficulty.SetState((int)cc.contract.Prestige);
            }
            else
            {
                // Set difficulty
                Contract.ContractPrestige? prestige = GetPrestige(cc.contractType);
                if (prestige != null)
                {
                    cc.mcListItem.difficulty.SetState((int)prestige.Value);
                }
                else
                {
                    cc.mcListItem.difficulty.gameObject.SetActive(false);
                }
            }
        }