public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager,
                                     IEntityManager entityManager)
        {
            RobustXamlLoader.Load(this);
            _random             = IoCManager.Resolve <IRobustRandom>();
            _prototypeManager   = prototypeManager;
            _entMan             = entityManager;
            _preferencesManager = preferencesManager;

            #region Left

            #region Randomize

            #endregion Randomize

            #region Name

            _nameEdit.OnTextChanged              += args => { SetName(args.Text); };
            _nameRandomButton.OnPressed          += args => RandomizeName();
            _randomizeEverythingButton.OnPressed += args => { RandomizeEverything(); };
            _warningLabel.SetMarkup($"[color=red]{Loc.GetString("humanoid-profile-editor-naming-rules-warning")}[/color]");

            #endregion Name

            #region Appearance

            _tabContainer.SetTabTitle(0, Loc.GetString("humanoid-profile-editor-appearance-tab"));

            #region Sex

            var sexButtonGroup = new ButtonGroup();

            _sexMaleButton.Group      = sexButtonGroup;
            _sexMaleButton.OnPressed += args =>
            {
                SetSex(Sex.Male);
                if (Profile?.Gender == Gender.Female)
                {
                    SetGender(Gender.Male);
                    UpdateGenderControls();
                }
            };

            _sexFemaleButton.Group      = sexButtonGroup;
            _sexFemaleButton.OnPressed += _ =>
            {
                SetSex(Sex.Female);

                if (Profile?.Gender == Gender.Male)
                {
                    SetGender(Gender.Female);
                    UpdateGenderControls();
                }
            };

            #endregion Sex

            #region Age

            _ageEdit.OnTextChanged += args =>
            {
                if (!int.TryParse(args.Text, out var newAge))
                {
                    return;
                }
                SetAge(newAge);
            };

            #endregion Age

            #region Gender

            _genderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-male-text"), (int)Gender.Male);
            _genderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-female-text"), (int)Gender.Female);
            _genderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-epicene-text"), (int)Gender.Epicene);
            _genderButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-neuter-text"), (int)Gender.Neuter);

            _genderButton.OnItemSelected += args =>
            {
                _genderButton.SelectId(args.Id);
                SetGender((Gender)args.Id);
            };

            #endregion Gender

            #region Species

            _speciesList = prototypeManager.EnumeratePrototypes <SpeciesPrototype>().ToList();
            for (var i = 0; i < _speciesList.Count; i++)
            {
                CSpeciesButton.AddItem(_speciesList[i].Name, i);
            }

            CSpeciesButton.OnItemSelected += args =>
            {
                CSpeciesButton.SelectId(args.Id);
                SetSpecies(_speciesList[args.Id].ID);
                OnSkinColorOnValueChanged(CSkin);
            };

            #endregion Species

            #region Skin

            // 0 - 100, 0 being gold/yellowish and 100 being dark
            // HSV based
            //
            // 0 - 20 changes the hue
            // 20 - 100 changes the value
            // 0 is 45 - 20 - 100
            // 20 is 25 - 20 - 100
            // 100 is 25 - 100 - 20
            _skinColor.OnValueChanged += OnSkinColorOnValueChanged;

            #endregion

            #region Hair

            _hairPicker.Populate();

            _hairPicker.OnHairStylePicked += newStyle =>
            {
                if (Profile is null)
                {
                    return;
                }
                Profile = Profile.WithCharacterAppearance(
                    Profile.Appearance.WithHairStyleName(newStyle));
                IsDirty = true;
            };

            _hairPicker.OnHairColorPicked += newColor =>
            {
                if (Profile is null)
                {
                    return;
                }
                Profile = Profile.WithCharacterAppearance(
                    Profile.Appearance.WithHairColor(newColor));
                IsDirty = true;
            };

            _facialHairPicker.Populate();

            _facialHairPicker.OnHairStylePicked += newStyle =>
            {
                if (Profile is null)
                {
                    return;
                }
                Profile = Profile.WithCharacterAppearance(
                    Profile.Appearance.WithFacialHairStyleName(newStyle));
                IsDirty = true;
            };

            _facialHairPicker.OnHairColorPicked += newColor =>
            {
                if (Profile is null)
                {
                    return;
                }
                Profile = Profile.WithCharacterAppearance(
                    Profile.Appearance.WithFacialHairColor(newColor));
                IsDirty = true;
            };

            #endregion Hair

            #region Clothing

            _clothingButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-jumpsuit"), (int)ClothingPreference.Jumpsuit);
            _clothingButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-jumpskirt"), (int)ClothingPreference.Jumpskirt);

            _clothingButton.OnItemSelected += args =>
            {
                _clothingButton.SelectId(args.Id);
                SetClothing((ClothingPreference)args.Id);
            };

            #endregion Clothing

            #region Backpack

            _backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-backpack"), (int)BackpackPreference.Backpack);
            _backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-satchel"), (int)BackpackPreference.Satchel);
            _backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-duffelbag"), (int)BackpackPreference.Duffelbag);

            _backpackButton.OnItemSelected += args =>
            {
                _backpackButton.SelectId(args.Id);
                SetBackpack((BackpackPreference)args.Id);
            };

            #endregion Backpack

            #region Eyes

            _eyesPicker.OnEyeColorPicked += newColor =>
            {
                if (Profile is null)
                {
                    return;
                }
                Profile = Profile.WithCharacterAppearance(
                    Profile.Appearance.WithEyeColor(newColor));
                IsDirty = true;
            };

            #endregion Eyes

            #endregion Appearance

            #region Jobs

            _tabContainer.SetTabTitle(1, Loc.GetString("humanoid-profile-editor-jobs-tab"));

            _preferenceUnavailableButton.AddItem(
                Loc.GetString("humanoid-profile-editor-preference-unavailable-stay-in-lobby-button"),
                (int)PreferenceUnavailableMode.StayInLobby);
            _preferenceUnavailableButton.AddItem(
                Loc.GetString("humanoid-profile-editor-preference-unavailable-spawn-as-overflow-button",
                              ("overflowJob", Loc.GetString(SharedGameTicker.FallbackOverflowJobName))),
                (int)PreferenceUnavailableMode.SpawnAsOverflow);

            _preferenceUnavailableButton.OnItemSelected += args =>
            {
                _preferenceUnavailableButton.SelectId(args.Id);

                Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode)args.Id);
                IsDirty = true;
            };

            _jobPriorities = new List <JobPrioritySelector>();
            _jobCategories = new Dictionary <string, BoxContainer>();

            var firstCategory = true;

            foreach (var job in prototypeManager.EnumeratePrototypes <JobPrototype>().OrderBy(j => j.Name))
            {
                if (!job.SetPreference)
                {
                    continue;
                }

                foreach (var department in job.Departments)
                {
                    if (!_jobCategories.TryGetValue(department, out var category))
                    {
                        category = new BoxContainer
                        {
                            Orientation = LayoutOrientation.Vertical,
                            Name        = department,
                            ToolTip     = Loc.GetString("humanoid-profile-editor-jobs-amount-in-department-tooltip",
                                                        ("departmentName", department))
                        };

                        if (firstCategory)
                        {
                            firstCategory = false;
                        }
                        else
                        {
                            category.AddChild(new Control
                            {
                                MinSize = new Vector2(0, 23),
                            });
                        }

                        category.AddChild(new PanelContainer
                        {
                            PanelOverride = new StyleBoxFlat {
                                BackgroundColor = Color.FromHex("#464966")
                            },
                            Children =
                            {
                                new Label
                                {
                                    Text = Loc.GetString("humanoid-profile-editor-department-jobs-label",
                                                         ("departmentName", department))
                                }
                            }
                        });
        public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager)
        {
            _random = IoCManager.Resolve <IRobustRandom>();

            _preferencesManager = preferencesManager;

            var margin = new MarginContainer
            {
                MarginTopOverride    = 10,
                MarginBottomOverride = 10,
                MarginLeftOverride   = 10,
                MarginRightOverride  = 10
            };

            AddChild(margin);

            var vBox = new VBoxContainer();

            margin.AddChild(vBox);

            var middleContainer = new HBoxContainer
            {
                SeparationOverride = 10
            };

            vBox.AddChild(middleContainer);

            var leftColumn = new VBoxContainer();

            middleContainer.AddChild(leftColumn);

            #region Randomize

            {
                var panel = HighlightedContainer();
                var randomizeEverythingButton = new Button
                {
                    Text = Loc.GetString("Randomize everything")
                };
                randomizeEverythingButton.OnPressed += args => { RandomizeEverything(); };
                panel.AddChild(randomizeEverythingButton);
                leftColumn.AddChild(panel);
            }

            #endregion Randomize

            #region Name

            {
                var panel = HighlightedContainer();
                var hBox  = new HBoxContainer
                {
                    SizeFlagsVertical = SizeFlags.FillExpand
                };
                var nameLabel = new Label {
                    Text = Loc.GetString("Name:")
                };
                _nameEdit = new LineEdit
                {
                    CustomMinimumSize = (270, 0),
                    SizeFlagsVertical = SizeFlags.ShrinkCenter
                };
                _nameEdit.OnTextChanged += args => { SetName(args.Text); };
                var nameRandomButton = new Button
                {
                    Text = Loc.GetString("Randomize"),
                };
                nameRandomButton.OnPressed += args => RandomizeName();
                hBox.AddChild(nameLabel);
                hBox.AddChild(_nameEdit);
                hBox.AddChild(nameRandomButton);
                panel.AddChild(hBox);
                leftColumn.AddChild(panel);
            }

            #endregion Name

            var tabContainer = new TabContainer {
                SizeFlagsVertical = SizeFlags.FillExpand
            };
            vBox.AddChild(tabContainer);

            #region Appearance

            {
                var appearanceVBox = new VBoxContainer();
                tabContainer.AddChild(appearanceVBox);
                tabContainer.SetTabTitle(0, Loc.GetString("Appearance"));

                var sexAndAgeRow = new HBoxContainer
                {
                    SeparationOverride = 10
                };

                appearanceVBox.AddChild(sexAndAgeRow);

                #region Sex

                {
                    var panel    = HighlightedContainer();
                    var hBox     = new HBoxContainer();
                    var sexLabel = new Label {
                        Text = Loc.GetString("Sex:")
                    };

                    var sexButtonGroup = new ButtonGroup();

                    _sexMaleButton = new Button
                    {
                        Text  = Loc.GetString("Male"),
                        Group = sexButtonGroup
                    };
                    _sexMaleButton.OnPressed += args => { SetSex(Sex.Male); };
                    _sexFemaleButton          = new Button
                    {
                        Text  = Loc.GetString("Female"),
                        Group = sexButtonGroup
                    };
                    _sexFemaleButton.OnPressed += args => { SetSex(Sex.Female); };
                    hBox.AddChild(sexLabel);
                    hBox.AddChild(_sexMaleButton);
                    hBox.AddChild(_sexFemaleButton);
                    panel.AddChild(hBox);
                    sexAndAgeRow.AddChild(panel);
                }

                #endregion Sex

                #region Age

                {
                    var panel    = HighlightedContainer();
                    var hBox     = new HBoxContainer();
                    var ageLabel = new Label {
                        Text = Loc.GetString("Age:")
                    };
                    _ageEdit = new LineEdit {
                        CustomMinimumSize = (40, 0)
                    };
                    _ageEdit.OnTextChanged += args =>
                    {
                        if (!int.TryParse(args.Text, out var newAge))
                        {
                            return;
                        }
                        SetAge(newAge);
                    };
                    hBox.AddChild(ageLabel);
                    hBox.AddChild(_ageEdit);
                    panel.AddChild(hBox);
                    sexAndAgeRow.AddChild(panel);
                }

                #endregion Age

                #region Hair

                {
                    var panel = HighlightedContainer();
                    panel.SizeFlagsHorizontal = SizeFlags.None;
                    var hairHBox = new HBoxContainer();

                    _hairPicker = new HairStylePicker();
                    _hairPicker.Populate();

                    _hairPicker.OnHairStylePicked += newStyle =>
                    {
                        if (Profile is null)
                        {
                            return;
                        }
                        Profile = Profile.WithCharacterAppearance(
                            Profile.Appearance.WithHairStyleName(newStyle));
                        IsDirty = true;
                    };

                    _hairPicker.OnHairColorPicked += newColor =>
                    {
                        if (Profile is null)
                        {
                            return;
                        }
                        Profile = Profile.WithCharacterAppearance(
                            Profile.Appearance.WithHairColor(newColor));
                        IsDirty = true;
                    };

                    _facialHairPicker = new FacialHairStylePicker();
                    _facialHairPicker.Populate();

                    _facialHairPicker.OnHairStylePicked += newStyle =>
                    {
                        if (Profile is null)
                        {
                            return;
                        }
                        Profile = Profile.WithCharacterAppearance(
                            Profile.Appearance.WithFacialHairStyleName(newStyle));
                        IsDirty = true;
                    };

                    _facialHairPicker.OnHairColorPicked += newColor =>
                    {
                        if (Profile is null)
                        {
                            return;
                        }
                        Profile = Profile.WithCharacterAppearance(
                            Profile.Appearance.WithFacialHairColor(newColor));
                        IsDirty = true;
                    };

                    hairHBox.AddChild(_hairPicker);
                    hairHBox.AddChild(_facialHairPicker);

                    panel.AddChild(hairHBox);
                    appearanceVBox.AddChild(panel);
                }

                #endregion Hair
            }

            #endregion

            #region Jobs

            {
                var jobList = new VBoxContainer();

                var jobVBox = new VBoxContainer
                {
                    Children =
                    {
                        (_preferenceUnavailableButton = new OptionButton()),
                        new ScrollContainer
                        {
                            SizeFlagsVertical = SizeFlags.FillExpand,
                            Children          =
                            {
                                jobList
                            }
                        }
                    }
                };

                tabContainer.AddChild(jobVBox);

                tabContainer.SetTabTitle(1, Loc.GetString("Jobs"));

                _preferenceUnavailableButton.AddItem(
                    Loc.GetString("Stay in lobby if preference unavailable."),
                    (int)PreferenceUnavailableMode.StayInLobby);
                _preferenceUnavailableButton.AddItem(
                    Loc.GetString("Be an {0} if preference unavailable.",
                                  Loc.GetString(SharedGameTicker.OverflowJobName)),
                    (int)PreferenceUnavailableMode.SpawnAsOverflow);

                _preferenceUnavailableButton.OnItemSelected += args =>
                {
                    _preferenceUnavailableButton.SelectId(args.Id);

                    Profile = Profile.WithPreferenceUnavailable((PreferenceUnavailableMode)args.Id);
                    IsDirty = true;
                };

                _jobPriorities = new List <JobPrioritySelector>();

                foreach (var job in prototypeManager.EnumeratePrototypes <JobPrototype>().OrderBy(j => j.Name))
                {
                    var selector = new JobPrioritySelector(job);
                    jobList.AddChild(selector);
                    _jobPriorities.Add(selector);

                    selector.PriorityChanged += priority =>
                    {
                        Profile = Profile.WithJobPriority(job.ID, priority);
                        IsDirty = true;

                        if (priority == JobPriority.High)
                        {
                            // Lower any other high priorities to medium.
                            foreach (var jobSelector in _jobPriorities)
                            {
                                if (jobSelector != selector && jobSelector.Priority == JobPriority.High)
                                {
                                    jobSelector.Priority = JobPriority.Medium;
                                    Profile = Profile.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium);
                                }
                            }
                        }
                    };
                }
            }

            #endregion

            #region Antags

            {
                var antagList = new VBoxContainer();

                var antagVBox = new VBoxContainer
                {
                    Children =
                    {
                        new ScrollContainer
                        {
                            SizeFlagsVertical = SizeFlags.FillExpand,
                            Children          =
                            {
                                antagList
                            }
                        }
                    }
                };

                tabContainer.AddChild(antagVBox);

                tabContainer.SetTabTitle(2, Loc.GetString("Antags"));

                _antagPreferences = new List <AntagPreferenceSelector>();

                foreach (var antag in prototypeManager.EnumeratePrototypes <AntagPrototype>().OrderBy(a => a.Name))
                {
                    if (!antag.SetPreference)
                    {
                        continue;
                    }
                    var selector = new AntagPreferenceSelector(antag);
                    antagList.AddChild(selector);
                    _antagPreferences.Add(selector);

                    selector.PreferenceChanged += preference =>
                    {
                        Profile = Profile.WithAntagPreference(antag.ID, preference);
                        IsDirty = true;
                    };
                }
            }

            #endregion

            var rightColumn = new VBoxContainer();
            middleContainer.AddChild(rightColumn);

            #region Import/Export

            {
                var panelContainer = HighlightedContainer();
                var hBox           = new HBoxContainer();
                var importButton   = new Button
                {
                    Text     = Loc.GetString("Import"),
                    Disabled = true,
                    ToolTip  = "Not yet implemented!"
                };
                var exportButton = new Button
                {
                    Text     = Loc.GetString("Export"),
                    Disabled = true,
                    ToolTip  = "Not yet implemented!"
                };
                hBox.AddChild(importButton);
                hBox.AddChild(exportButton);
                panelContainer.AddChild(hBox);
                rightColumn.AddChild(panelContainer);
            }

            #endregion Import/Export

            #region Save

            {
                var panel = HighlightedContainer();
                _saveButton = new Button
                {
                    Text = Loc.GetString("Save"),
                    SizeFlagsHorizontal = SizeFlags.ShrinkCenter
                };
                _saveButton.OnPressed += args => { Save(); };
                panel.AddChild(_saveButton);
                rightColumn.AddChild(panel);
            }

            #endregion Save

            if (preferencesManager.ServerDataLoaded)
            {
                LoadServerData();
            }

            preferencesManager.OnServerDataLoaded += LoadServerData;

            IsDirty = false;
        }