using System;
using System.Globalization;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core.Plugins;
using Oxide.Core;
using Oxide.Game.Rust.Cui;
using UnityEngine;

/*1.2.3
 * Potential fix for reported Out of Memory exception in OnActiveItemChanged
 * Added `Disable Monitoring Ammo Added/Removed to/from Inventory (Improves Performance)` to config
 */

namespace Oxide.Plugins
{
    [Info("Ammo HUD", "beee", "1.2.3")]
    [Description("Allows players to show an Ammo HUD.")]
    public class AmmoHUD : RustPlugin
    {
        private PluginConfig _config;
        
        [PluginReference]
        private Plugin ImageLibrary;

        PlayersData pData;

        const string PREFIX_LONG = "ammohud.";
        const string PERM_USE = PREFIX_LONG + "use";
        
        const uint SNOWBALLGUN_PREFABID = 3228215527;
        const int SNOWBALLGUN_ITEMID = 1103488722;
        const uint SUPPLYSIGNAL_PREFABID = 775476535;
        
        #region Config

        private class PluginConfig
        {
            public VersionNumber Version;

            [JsonProperty("General Settings")]
            public GeneralConfigSettings GeneralSettings { get; set; }

            [JsonProperty("Position Settings")]
            public PositionConfigSettings PositionSettings { get; set; }

            #region GeneralConfigSettings
            internal class GeneralConfigSettings
            {
                [JsonProperty("Show Text Outline")]
                public bool ShowOutline { get; set; }

                [JsonProperty("Show Individual Ammo Icons")]
                public bool ShowAmmoIcons { get; set; }

                [JsonProperty("Show Throwables")]
                public bool ShowThrowables { get; set; }

                [JsonProperty("Disable Monitoring Ammo Added/Removed to/from Inventory (Improves Performance)")]
                public bool DisableInventoryMonitor { get; set; }
            }
            #endregion

            #region PositionConfigSettings
            internal class PositionConfigSettings
            {
                [JsonProperty("Default State (true = on, false = off)")]
                public bool DefaultState { get; set; }

                [JsonProperty(
                    "Position (Top, TopLeft, TopRight, Left, Right, Bottom, BottomLeft, BottomRight)"
                )]
                public string Position { get; set; }

                [JsonProperty("Custom Position")]
                public CustomPositionConfigSettings CustomPositionSettings { get; set; }

                #region CustomPositionConfigSettings
                internal class CustomPositionConfigSettings
                {
                    [JsonProperty("Enabled")]
                    public bool Enabled { get; set; }

                    [JsonProperty("Custom Position")]
                    public PositionPreset CustomPosition { get; set; }
                }
                #endregion
            }
            #endregion
        }

        protected override void LoadDefaultConfig() => _config = GetDefaultConfig();

        private PluginConfig GetDefaultConfig()
        {
            PluginConfig result = new PluginConfig
            {
                Version = Version,
                GeneralSettings = new ()
                {
                    ShowOutline = true,
                    ShowAmmoIcons = true,
                    ShowThrowables = false
                },
                PositionSettings = new ()
                {
                    DefaultState = true,
                    Position = "Right",
                    CustomPositionSettings =
                        new ()
                        {
                            Enabled = false,
                            CustomPosition = new ()
                            {
                                Position = PositionEnum.right,
                                ParentPosition = new ()
                                {
                                    Enabled = true,
                                    AnchorMin = "1 0.5",
                                    AnchorMax = "1 0.5",
                                    OffsetMin = "-155 -32",
                                    OffsetMax = "-15 33"
                                },
                                WeaponAmmoFontSize = 36,
                                WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                                WeaponAmmoPosition = new ()
                                {
                                    Enabled = true,
                                    AnchorMin = "0 0",
                                    AnchorMax = "0.79 0.70",
                                    OffsetMin = "0 0",
                                    OffsetMax = "0 0"
                                },
                                TotalAmmoFontSize = 22,
                                TotalAmmoTextAlignment = TextAnchor.LowerRight,
                                TotalAmmoPosition = new ()
                                {
                                    Enabled = true,
                                    AnchorMin = "0 0.55",
                                    AnchorMax = "1 1",
                                    OffsetMin = "0 0",
                                    OffsetMax = "0 0"
                                },
                                IconPosition = new ()
                                {
                                    Enabled = true,
                                    AnchorMin = "1 0.13",
                                    AnchorMax = "1 0.13",
                                    OffsetMin = "-30 0",
                                    OffsetMax = "0 30"
                                }
                            }
                        }
                }
            };

            return result;
        }

        private void CheckForConfigUpdates()
        {
            bool changes = false;

            if (
                _config == null
                || _config.PositionSettings == null
                || _config.PositionSettings.CustomPositionSettings == null
                || _config.PositionSettings.CustomPositionSettings.CustomPosition == null
            )
            {
                PluginConfig tmpDefaultConfig = GetDefaultConfig();
                _config = tmpDefaultConfig;
                changes = true;
            }

            //1.2.1 update
            if (_config.Version == null || _config.Version < new VersionNumber(1, 2, 1))
            {
                PluginConfig tmpDefaultConfig = GetDefaultConfig();
                _config.GeneralSettings = tmpDefaultConfig.GeneralSettings;
                changes = true;
            }

            if(_config.Version != Version)
            {
                changes = true;
            }

            if (changes)
            {
                _config.Version = Version;

                PrintWarning("Config updated");
                SaveConfig();
            }
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            _config = Config.ReadObject<PluginConfig>();

            CheckForConfigUpdates();

            LoadColors();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(_config);
        }

        #endregion

        void OnServerInitialized()
        {
            if(!_config.GeneralSettings.ShowAmmoIcons)
            {
                if (!ImageLibrary)
                {
                    PrintError("The plugin is not installed on the server [ImageLibrary]");
                }
                else
                {
                    ImageLibrary.Call("AddImage", "https://www.dropbox.com/s/6y817vyw3je75ya/DvgPtiW.png?dl=1", "genammo");
                }
            }

            if (!_config.GeneralSettings.ShowThrowables)
            {
                Unsubscribe(nameof(OnExplosiveThrown));
                Unsubscribe(nameof(OnExplosiveDropped));
            }

            if (_config.GeneralSettings.DisableInventoryMonitor)
            {
                Unsubscribe(nameof(OnItemAddedToContainer));
                Unsubscribe(nameof(OnItemRemovedFromContainer));
            }

            RegisterMessages();
            
            permission.RegisterPermission(PERM_USE, this);
        }

        void Loaded()
        {
            LoadDefaultPresets();
            LoadData();
        }

        void Unload()
        {
            SaveData();
            foreach (var player in BasePlayer.activePlayerList)
                HideUI(player);
        }

        void RegisterMessages() => lang.RegisterMessages(messages, this);

        #region Data

        private void OnServerSave() => timer.Once(UnityEngine.Random.Range(5f, 10f), SaveData);

        private bool wiped { get; set; }

        private void OnNewSave(string filename) => wiped = true;

        private void SaveData()
        {
            if (pData != null)
                Interface.Oxide.DataFileSystem.WriteObject(nameof(AmmoHUD), pData);
        }

        public class PlayersData
        {
            public Dictionary<ulong, PlayerInfo> Players = new ();
        }

        public class PlayerInfo
        {
            public bool HUDActive = true;
            public string Position = "Default";

            [JsonIgnore] public bool WeaponActive;
        }

        public void LoadData()
        {
            pData = Interface.Oxide.DataFileSystem.ReadObject<PlayersData>(nameof(AmmoHUD));
            if (pData == null || wiped)
            {
                PrintWarning("No player data found! Creating a new data file");
                pData = new ();
                SaveData();
            }
        }

        public PlayerInfo GetPlayerInfo(ulong playerid)
        {
            if (!pData.Players.TryGetValue(playerid, out PlayerInfo playerInfo))
                pData.Players.Add(playerid, playerInfo = new (){ HUDActive = _config.PositionSettings.DefaultState });

            return playerInfo;
        }
        
        public PositionPreset GetPosition(PlayerInfo playerInfo)
        {
            if (Enum.TryParse(playerInfo.Position.ToLower(), out PositionEnum position))
                return PositionPresets[position];

            return GetConfigPosition();
        }

        #endregion

        #region Commands

        [ChatCommand("ammohud")]
        void AmmoHUDChatCMD(BasePlayer player, string command, string[] args)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;

            if (args.Length == 0)
            {
                BroadcastCommands(player);
                return;
            }

            string argument = args[0].ToLower().Trim();

            PlayerInfo playerInfo = GetPlayerInfo(player.userID);
            if (argument == "toggle")
            {
                playerInfo.HUDActive = !playerInfo.HUDActive;
                UpdateUI_All(player, playerInfo, player.GetHeldEntity() as AttackEntity);

                if (playerInfo.HUDActive)
                    Reply(player, lang.GetMessage("toggle_enabled", this, player.UserIDString));
                else
                    Reply(player, lang.GetMessage("toggle_disabled", this, player.UserIDString));
            }
            else if (argument.ToLower() == "default")
            {
                playerInfo.Position = argument;
                UpdateUI_All(player, playerInfo, player.GetHeldEntity() as AttackEntity);

                Reply(player, string.Format(lang.GetMessage("positionsetto", this, player.UserIDString), "Default"));
            }
            else
            {
                if (Enum.TryParse(argument.ToLower(), out PositionEnum position))
                {
                    playerInfo.Position = argument;
                    UpdateUI_All(player, playerInfo, player.GetHeldEntity() as AttackEntity);

                    Reply(player, string.Format(lang.GetMessage("positionsetto", this, player.UserIDString), position.ToString()));
                }
            }
        }

        public void BroadcastCommands(BasePlayer player)
        {
            string msg = string.Format(lang.GetMessage("commands", this, player.UserIDString), "<color=orange>");

            SendReply(player, msg);
        }

        #endregion

        #region Hooks

        void OnActiveItemChanged(BasePlayer player, Item oldItem, Item newItem)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;

            if (newItem == null || newItem.info.itemid == SNOWBALLGUN_ITEMID)
            {
                HideUI(player);
                return;
            }
            
            PlayerInfo playerInfo = GetPlayerInfo(player.userID);
            BaseEntity heldEntity = newItem.GetHeldEntity();
            if (heldEntity is BaseProjectile || (heldEntity is ThrownWeapon && _config.GeneralSettings.ShowThrowables && heldEntity.prefabID != SUPPLYSIGNAL_PREFABID))
            {
                if (!playerInfo.WeaponActive)
                    playerInfo.WeaponActive = true;
            }
            else if (playerInfo.WeaponActive)
            {
                playerInfo.WeaponActive = false;
                HideUI(player);
                return;
            }
            
            UpdateUI_All_Delayed(player, playerInfo);
        }

        void OnWeaponFired(BaseProjectile projectile, BasePlayer player, ItemModProjectile mod, ProtoBuf.ProjectileShoot projectiles)
        {
            if (player == null || projectile.prefabID == SNOWBALLGUN_PREFABID || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;

            UpdateUI_Partial(player, projectile, UpdateEnum.WeaponAmmo);
        }

        void OnRocketLaunched(BasePlayer player, BaseEntity entity)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;

            UpdateUI_Partial(player, player.GetHeldEntity() as AttackEntity, UpdateEnum.WeaponAmmo);
        }

        void OnAmmoUnload(BaseProjectile weapon, Item item, BasePlayer player)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;
            
            NextTick(() =>
            {
                if (player != null)
                    UpdateUI_Partial(player, weapon, UpdateEnum.AllAmmo);
            });
        }

        void OnAmmoSwitch(BaseProjectile weapon, BasePlayer player, ItemDefinition itemDefinition) => OnMagazineReload(weapon, null, player);

        void OnMagazineReload(BaseProjectile weapon, IAmmoContainer ammoSource, BasePlayer player)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;
            
            NextTick(() =>
            {
                if (player != null)
                    UpdateUI_All(player, weapon);
            });
        }

        void OnItemAddedToContainer(ItemContainer container, Item item)
        {
            if(container == null || !container.HasFlag(ItemContainer.Flag.IsPlayer))
                return;
                
            var player = container.playerOwner;
            if (player == null || player.UserIDString == null || !permission.UserHasPermission(player.UserIDString, PERM_USE))
                return;

            UpdateUI_All_Delayed(player);
        }

        void OnItemRemovedFromContainer(ItemContainer container, Item item) => OnItemAddedToContainer(container, item);

        void OnExplosiveThrown(BasePlayer player, BaseEntity entity)
        {
            if (player == null || !permission.UserHasPermission(player.UserIDString, PERM_USE) || entity is SupplySignal)
                return;

            NextTick(() =>
            {
                UpdateUI_Partial(player, player.GetHeldEntity() as AttackEntity, UpdateEnum.WeaponAmmo);
            });
        }

        void OnExplosiveDropped(BasePlayer player, BaseEntity entity) => OnExplosiveThrown(player, entity);
        
        private void OnUserPermissionGranted(string id, string permName)
        {
            if(permName != PERM_USE) return;
            
            var player = BasePlayer.FindByID(Convert.ToUInt64(id));
            if (player != null)
                OnActiveItemChanged(player, null, player.GetActiveItem());
        }

        private void OnUserPermissionRevoked(string id, string permName)
        {
            if(permName != PERM_USE) return;
            
            var player = BasePlayer.FindByID(Convert.ToUInt64(id));
            if (player != null)
                HideUI(player);
        }

        #endregion

        #region UI

        string defaultAmmoColor, redAmmoColor, orangeAmmoColor = "";

        string defaultIconColor, blueIconColor, redIconColor, blackIconColor, greyIconColor, greenIconColor = "";

        public void LoadColors()
        {
            defaultAmmoColor = GetColorFromHex("#ffffff", 80);
            redAmmoColor = GetColorFromHex("#ff3737", 80);
            orangeAmmoColor = GetColorFromHex("#ffa749", 80);

            defaultIconColor = GetColorFromHex("#ba9a51", 50);
            blueIconColor = GetColorFromHex("#3564c3", 50);
            redIconColor = GetColorFromHex("#d43224", 50);
            blackIconColor = GetColorFromHex("#1e1e1e", 50);
            greyIconColor = GetColorFromHex("#a1a1a1", 50);
            greenIconColor = GetColorFromHex("#57a14f", 50);
        }

        private enum UpdateEnum
        {
            WeaponAmmo,
            AvailableAmmo,
            AllAmmo
        }

        public string Layer_UI = "Under";
        public string RootName_UI = "ammohudui";
        
        private List<string> _pendingUpdates = new();

        private void UpdateUI_All_Delayed(BasePlayer player) => UpdateUI_All_Delayed(player, GetPlayerInfo(player.userID));
        private void UpdateUI_All_Delayed(BasePlayer player, PlayerInfo playerInfo)
        {
            if(_pendingUpdates.Contains(player.UserIDString)) return;
            
            _pendingUpdates.Add(player.UserIDString);
            
            timer.Once(0.2f, () =>
            {
                if (!_pendingUpdates.Contains(player.UserIDString)) return;

                UpdateUI_All(player, playerInfo, player.GetHeldEntity() as AttackEntity);
                _pendingUpdates.Remove(player.UserIDString);
            });
        }

        private void UpdateUI_All(BasePlayer player, AttackEntity attackEntity) => UpdateUI_All(player, GetPlayerInfo(player.userID), attackEntity);
        private void UpdateUI_Partial(BasePlayer player, AttackEntity attackEntity, UpdateEnum uiToUpdate) => UpdateUI_Partial(player, GetPlayerInfo(player.userID), attackEntity, uiToUpdate);

        private void UpdateUI_All(BasePlayer player, PlayerInfo playerInfo, AttackEntity attackEntity)
        {
            if (attackEntity == null || !playerInfo.HUDActive || !playerInfo.WeaponActive || attackEntity.prefabID == SNOWBALLGUN_PREFABID)
            {
                HideUI(player);
                return;
            }

            PositionPreset position = GetPosition(playerInfo);

            if (!position.ParentPosition.Enabled)
            {
                HideUI(player);
                return;
            }

            int weaponAmmo = 0;
            int availableAmmo = 0;

            ulong skin = 0;
            
            BaseProjectile projectile = attackEntity as BaseProjectile;
            ItemDefinition ammoItemDefinition;

            if(projectile != null)
            {
                weaponAmmo = projectile.primaryMagazine.contents;
                availableAmmo = projectile.GetAvailableAmmo();

                ammoItemDefinition = projectile.primaryMagazine.ammoType;
            }
            else
            {
                ThrownWeapon thrownWeapon = attackEntity as ThrownWeapon;
                Item cachedItem = attackEntity.GetCachedItem();

                if(thrownWeapon == null || cachedItem == null)
                    return;

                weaponAmmo = cachedItem.amount;
                availableAmmo = player.inventory.GetAmount(cachedItem.info) - weaponAmmo;

                if(availableAmmo <= 0)
                    availableAmmo = -1;

                skin = cachedItem.skin;
                ammoItemDefinition = cachedItem.info;
            }

            ///

            var cont = new CuiElementContainer();
            
            cont.Add(
                new CuiElement
                {
                    Parent = Layer_UI,
                    Name = RootName_UI,
                    DestroyUi = RootName_UI,
                    Components =
                    {
                        new CuiRectTransformComponent
                        {
                            AnchorMin = position.ParentPosition.AnchorMin,
                            AnchorMax = position.ParentPosition.AnchorMax,
                            OffsetMin = position.ParentPosition.OffsetMin,
                            OffsetMax = position.ParentPosition.OffsetMax
                        },
                        new CuiImageComponent() { Color = "0 0 1 0" }
                    }
                }
            );

            float outlineOpacity = 0;

            if(_config.GeneralSettings.ShowOutline)
                outlineOpacity = 0.3f;

            if (position.TotalAmmoPosition.Enabled)
            {
                if(availableAmmo >= 0)
                {
                    string availableAmmoTxt = string.Format("{0:n0}", availableAmmo);

                    cont.Add(
                        new CuiElement
                        {
                            Parent = RootName_UI,
                            Name = $"{RootName_UI}.availableAmmo",
                            Components =
                            {
                                new CuiTextComponent()
                                {
                                    Color = "1 1 1 0.5",
                                    Text = availableAmmoTxt,
                                    FontSize = position.TotalAmmoFontSize,
                                    Align = position.TotalAmmoTextAlignment,
                                    Font = "robotocondensed-bold.ttf"
                                },
                                new CuiRectTransformComponent
                                {
                                    AnchorMin = position.TotalAmmoPosition.AnchorMin,
                                    AnchorMax = position.TotalAmmoPosition.AnchorMax,
                                    OffsetMin = position.TotalAmmoPosition.OffsetMin,
                                    OffsetMax = position.TotalAmmoPosition.OffsetMax
                                },
                                new CuiOutlineComponent { Color = $"0 0 0 {outlineOpacity}", Distance = "1 -1" }
                            }
                        }
                    );
                }
            }

            if (position.WeaponAmmoPosition.Enabled)
            {
                string ammoColor = projectile != null ? GetAmmoColor(weaponAmmo, projectile.primaryMagazine.capacity) : defaultAmmoColor;

                cont.Add(
                    new CuiElement
                    {
                        Parent = RootName_UI,
                        Name = $"{RootName_UI}.weaponAmmo",
                        Components =
                        {
                            new CuiTextComponent()
                            {
                                Color = ammoColor,
                                Text = $"{weaponAmmo}",
                                FontSize = position.WeaponAmmoFontSize,
                                Align = position.WeaponAmmoTextAlignment,
                                Font = "robotocondensed-bold.ttf"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = position.WeaponAmmoPosition.AnchorMin,
                                AnchorMax = position.WeaponAmmoPosition.AnchorMax,
                                OffsetMin = position.WeaponAmmoPosition.OffsetMin,
                                OffsetMax = position.WeaponAmmoPosition.OffsetMax
                            },
                            new CuiOutlineComponent { Color = $"0 0 0 {outlineOpacity}", Distance = "1 -1" }
                        }
                    }
                );
            }

            if (position.IconPosition.Enabled)
            {
                string iconColor = defaultIconColor;
                
                if(_config.GeneralSettings.ShowAmmoIcons)
                {
                    cont.Add(
                        new CuiElement
                        {
                            Parent = RootName_UI,
                            Name = $"{RootName_UI}.icon",
                            Components =
                            {
                                new CuiRectTransformComponent
                                {
                                    AnchorMin = position.IconPosition.AnchorMin,
                                    AnchorMax = position.IconPosition.AnchorMax,
                                    OffsetMin = position.IconPosition.OffsetMin,
                                    OffsetMax = position.IconPosition.OffsetMax
                                },
                                new CuiImageComponent() { Color = "1 1 1 0.2" }
                            }
                        }
                    );
                    
                    cont.Add(
                        new CuiElement
                        {
                            Parent = $"{RootName_UI}.icon",
                            Name = $"{RootName_UI}.icon.image",
                            Components =
                            {
                                new CuiRectTransformComponent
                                {
                                    AnchorMin = "0.1 0.1",
                                    AnchorMax = "0.9 0.9"
                                },
                                new CuiImageComponent() { ItemId = ammoItemDefinition.itemid, SkinId = skin }
                            }
                        }
                    );
                }
                else
                {
                    if (ammoItemDefinition.shortname.Contains(".hv") || ammoItemDefinition.shortname.Contains("shotgun.fire"))
                    {
                        iconColor = blueIconColor;
                    }
                    else if (
                        ammoItemDefinition.shortname.Contains("incen")
                        || ammoItemDefinition.shortname.Contains("fire")
                        || ammoItemDefinition.shortname == "ammo.shotgun"
                        || ammoItemDefinition.shortname.Contains("buckshot")
                    )
                    {
                        iconColor = redIconColor;
                    }
                    else if (ammoItemDefinition.shortname.Contains("explo") || ammoItemDefinition.shortname.Contains("grenadelauncher.he"))
                    {
                        iconColor = blackIconColor;
                    }
                    else if (ammoItemDefinition.shortname.Contains("smoke"))
                    {
                        iconColor = greyIconColor;
                    }
                    else if (ammoItemDefinition.shortname.Contains("slug"))
                    {
                        iconColor = greenIconColor;
                    }

                    cont.Add(
                        new CuiElement
                        {
                            Parent = RootName_UI,
                            Name = $"{RootName_UI}.icon",
                            Components =
                            {
                                new CuiRawImageComponent
                                {
                                    Color = iconColor,
                                    Png = GetImg("genammo")
                                },
                                new CuiRectTransformComponent
                                {
                                    AnchorMin = position.IconPosition.AnchorMin,
                                    AnchorMax = position.IconPosition.AnchorMax,
                                    OffsetMin = position.IconPosition.OffsetMin,
                                    OffsetMax = position.IconPosition.OffsetMax
                                }
                            }
                        }
                    );
                }
            }
            CuiHelper.AddUi(player, cont);
        }

        private void UpdateUI_Partial(BasePlayer player, PlayerInfo playerInfo, AttackEntity attackEntity, UpdateEnum uiToUpdate)
        {
            if(attackEntity == null || !playerInfo.HUDActive || !playerInfo.WeaponActive) return;
            
            int weaponAmmo = 0;
            int availableAmmo = 0;

            BaseProjectile projectile = attackEntity as BaseProjectile;
            if(projectile != null)
            {
                if (uiToUpdate == UpdateEnum.AllAmmo || uiToUpdate == UpdateEnum.WeaponAmmo)
                    weaponAmmo = projectile.primaryMagazine.contents;

                if (uiToUpdate == UpdateEnum.AllAmmo || uiToUpdate == UpdateEnum.AvailableAmmo)
                    availableAmmo = projectile.GetAvailableAmmo();
            }
            else 
            {
                if(!_config.GeneralSettings.ShowThrowables) return;
                
                ThrownWeapon thrownWeapon = attackEntity as ThrownWeapon;
                Item cachedItem = attackEntity.GetCachedItem();

                if(thrownWeapon == null || cachedItem == null)
                    return;

                if (uiToUpdate == UpdateEnum.AllAmmo || uiToUpdate == UpdateEnum.WeaponAmmo)
                    weaponAmmo = cachedItem.amount;

                if (uiToUpdate == UpdateEnum.AllAmmo || uiToUpdate == UpdateEnum.AvailableAmmo)
                {
                    availableAmmo = player.inventory.GetAmount(cachedItem.info) - weaponAmmo;

                    if(availableAmmo <= 0)
                        availableAmmo = -1;
                }
            }
            
            CuiElementContainer cont = new();
            
            if (uiToUpdate == UpdateEnum.WeaponAmmo || uiToUpdate == UpdateEnum.AllAmmo)
            {
                //update weapon ammo text
                string ammoColor = projectile != null ? GetAmmoColor(weaponAmmo, projectile.primaryMagazine.capacity) : defaultAmmoColor;

                cont.Add(new CuiElement
                {
                    Name = $"{RootName_UI}.weaponAmmo",
                    Components =
                    {
                        new CuiTextComponent()
                        {
                            Color = ammoColor,
                            Text = $"{weaponAmmo}"
                        }
                    },
                    Update = true
                });
            }
            
            if ((uiToUpdate == UpdateEnum.AvailableAmmo || uiToUpdate == UpdateEnum.AllAmmo) && availableAmmo >= 0)
            {
                //update available ammo text
                cont.Add(
                    new CuiElement
                    {
                        Name = $"{RootName_UI}.availableAmmo",
                        Components =
                        {
                            new CuiTextComponent()
                            {
                                Text = string.Format("{0:n0}", availableAmmo)
                            }
                        },
                        Update = true
                    }
                );
            }
            
            CuiHelper.AddUi(player, cont);
        }
        
        private void HideUI(BasePlayer player)
        {
            CuiHelper.DestroyUi(player, RootName_UI);
            _pendingUpdates.Remove(player.UserIDString);
        }

        #endregion

        #region Helpers

        private string GetAmmoColor(int magAmmo, int magCapacity)
        {
            string ammoColor = defaultAmmoColor;

            float ammoRatio = (float)magAmmo / magCapacity;

            if (ammoRatio < 0.6f)
                ammoColor = orangeAmmoColor;
            if (ammoRatio < 0.3f)
                ammoColor = redAmmoColor;

            return ammoColor;
        }
        
        private string GetColorFromHex(string hex, double alpha)
        {
            if (string.IsNullOrEmpty(hex))
                hex = "#FFFFFF";

            var str = hex.Trim('#');
            if (str.Length != 6)
                throw new Exception(hex);
            var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
            var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
            var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);

            return $"{(double)r / 255} {(double)g / 255} {(double)b / 255} {alpha / 100}";
        }

        private string GetImg(string name)
        {
            return (string)ImageLibrary?.Call("GetImage", name) ?? "";
        }

        public void Reply(BasePlayer player, string msg) => PrintToChat(player, $"<color=orange>[AmmoHUD]</color> {msg}");
        
        #endregion

        #region Lang
        Dictionary<string, string> messages = new Dictionary<string, string>() 
        {
            {"commands", "<color=orange>Ammo HUD Available Commands:</color>\n"
                + "/ammohud toggle\t\tToggles on/off\n"
                + "/ammohud [position]\t\tRepositions on screen\n\n"
                + "<color=orange>Available Positions:</color>\nDefault, Top, Bottom, Right, Left,\n"
                + "BottomRight, BottomLeft, TopRight, TopLeft"},
            {"toggle_enabled", "HUD enabled."},
            {"toggle_disabled", "HUD disabled."},
            {"positionsetto", "HUD position set to {0}."}
        };
        #endregion

        #region PositionPresets

        public PositionPreset GetConfigPosition()
        {
            if (_config.PositionSettings.CustomPositionSettings.Enabled)
                return _config.PositionSettings.CustomPositionSettings.CustomPosition;
            else
                if (Enum.TryParse(_config.PositionSettings.Position.ToLower(), out PositionEnum position))
                    return PositionPresets[position];
                else
                    return null;
        }

        public void LoadDefaultPresets()
        {
            PositionPresets = new ()
            {
                {
                    PositionEnum.right,
                    new ()
                    {
                        Position = PositionEnum.right,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.5",
                            AnchorMax = "1 0.5",
                            OffsetMin = "-165 -35",
                            OffsetMax = "-15 35"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "0 1",
                            OffsetMax = "-35 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerRight,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.13",
                            AnchorMax = "1 0.13",
                            OffsetMin = "-30 0",
                            OffsetMax = "0 30"
                        }
                    }
                },
                {
                    PositionEnum.bottomright,
                    new ()
                    {
                        Position = PositionEnum.bottomright,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0",
                            AnchorMax = "1 0",
                            OffsetMin = "-355 25",
                            OffsetMax = "-215 90"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "0 1",
                            OffsetMax = "-35 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerRight,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.13",
                            AnchorMax = "1 0.13",
                            OffsetMin = "-30 0",
                            OffsetMax = "0 30"
                        }
                    }
                },
                {
                    PositionEnum.bottom,
                    new ()
                    {
                        Position = PositionEnum.bottom,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0.5 0",
                            AnchorMax = "0.5 0",
                            OffsetMin = "-120 80",
                            OffsetMax = "20 145"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "0 1",
                            OffsetMax = "-35 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerRight,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.13",
                            AnchorMax = "1 0.13",
                            OffsetMin = "-30 0",
                            OffsetMax = "0 30"
                        }
                    }
                },
                {
                    PositionEnum.bottomleft,
                    new ()
                    {
                        Position = PositionEnum.bottomleft,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "0 0",
                            OffsetMin = "15 60",
                            OffsetMax = "155 125"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerLeft,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "35 0",
                            OffsetMax = "0 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerLeft,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.12",
                            AnchorMax = "0 0.12",
                            OffsetMin = "0 0",
                            OffsetMax = "30 30"
                        }
                    }
                },
                {
                    PositionEnum.left,
                    new ()
                    {
                        Position = PositionEnum.left,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.5",
                            AnchorMax = "0 0.5",
                            OffsetMin = "15 -32",
                            OffsetMax = "155 33"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerLeft,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "35 0",
                            OffsetMax = "0 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerLeft,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.12",
                            AnchorMax = "0 0.12",
                            OffsetMin = "0 0",
                            OffsetMax = "30 30"
                        }
                    }
                },
                {
                    PositionEnum.topleft,
                    new ()
                    {
                        Position = PositionEnum.topleft,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 1",
                            AnchorMax = "0 1",
                            OffsetMin = "15 -95",
                            OffsetMax = "155 -30"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerLeft,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "35 0",
                            OffsetMax = "0 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerLeft,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.12",
                            AnchorMax = "0 0.12",
                            OffsetMin = "0 0",
                            OffsetMax = "30 30"
                        }
                    }
                },
                {
                    PositionEnum.top,
                    new ()
                    {
                        Position = PositionEnum.top,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0.5 1",
                            AnchorMax = "0.5 1",
                            OffsetMin = "-110 -95",
                            OffsetMax = "30 -30"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "0 1",
                            OffsetMax = "-35 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerRight,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.13",
                            AnchorMax = "1 0.13",
                            OffsetMin = "-30 0",
                            OffsetMax = "0 30"
                        }
                    }
                },
                {
                    PositionEnum.topright,
                    new ()
                    {
                        Position = PositionEnum.topright,
                        ParentPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 1",
                            AnchorMax = "1 1",
                            OffsetMin = "-155 -95",
                            OffsetMax = "-15 -30"
                        },
                        WeaponAmmoFontSize = 36,
                        WeaponAmmoTextAlignment = TextAnchor.LowerRight,
                        WeaponAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0",
                            AnchorMax = "1 0.70",
                            OffsetMin = "0 1",
                            OffsetMax = "-35 0"
                        },
                        TotalAmmoFontSize = 22,
                        TotalAmmoTextAlignment = TextAnchor.LowerRight,
                        TotalAmmoPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "0 0.55",
                            AnchorMax = "1 1",
                            OffsetMin = "0 0",
                            OffsetMax = "0 0"
                        },
                        IconPosition = new PositionParams()
                        {
                            Enabled = true,
                            AnchorMin = "1 0.13",
                            AnchorMax = "1 0.13",
                            OffsetMin = "-30 0",
                            OffsetMax = "0 30"
                        }
                    }
                }
            };
        }

        public Dictionary<PositionEnum, PositionPreset> PositionPresets;

        public class PositionPreset
        {
            [JsonIgnore]
            public PositionEnum Position { get; set; }
            public PositionParams ParentPosition { get; set; }
            public PositionParams WeaponAmmoPosition { get; set; }
            public int WeaponAmmoFontSize { get; set; }
            public TextAnchor WeaponAmmoTextAlignment { get; set; }
            public PositionParams TotalAmmoPosition { get; set; }
            public int TotalAmmoFontSize { get; set; }
            public TextAnchor TotalAmmoTextAlignment { get; set; }
            public PositionParams IconPosition { get; set; }
        }

        public class PositionParams
        {
            public bool Enabled { get; set; }
            public string AnchorMin { get; set; }
            public string AnchorMax { get; set; }
            public string OffsetMin { get; set; }
            public string OffsetMax { get; set; }
        }

        public enum PositionEnum
        {
            top,
            topleft,
            topright,
            left,
            right,
            bottom,
            bottomleft,
            bottomright
        }

        #endregion
    }
}
