using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

namespace Barotrauma
{
    class CampaignUI
    {
        private CampaignMode.InteractionType selectedTab;

        private GUIFrame[] tabs;

        public CampaignMode.InteractionType SelectedTab => selectedTab;

        private Point prevResolution;

        private GUIComponent locationInfoPanel;

        private GUIListBox missionList;
        private readonly List<GUITickBox> missionTickBoxes = new List<GUITickBox>();
        private readonly List<GUITextBlock> missionRewardTexts = new List<GUITextBlock>();

        private bool hasMaxMissions;

        private SubmarineSelection submarineSelection;

        private Location selectedLocation;

        public Action StartRound;

        public LevelData SelectedLevel { get; private set; }
                
        private GUIButton StartButton { get; set; }

        public CampaignMode Campaign { get; }

        public HRManagerUI HRManagerUI { get; set; }

        public Store Store { get; private set; }

        public UpgradeStore UpgradeStore { get; set; }

        public MedicalClinicUI MedicalClinic { get; set; }

        public CampaignUI(CampaignMode campaign, GUIComponent container)
        {
            Campaign = campaign;

            if (campaign.Map == null) { throw new InvalidOperationException("Failed to create campaign UI (campaign map was null)."); }
            if (campaign.Map.CurrentLocation == null) { throw new InvalidOperationException("Failed to create campaign UI (current location not set)."); }

            CreateUI(container);

            campaign.Map.OnLocationSelected = SelectLocation;
            campaign.Map.OnMissionsSelected = (connection, missions) => 
            {
                if (missionList?.Content != null)
                {
                    foreach (GUIComponent missionElement in missionList.Content.Children)
                    {
                        if (missionElement.FindChild(c => c is GUITickBox, recursive: true) is GUITickBox tickBox) 
                        { 
                            tickBox.Selected = missions.Contains(tickBox.UserData as Mission); 
                        }
                    }
                }
            };
        }

        private void CreateUI(GUIComponent container)
        {
            container.ClearChildren();

            tabs = new GUIFrame[Enum.GetValues(typeof(CampaignMode.InteractionType)).Length];

            // map tab -------------------------------------------------------------------------

            tabs[(int)CampaignMode.InteractionType.Map] = CreateDefaultTabContainer(container, new Vector2(0.9f));
            var mapFrame = new GUIFrame(new RectTransform(Vector2.One, GetTabContainer(CampaignMode.InteractionType.Map).RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f);
            var mapContainer = new GUICustomComponent(new RectTransform(Vector2.One, mapFrame.RectTransform), DrawMap, UpdateMap);
            var notificationFrame = new GUIFrame(new RectTransform(new Point(mapContainer.Rect.Width, GUI.IntScale(40)), mapContainer.RectTransform, Anchor.BottomCenter), style: "ChatBox");
            
            new GUIFrame(new RectTransform(Vector2.One, mapFrame.RectTransform), style: "InnerGlow", color: Color.Black * 0.9f)
            {
                CanBeFocused = false
            };            
            
            var notificationContainer = new GUICustomComponent(new RectTransform(new Vector2(0.98f, 1.0f), notificationFrame.RectTransform, Anchor.Center), DrawMapNotifications, null)
            {
                HideElementsOutsideFrame = true
            };
            var notificationHeader = new GUIImage(new RectTransform(new Vector2(0.1f, 1.0f), notificationFrame.RectTransform, Anchor.CenterLeft), style: "GUISlopedHeaderRight");
            var text = new GUITextBlock(new RectTransform(Vector2.One, notificationHeader.RectTransform, Anchor.Center), TextManager.Get("breakingnews"), font: GUIStyle.LargeFont);
            notificationHeader.RectTransform.MinSize = new Point((int)(text.TextSize.X * 1.3f), 0);

            // crew tab -------------------------------------------------------------------------

            var crewTab = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
            tabs[(int)CampaignMode.InteractionType.Crew] = crewTab;
            HRManagerUI = new HRManagerUI(this, crewTab);

            // store tab -------------------------------------------------------------------------
            
            var storeTab = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
            tabs[(int)CampaignMode.InteractionType.Store] = storeTab;
            Store = new Store(this, storeTab);

            // upgrade tab -------------------------------------------------------------------------

            tabs[(int)CampaignMode.InteractionType.Upgrade] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
            UpgradeStore = new UpgradeStore(this, GetTabContainer(CampaignMode.InteractionType.Upgrade));

            // Submarine buying tab
            tabs[(int)CampaignMode.InteractionType.PurchaseSub] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform, Anchor.TopLeft), color: Color.Black * 0.9f);

            tabs[(int)CampaignMode.InteractionType.MedicalClinic] = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
            MedicalClinic = new MedicalClinicUI(Campaign.MedicalClinic, GetTabContainer(CampaignMode.InteractionType.MedicalClinic));

            // mission info -------------------------------------------------------------------------

            locationInfoPanel = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.75f), GetTabContainer(CampaignMode.InteractionType.Map).RectTransform, Anchor.CenterRight)
            { RelativeOffset = new Vector2(0.02f, 0.0f) }, 
                color: Color.Black)
            {
                Visible = false
            };

            // -------------------------------------------------------------------------

            SelectTab(CampaignMode.InteractionType.Map);

            prevResolution = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
        }

        private GUIFrame CreateDefaultTabContainer(GUIComponent container, Vector2 frameSize, bool visible = true)
        {
            var innerFrame = new GUIFrame(new RectTransform(frameSize, container.RectTransform, Anchor.Center))
            {
                Visible = visible
            };
            new GUIFrame(new RectTransform(innerFrame.Rect.Size - GUIStyle.ItemFrameMargin, innerFrame.RectTransform, Anchor.Center), style: null)
            {
                UserData = "container"
            };
            return innerFrame;
        }

        public GUIComponent GetTabContainer(CampaignMode.InteractionType tab)
        {
            var tabFrame = tabs[(int)tab];
            return tabFrame?.GetChildByUserData("container") ?? tabFrame;
        }

        private void DrawMap(SpriteBatch spriteBatch, GUICustomComponent mapContainer)
        {
            if (GameMain.GraphicsWidth != prevResolution.X || GameMain.GraphicsHeight != prevResolution.Y)
            {
                CreateUI(tabs[(int)CampaignMode.InteractionType.Map].Parent);
            }

            Campaign?.Map?.Draw(Campaign, spriteBatch, mapContainer);
        }

        private void DrawMapNotifications(SpriteBatch spriteBatch, GUICustomComponent notificationContainer)
        {
            Campaign?.Map?.DrawNotifications(spriteBatch, notificationContainer);
        }

        private void UpdateMap(float deltaTime, GUICustomComponent mapContainer)
        {
            var map = Campaign?.Map;
            if (map == null) { return; }
            if (selectedLocation != null && selectedLocation == Campaign.GetCurrentDisplayLocation())
            {
                map.SelectLocation(-1);
            }
            map.Update(Campaign, deltaTime, mapContainer);
            foreach (GUITickBox tickBox in missionTickBoxes)
            {
                bool disable = hasMaxMissions && !tickBox.Selected;
                tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && !disable;
                tickBox.Box.DisabledColor = disable ? tickBox.Box.Color * 0.5f : tickBox.Box.Color * 0.8f;
                foreach (GUIComponent child in tickBox.Parent.Parent.Children)
                {
                    if (child is GUITextBlock textBlock)
                    {
                        textBlock.SelectedTextColor = textBlock.HoverTextColor = textBlock.TextColor = 
                            disable ? new Color(textBlock.TextColor, 0.5f) : new Color(textBlock.TextColor, 1.0f);
                    }
                }
            }
        }

        public void Update(float deltaTime)
        {
            switch (SelectedTab)
            {
                case CampaignMode.InteractionType.PurchaseSub:
                    submarineSelection?.Update();
                    break;
                case CampaignMode.InteractionType.Crew:
                    HRManagerUI?.Update();
                    break;
                case CampaignMode.InteractionType.Store:
                    Store?.Update(deltaTime);
                    break;                
                case CampaignMode.InteractionType.MedicalClinic:
                    MedicalClinic?.Update(deltaTime);
                    break;
                case CampaignMode.InteractionType.Map:
                    if (StartButton != null)
                    {
                        StartButton.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && Character.Controlled is { IsIncapacitated: false };
                    }
                    break;
            }
        }

        public void RefreshLocationInfo()
        {
            if (selectedLocation != null && Campaign?.Map?.SelectedConnection != null)
            {
                SelectLocation(selectedLocation, Campaign.Map.SelectedConnection);
            }
        }

        public void SelectLocation(Location location, LocationConnection connection)
        {
            missionTickBoxes.Clear();
            missionRewardTexts.Clear();
            locationInfoPanel.ClearChildren();
            //don't select the map panel if we're looking at some other tab
            if (selectedTab == CampaignMode.InteractionType.Map)
            {
                SelectTab(CampaignMode.InteractionType.Map);
                locationInfoPanel.Visible = location != null;
            }

            Location prevSelectedLocation = selectedLocation;
            float prevMissionListScroll = missionList?.BarScroll ?? 0.0f;

            selectedLocation = location;
            if (location == null) { return; }

            int padding = GUI.IntScale(20);

            var content = new GUILayoutGroup(new RectTransform(locationInfoPanel.Rect.Size - new Point(padding * 2), locationInfoPanel.RectTransform, Anchor.Center), childAnchor: Anchor.TopRight)
            {
                Stretch = true,
                RelativeSpacing = 0.02f,
            };

            new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.DisplayName, font: GUIStyle.LargeFont)
            {
                AutoScaleHorizontal = true
            };
            new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), location.GetLocationTypeToDisplay().Name, font: GUIStyle.SubHeadingFont);

            Sprite portrait = location.Type.GetPortrait(location.PortraitId);
            portrait.EnsureLazyLoaded();

            var portraitContainer = new GUICustomComponent(new RectTransform(new Vector2(1.0f, 0.3f), content.RectTransform), onDraw: (sb, customComponent) =>
            {
                portrait.Draw(sb, customComponent.Rect.Center.ToVector2(), Color.Gray, portrait.size / 2, scale: Math.Max(customComponent.Rect.Width / portrait.size.X, customComponent.Rect.Height / portrait.size.Y));
            })
            {
                HideElementsOutsideFrame = true
            };

            var textContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), portraitContainer.RectTransform, Anchor.Center))
            {
                RelativeSpacing = 0.05f
            };

            if (connection?.LevelData != null)
            {
                if (location.Faction?.Prefab != null)
                {
                    var factionLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
                        TextManager.Get("Faction"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
                    new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), factionLabel.RectTransform), location.Faction.Prefab.Name, textAlignment: Alignment.CenterRight, textColor: location.Faction.Prefab.IconColor);
                }
                var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
                    TextManager.Get("Biome", "location"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), connection.Biome.DisplayName, textAlignment: Alignment.CenterRight);

                var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), textContent.RectTransform),
                    TextManager.Get("LevelDifficulty"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft);
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), TextManager.GetWithVariable("percentageformat", "[value]", ((int)connection.LevelData.Difficulty).ToString()), textAlignment: Alignment.CenterRight);

                if (connection.LevelData.HasBeaconStation)
                {
                    var beaconStationContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
                    string style = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
                    var icon = new GUIImage(new RectTransform(new Point((int)(beaconStationContent.Rect.Height * 1.2f)), beaconStationContent.RectTransform),
                        style, scaleToFit: true)
                    {
                        Color = MapGenerationParams.Instance.IndicatorColor,
                        HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
                        ToolTip = RichString.Rich(TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"))
                    };
                    new GUITextBlock(new RectTransform(Vector2.One, beaconStationContent.RectTransform),
                        TextManager.Get("submarinetype.beaconstation", "beaconstationsonarlabel"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
                    {
                        Padding = Vector4.Zero,
                        ToolTip = icon.ToolTip
                    };
                }
                if (connection.LevelData.HasHuntingGrounds)
                {
                    var huntingGroundsContent = new GUILayoutGroup(new RectTransform(biomeLabel.RectTransform.NonScaledSize, textContent.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
                    var icon = new GUIImage(new RectTransform(new Point((int)(huntingGroundsContent.Rect.Height * 1.5f)), huntingGroundsContent.RectTransform),
                        "HuntingGrounds", scaleToFit: true)
                    {
                        Color = MapGenerationParams.Instance.IndicatorColor,
                        HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
                        ToolTip = RichString.Rich(TextManager.Get("HuntingGroundsTooltip"))
                    };
                    new GUITextBlock(new RectTransform(Vector2.One, huntingGroundsContent.RectTransform),
                        TextManager.Get("missionname.huntinggrounds"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
                    {
                        Padding = Vector4.Zero,
                        ToolTip = icon.ToolTip
                    };
                }
            }

            missionList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.4f), content.RectTransform))
            {
                Spacing = (int)(5 * GUI.yScale)
            };
            missionList.OnSelected = (GUIComponent selected, object userdata) =>
            {
                var tickBox = selected.FindChild(c => c is GUITickBox, recursive: true) as GUITickBox;
                if (GUI.MouseOn == tickBox) { return false; }
                if (tickBox != null)
                {
                    if (CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap) && tickBox.Enabled)
                    {
                        tickBox.Selected = !tickBox.Selected;
                    }
                }
                return true;
            };

            SelectedLevel = connection?.LevelData;
            Location currentDisplayLocation = Campaign.GetCurrentDisplayLocation();
            if (connection != null && connection.Locations.Contains(currentDisplayLocation))
            {
                List<Mission> availableMissions = currentDisplayLocation.GetMissionsInConnection(connection).Where(m => m.Prefab.ShowInMenus || GameMain.DebugDraw).ToList();

                if (availableMissions.None()) { availableMissions.Insert(0, null); }

                availableMissions.AddRange(location.AvailableMissions.Where(m => m.Locations[0] == m.Locations[1]));
                //show side objectives last
                availableMissions.Sort((m1, m2) => (m1?.Prefab.IsSideObjective ?? false).CompareTo(m2?.Prefab.IsSideObjective ?? false));

                missionList.Content.ClearChildren();

                bool isPrevMissionInNextLocation = false;
                foreach (Mission mission in availableMissions)
                {
                    bool isMissionInNextLocation = mission != null && location.AvailableMissions.Contains(mission);
                    if (isMissionInNextLocation && !isPrevMissionInNextLocation)
                    {
                        new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionList.Content.RectTransform), TextManager.Get("outpostmissions"),
                            textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont, wrap: true)
                        {
                            CanBeFocused = false
                        };
                        new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionList.Content.RectTransform), style: "HorizontalLine")
                        {
                            CanBeFocused = false
                        };
                    }
                    isPrevMissionInNextLocation = isMissionInNextLocation;

                    var missionPanel = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.1f), missionList.Content.RectTransform), style: null)
                    {
                        UserData = mission
                    };
                    var missionTextContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.9f), missionPanel.RectTransform, Anchor.Center))
                    {
                        Stretch = true,
                        CanBeFocused = true,
                        AbsoluteSpacing = GUI.IntScale(5)
                    };

                    LocalizedString missionName = mission?.Name ?? TextManager.Get("NoMission");
                    if (mission is { Prefab.IsSideObjective: true })
                    {
                        missionName = TextManager.AddPunctuation(':', TextManager.Get("sideobjective"), missionName);
                    }
                    if (GameMain.DebugDraw && mission != null)
                    {
                        if (!mission.Prefab.ShowInMenus) { missionName = $"[HIDDEN] {missionName}"; }
                        missionName += $" ({mission.Prefab.Identifier})";
                    }

                    var missionNameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), missionName, font: GUIStyle.SubHeadingFont, wrap: true);
                    missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(15));
                    if (mission == null)
                    {
                        missionTextContent.RectTransform.MinSize = missionNameBlock.RectTransform.MinSize = new Point(0, GUI.IntScale(35));
                        missionTextContent.ChildAnchor = Anchor.CenterLeft;
                    }
                    else
                    {
                        GUITickBox tickBox = null;
                        if (!isMissionInNextLocation && mission.Prefab.ShowInMenus && !mission.Prefab.IsSideObjective)
                        {
                            tickBox = new GUITickBox(new RectTransform(Vector2.One * 0.9f, missionNameBlock.RectTransform, anchor: Anchor.CenterLeft, scaleBasis: ScaleBasis.Smallest) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.X, 0) }, label: string.Empty)
                            {
                                UserData = mission,
                                Selected = Campaign.Map.CurrentLocation?.SelectedMissions.Contains(mission) ?? false
                            };
                            tickBox.RectTransform.MinSize = new Point(tickBox.Rect.Height, 0);
                            tickBox.RectTransform.IsFixedSize = true;
                            tickBox.Enabled = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap);
                            tickBox.OnSelected += (GUITickBox tb) =>
                            {
                                if (!CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap)) { return false; }

                                if (tb.Selected)
                                {
                                    Campaign.Map.CurrentLocation.SelectMission(mission);
                                }
                                else
                                {
                                    Campaign.Map.CurrentLocation.DeselectMission(mission);
                                }

                                foreach (GUITextBlock rewardText in missionRewardTexts)
                                {
                                    Mission otherMission = rewardText.UserData as Mission;
                                    rewardText.Text = otherMission.GetMissionRewardText(Submarine.MainSub);
                                }

                                UpdateMaxMissions(connection.OtherLocation(currentDisplayLocation));

                                if ((Campaign is MultiPlayerCampaign multiPlayerCampaign) && !multiPlayerCampaign.SuppressStateSending &&
                                    CampaignMode.AllowedToManageCampaign(Networking.ClientPermissions.ManageMap))
                                {
                                    GameMain.Client?.SendCampaignState();
                                }
                                return true;
                            };
                            missionTickBoxes.Add(tickBox);
                        }

                        GUILayoutGroup difficultyIndicatorGroup = null;
                        if (mission.Difficulty.HasValue)
                        {
                            difficultyIndicatorGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 0.9f), missionNameBlock.RectTransform, anchor: Anchor.CenterRight) { AbsoluteOffset = new Point((int)missionNameBlock.Padding.Z, 0) },
                                isHorizontal: true, childAnchor: Anchor.CenterRight)
                            {
                                AbsoluteSpacing = 1,
                                UserData = "difficulty",
                            };
                            difficultyIndicatorGroup.SetAsFirstChild();
                            var difficultyColor = mission.GetDifficultyColor();
                            for (int i = 0; i < mission.Difficulty; i++)
                            {
                                new GUIImage(new RectTransform(Vector2.One * 0.9f, difficultyIndicatorGroup.RectTransform, scaleBasis: ScaleBasis.Smallest) { IsFixedSize = true }, "DifficultyIndicator", scaleToFit: true)
                                {
                                    Color = difficultyColor,
                                    SelectedColor = difficultyColor,
                                    HoverColor = difficultyColor,
                                    ToolTip = mission.GetDifficultyToolTipText()
                                };
                            }
                        }

                        float extraPadding = 0;// 0.8f * tickBox.Rect.Width;
                        float extraZPadding = difficultyIndicatorGroup != null ? mission.Difficulty.Value * (difficultyIndicatorGroup.Children.First().Rect.Width + difficultyIndicatorGroup.AbsoluteSpacing) : 0;
                        missionNameBlock.Padding = new Vector4(missionNameBlock.Padding.X + (tickBox?.Rect.Width ?? 0) * 1.2f + extraPadding,
                            missionNameBlock.Padding.Y,
                            missionNameBlock.Padding.Z + extraZPadding + extraPadding,
                            missionNameBlock.Padding.W);
                        missionNameBlock.CalculateHeightFromText();

                        //spacing
                        new GUIFrame(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform) { MinSize = new Point(0, GUI.IntScale(10)) }, style: null);
                        
                        var rewardText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(mission.GetMissionRewardText(Submarine.MainSub)), wrap: true)
                        {
                            UserData = mission
                        };
                        missionRewardTexts.Add(rewardText);

                        LocalizedString reputationText = mission.GetReputationRewardText();
                        if (!reputationText.IsNullOrEmpty())
                        {
                            new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(reputationText), wrap: true);
                        }
                        new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(mission.Description), wrap: true);
                    }
                    missionPanel.RectTransform.MinSize = new Point(0, (int)(missionTextContent.Children.Sum(c => c.Rect.Height + missionTextContent.AbsoluteSpacing) / missionTextContent.RectTransform.RelativeSize.Y) + GUI.IntScale(0));
                    foreach (GUIComponent child in missionTextContent.Children)
                    {
                        if (child is GUITextBlock textBlock)
                        {
                            textBlock.Color = textBlock.SelectedColor = textBlock.HoverColor = Color.Transparent;
                            textBlock.SelectedTextColor = textBlock.HoverTextColor = textBlock.TextColor;
                        }
                    }
                    missionPanel.OnAddedToGUIUpdateList = (c) =>
                    {
                        missionTextContent.Children.ForEach(child => child.State = c.State);
                        if (missionTextContent.FindChild("difficulty", recursive: true) is GUILayoutGroup group)
                        {
                            group.State = c.State;
                        }
                    };

                    if (mission != availableMissions.Last())
                    {
                        new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionList.Content.RectTransform), style: "HorizontalLine")
                        {
                            CanBeFocused = false
                        };
                    }
                }
                if (prevSelectedLocation == selectedLocation)
                {
                    missionList.BarScroll = prevMissionListScroll;
                    missionList.UpdateDimensions();
                    missionList.UpdateScrollBarSize();
                }
            }
            var destination = connection.OtherLocation(currentDisplayLocation);
            UpdateMaxMissions(destination);

            var buttonArea = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), content.RectTransform), isHorizontal: true);

            new GUITextBlock(new RectTransform(new Vector2(0.6f, 1.0f), buttonArea.RectTransform), "", font: GUIStyle.SubHeadingFont)
            {
                TextGetter = () =>
                {
                    int missionCount = 0;
                    if (GameMain.GameSession != null && Campaign.Map?.CurrentLocation?.SelectedMissions != null)
                    {
                        missionCount = Campaign.Map.CurrentLocation.SelectedMissions.Count(m => m.Locations.Contains(location) && !GameMain.GameSession.Missions.Contains(m) && !m.Prefab.IsSideObjective);
                    }
                    return TextManager.AddPunctuation(':', TextManager.Get("Missions"), $"{missionCount}/{Campaign.Settings.TotalMaxMissionCount}");
                }
            };

            StartButton = new GUIButton(new RectTransform(new Vector2(0.4f, 1.0f), buttonArea.RectTransform),
                TextManager.Get("StartCampaignButton"), style: "GUIButtonLarge")
            {
                OnClicked = (GUIButton btn, object obj) =>
                {
                    if (missionList.Content.FindChild(c => c is GUITickBox tickBox && tickBox.Selected, recursive: true) == null &&
                        missionList.Content.Children.Any(c => c.UserData is Mission { Prefab.ShowInMenus: true, Prefab.IsSideObjective: false } mission && mission.Locations.Contains(Campaign?.Map?.CurrentLocation)))
                    {
                        var noMissionVerification = new GUIMessageBox(string.Empty, TextManager.Get("nomissionprompt"), [TextManager.Get("yes"), TextManager.Get("no")]);
                        noMissionVerification.Buttons[0].OnClicked = (btn, userdata) =>
                        {
                            StartRound?.Invoke();
                            noMissionVerification.Close();
                            return true;
                        };
                        noMissionVerification.Buttons[1].OnClicked = noMissionVerification.Close;
                    }
                    else
                    {
                        StartRound?.Invoke();
                    }
                    return true;
                },
                Enabled = true,
                Visible = CampaignMode.AllowedToManageCampaign(ClientPermissions.ManageMap)
            };

            buttonArea.RectTransform.MinSize = new Point(0, StartButton.RectTransform.MinSize.Y);

            if (Level.Loaded != null &&
                connection?.LevelData == Level.Loaded.LevelData &&
                currentDisplayLocation == Campaign.Map?.CurrentLocation)
            {
                StartButton.Visible = false;
                missionList.Enabled = false;
            }
        }

        public void SelectTab(CampaignMode.InteractionType tab, Character npc = null)
        {
            if (Campaign.ShowCampaignUI || (Campaign.ForceMapUI && tab == CampaignMode.InteractionType.Map))
            {
                HintManager.OnShowCampaignInterface(tab);
            }

            selectedTab = tab;
            for (int i = 0; i < tabs.Length; i++)
            {
                if (tabs[i] != null)
                {
                    tabs[i].Visible = (int)selectedTab == i;
                }
            }
            
            locationInfoPanel.Visible = tab == CampaignMode.InteractionType.Map && selectedLocation != null;

            switch (selectedTab)
            {
                case CampaignMode.InteractionType.Store:
                    Store.SelectStore(npc);
                    break;
                case CampaignMode.InteractionType.Crew:
                    HRManagerUI.UpdateCrew();
                    HRManagerUI.UpdateHireables();
                    break;
                case CampaignMode.InteractionType.PurchaseSub:
                    submarineSelection ??= new SubmarineSelection(false, () => Campaign.ShowCampaignUI = false, tabs[(int)CampaignMode.InteractionType.PurchaseSub].RectTransform);
                    submarineSelection.RefreshSubmarineDisplay(true, setTransferOptionToTrue: true);
                    break;
                case CampaignMode.InteractionType.Map:
                    GameMain.GameSession?.Map?.ResetPendingSub();
                    //refresh mission rewards (may have been changed by e.g. a pending submarine switch)
                    foreach (GUITextBlock rewardText in missionRewardTexts)
                    {
                        Mission mission = (Mission)rewardText.UserData;
                        rewardText.Text = mission.GetMissionRewardText(Submarine.MainSub);
                    }
                    break;
            }
        }

        public static LocalizedString GetMoney()
        {
            return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.GetBalance()));
        }

        public static LocalizedString GetTotalBalance()
        {
            return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.GetBalance() : 0);
        }

        public static LocalizedString GetBankBalance()
        {
            return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.Bank.Balance : 0);
        }

        public static LocalizedString GetWalletBalance()
        {
            return TextManager.FormatCurrency(GameMain.GameSession?.Campaign is { } campaign ? campaign.Wallet.Balance : 0);
        }

        private void UpdateMaxMissions(Location location)
        {
            hasMaxMissions = Campaign.NumberOfSelectableMissionsAtLocation(location) >= Campaign.Settings.TotalMaxMissionCount;
        }

        public readonly struct PlayerBalanceElement
        {
            public readonly bool DisplaySeparateBalances;
            public readonly GUILayoutGroup ParentComponent;
            public readonly GUILayoutGroup TotalBalanceContainer;
            public readonly GUILayoutGroup BankBalanceContainer;

            public PlayerBalanceElement(bool displaySeparateBalances, GUILayoutGroup parentComponent, GUILayoutGroup totalBalanceContainer, GUILayoutGroup bankBalanceContainer)
            {
                DisplaySeparateBalances = displaySeparateBalances;
                ParentComponent = parentComponent;
                TotalBalanceContainer = totalBalanceContainer;
                BankBalanceContainer = bankBalanceContainer;
            }

            public PlayerBalanceElement(PlayerBalanceElement element, bool displaySeparateBalances)
            {
                DisplaySeparateBalances = displaySeparateBalances;
                ParentComponent = element.ParentComponent;
                TotalBalanceContainer = element.TotalBalanceContainer;
                BankBalanceContainer = element.BankBalanceContainer;
            }
        }

        public static PlayerBalanceElement? AddBalanceElement(GUIComponent elementParent, Vector2 relativeSize)
        {
            var parent = new GUILayoutGroup(new RectTransform(relativeSize, elementParent.RectTransform), isHorizontal: true, childAnchor: Anchor.TopRight);
            if (GameMain.IsSingleplayer)
            {
                AddBalance(parent, true, TextManager.Get("campaignstore.balance"), GetTotalBalance);
                return null;
            }
            else
            {
                bool displaySeparateBalances = CampaignMode.AllowedToManageWallets();
                var totalBalanceContainer = AddBalance(parent, displaySeparateBalances, TextManager.Get("campaignstore.total"), GetTotalBalance);
                var bankBalanceContainer = AddBalance(parent, displaySeparateBalances, TextManager.Get("crewwallet.bank"), GetBankBalance);
                AddBalance(parent, true, TextManager.Get("crewwallet.wallet"), GetWalletBalance);
                var playerBalanceElement = new PlayerBalanceElement(displaySeparateBalances, parent, totalBalanceContainer, bankBalanceContainer);
                parent.Recalculate();
                return playerBalanceElement;
            }

            static GUILayoutGroup AddBalance(GUIComponent parent, bool visible, LocalizedString text, GUITextBlock.TextGetterHandler textGetter)
            {
                float balanceContainerWidth = GameMain.IsSingleplayer ? 1 : 1 / 3f;
                var rt = new RectTransform(new Vector2(balanceContainerWidth, 1.0f), parent.RectTransform)
                {
                    MaxSize = new Point(GUI.IntScale(GUI.AdjustForTextScale(120)), int.MaxValue)
                };
                var balanceContainer = new GUILayoutGroup(rt, childAnchor: Anchor.TopRight)
                {
                    RelativeSpacing = 0.005f,
                    Visible = visible
                };
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), balanceContainer.RectTransform), text,
                    font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
                {
                    AutoScaleVertical = true,
                    ForceUpperCase = ForceUpperCase.Yes
                };
                new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), balanceContainer.RectTransform), "",
                    textColor: Color.White, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.TopRight)
                {
                    AutoScaleVertical = true,
                    TextScale = 1.1f,
                    TextGetter = textGetter
                };
                return balanceContainer;
            }
        }

        public static PlayerBalanceElement? UpdateBalanceElement(PlayerBalanceElement? playerBalanceElement)
        {
            if (playerBalanceElement is { } balanceElement)
            {
                bool displaySeparateBalances = CampaignMode.AllowedToManageWallets();
                if (displaySeparateBalances != balanceElement.DisplaySeparateBalances)
                {
                    balanceElement.TotalBalanceContainer.Visible = displaySeparateBalances;
                    balanceElement.BankBalanceContainer.Visible = displaySeparateBalances;
                    playerBalanceElement = new PlayerBalanceElement(balanceElement, displaySeparateBalances);
                    balanceElement.ParentComponent.Recalculate();
                }
            }
            return playerBalanceElement;
        }
    }
}
