using System;
using System.Collections.Generic;
using System.Linq;
using Content.Client.GameObjects.Components;
using Content.Client.Interfaces;
using Content.Client.Utility;
using Content.Shared;
using Content.Shared.Jobs;
using Content.Shared.Preferences;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using static Content.Client.StaticIoC;

namespace Content.Client.UserInterface
{
    public partial class HumanoidProfileEditor : Control
    {
        private static readonly StyleBoxFlat HighlightedStyle = new StyleBoxFlat
        {
            BackgroundColor = new Color(47, 47, 53),
            ContentMarginTopOverride = 10,
            ContentMarginBottomOverride = 10,
            ContentMarginLeftOverride = 10,
            ContentMarginRightOverride = 10
        };

        private readonly LineEdit _ageEdit;
        private readonly LineEdit _nameEdit;
        private readonly IClientPreferencesManager _preferencesManager;
        private readonly Button _saveButton;
        private readonly Button _sexFemaleButton;
        private readonly Button _sexMaleButton;
        private readonly HairStylePicker _hairPicker;
        private readonly FacialHairStylePicker _facialHairPicker;
        private readonly List<JobPrioritySelector> _jobPriorities;
        private readonly OptionButton _preferenceUnavailableButton;

        private bool _isDirty;
        public int CharacterSlot;
        public HumanoidCharacterProfile Profile;
        public event Action<HumanoidCharacterProfile> OnProfileChanged;

        public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager)
        {
            _random = IoCManager.Resolve<IRobustRandom>();
            Profile = (HumanoidCharacterProfile) preferencesManager.Preferences.SelectedCharacter;
            CharacterSlot = preferencesManager.Preferences.SelectedCharacterIndex;
            _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

            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

            UpdateControls();

            IsDirty = false;
        }

        private void SetAge(int newAge)
        {
            Profile = Profile?.WithAge(newAge);
            IsDirty = true;
        }

        private void SetSex(Sex newSex)
        {
            Profile = Profile?.WithSex(newSex);
            IsDirty = true;
        }

        private void SetName(string newName)
        {
            Profile = Profile?.WithName(newName);
            IsDirty = true;
        }

        public void Save()
        {
            IsDirty = false;
            _preferencesManager.UpdateCharacter(Profile, CharacterSlot);
            OnProfileChanged?.Invoke(Profile);
        }

        private bool IsDirty
        {
            get => _isDirty;
            set
            {
                _isDirty = value;
                UpdateSaveButton();
            }
        }

        private static Control HighlightedContainer()
        {
            return new PanelContainer
            {
                PanelOverride = HighlightedStyle
            };
        }

        private void UpdateNameEdit()
        {
            _nameEdit.Text = Profile.Name;
        }

        private void UpdateAgeEdit()
        {
            _ageEdit.Text = Profile.Age.ToString();
        }

        private void UpdateSexControls()
        {
            if (Profile.Sex == Sex.Male)
                _sexMaleButton.Pressed = true;
            else
                _sexFemaleButton.Pressed = true;
        }

        private void UpdateHairPickers()
        {
            _hairPicker.SetData(
                Profile.Appearance.HairColor,
                Profile.Appearance.HairStyleName);
            _facialHairPicker.SetData(
                Profile.Appearance.FacialHairColor,
                Profile.Appearance.FacialHairStyleName);
        }

        private void UpdateSaveButton()
        {
            _saveButton.Disabled = Profile is null || !IsDirty;
        }

        public void UpdateControls()
        {
            if (Profile is null) return;
            UpdateNameEdit();
            UpdateSexControls();
            UpdateAgeEdit();
            UpdateHairPickers();
            UpdateSaveButton();
            UpdateJobPriorities();

            _preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
        }

        private void UpdateJobPriorities()
        {
            foreach (var prioritySelector in _jobPriorities)
            {
                var jobId = prioritySelector.Job.ID;

                var priority = Profile.JobPriorities.GetValueOrDefault(jobId, JobPriority.Never);

                prioritySelector.Priority = priority;
            }
        }

        private class JobPrioritySelector : Control
        {
            public JobPrototype Job { get; }
            private readonly OptionButton _optionButton;

            public JobPriority Priority
            {
                get => (JobPriority) _optionButton.SelectedId;
                set => _optionButton.SelectId((int) value);
            }

            public event Action<JobPriority> PriorityChanged;

            public JobPrioritySelector(JobPrototype job)
            {
                Job = job;
                _optionButton = new OptionButton();
                _optionButton.AddItem(Loc.GetString("High"), (int) JobPriority.High);
                _optionButton.AddItem(Loc.GetString("Medium"), (int) JobPriority.Medium);
                _optionButton.AddItem(Loc.GetString("Low"), (int) JobPriority.Low);
                _optionButton.AddItem(Loc.GetString("Never"), (int) JobPriority.Never);

                _optionButton.OnItemSelected += args =>
                {
                    _optionButton.SelectId(args.Id);
                    PriorityChanged?.Invoke(Priority);
                };

                var icon = new TextureRect
                {
                    TextureScale = (2, 2),
                    Stretch = TextureRect.StretchMode.KeepCentered
                };

                if (job.Icon != null)
                {
                    var specifier = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/job_icons.rsi"), job.Icon);
                    icon.Texture = specifier.Frame0();
                }

                AddChild(new HBoxContainer
                {
                    Children =
                    {
                        icon,
                        new Label {Text = job.Name, CustomMinimumSize = (175, 0)},
                        _optionButton
                    }
                });
            }
        }
    }
}
