﻿/***********************************************************************************************************************/
/*** DO NOT edit this file! Edit the files under `oxide/config` and/or `oxide/lang`, created once plugin has loaded. ***/
/*** Please note, support cannot be provided if the plugin has been modified. Please use a fresh copy if modified.   ***/
/***********************************************************************************************************************/

//#define DEBUG

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;

// TODO: Add optional arg to put items in specific slots
// TODO: Add optional arg to use a custom name for items (Rust only)

namespace Oxide.Plugins
{
#if SEVENDAYSTODIE
    using UnityEngine;
#elif REIGNOFKINGS
    using CodeHatch.Engine.Networking;
    using CodeHatch.Inventory.Blueprints;
    using CodeHatch.Inventory.Blueprints.Components;
    using CodeHatch.ItemContainer;
    using UnityEngine;
#endif

    [Info("Give", "Wulf", "3.4.1")]
    [Description("Allows players with permission to give items or kits")]
    public class Give : CovalencePlugin
    {
        #region Configuration

        private Configuration _config;

        public class Configuration
        {
            // TODO: Add optional cooldown for commands

            [JsonProperty("Item blacklist (name or item ID)", ObjectCreationHandling = ObjectCreationHandling.Replace)]
            public List<string> ItemBlacklist = new List<string>();

            [JsonProperty("Log usage to console")]
            public bool LogToConsole = false;

            //[JsonProperty("Log usage to file")] // TODO: Implement logging options
            //public bool LogToFile = false;

            //[JsonProperty("Rotate logs daily")] // TODO: Implement logging options
            //public bool RotateLogs = true;

            [JsonProperty("Show chat notices")]
            public bool ShowChatNotices = false;
#if RUST
            [JsonProperty("Show popup notices")]
            public bool ShowPopupNotices = false;
#endif
            public string ToJson() => JsonConvert.SerializeObject(this);

            public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
        }

        protected override void LoadDefaultConfig() => _config = new Configuration();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                if (_config == null)
                {
                    throw new JsonException();
                }

                if (!_config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
                {
                    LogWarning("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
            }
            catch
            {
                LogWarning($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
        }

        protected override void SaveConfig()
        {
            LogWarning($"Configuration changes saved to {Name}.json");
            Config.WriteObject(_config, true);
        }

        #endregion Configuration

        #region Localization

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["CommandGive"] = "give",
                ["CommandGiveAll"] = "giveall",
                ["CommandGiveArm"] = "givearm",
                ["CommandGiveTo"] = "giveto",
                ["CommandGiveKit"] = "givekit",
                ["CommandGiveKitTo"] = "givekitto",
                ["CommandGiveKitAll"] = "givekitall",
                ["GiveKitAllFail"] = "Could not give kit '{0}' to {1} player(s)",
                ["GiveKitAllSuccess"] = "Gave kit '{0}' to {1} player(s)",
                ["GiveKitFail"] = "Could not give kit '{0}' to yourself",
                ["GiveKitSuccess"] = "Gave kit '{0}' to yourself",
                ["GiveKitToFail"] = "Could not give kit '{0}' to '{1}'",
                ["GiveKitToSuccess"] = "Gave kit '{0}' to '{1}'",
                ["GiveAllFail"] = "Could not give item {0} x {1} to {2} player(s)",
                ["GiveAllSuccess"] = "Gave item {0} x {1} to {2} player(s)",
                ["GiveFail"] = "Could not give item {0} x {1} to yourself",
                ["GiveSuccess"] = "Gave item {0} x {1} to yourself",
                ["GiveToFail"] = "Could not give item {0} x {1} to '{2}'",
                ["GiveToSuccess"] = "Gave item {0} x {1} to '{2}'",
                ["InvalidItem"] = "{0} is not a valid item or is blacklisted",
                ["InvalidKit"] = "{0} is not a valid kit",
                ["ItemNotFound"] = "Could not find any item by name or ID '{0}' to give",
                ["ItemReceived"] = "You've received {0} x {1}",
                ["KitsUnavailable"] = "Kits plugin is not installed or is not loaded",
                ["NotAllowed"] = "You are not allowed to use the '{0}' command",
                ["NoPlayersConnected"] = "There are no players connected to give items to",
                ["NoPlayersFound"] = "No players found with name or ID '{0}'",
                ["PlayersFound"] = "Multiple players were found, please specify: {0}",
                ["PlayersOnly"] = "Command '{0}' can only be used by a player",
                ["PlayerNotConnected"] = "Unable to give to player '{0} ({1})', not connected",
#if RUST
                ["UsageGive"] = "Usage: {0} <item id or name> [amount] [condition fraction] [skin id]",
                ["UsageGiveAll"] = "Usage: {0} <item id or name> [amount] [skin id]",
                ["UsageGiveTo"] = "Usage: {0} <player id or name> <item id or name> [amount] [skin id]",
                ["UsageGiveArm"] = "Usage: {0} <item id or name> [amount] [skin id]",
#else
                ["UsageGive"] = "Usage: {0} <item id or name> [amount]",
                ["UsageGiveAll"] = "Usage: {0} <item id or name> [amount]",
                ["UsageGiveTo"] = "Usage: {0} <player id or name> <item id or name> [amount]",
                ["UsageGiveArm"] = "Usage: {0} <item id or name> [amount]",
#endif
                ["UsageGiveKit"] = "Usage: {0} <kit name>",
                ["UsageGiveKitTo"] = "Usage: {0} <player id or name> <kit name>",
                ["UsageGiveKitAll"] = "Usage: {0} <kit name>"
            }, this);
        }

        #endregion Localization

        #region Initialization

#if HURTWORLD
        [PluginReference("HWKits")]
        private Plugin Kits;
#else
        [PluginReference]
        private Plugin Kits;
#endif

        private const string PermissionGive = "give.self";
        private const string PermissionGiveAll = "give.all";
#if REIGNOFKINGS || RUST
        private const string PermissionGiveArm = "give.arm";
#endif
        private const string PermissionGiveTo = "give.to";
        private const string PermissionGiveKit = "give.kit";
        private const string PermissionGiveKitAll = "give.kitall";
        private const string PermissionGiveKitTo = "give.kitto";
        private const string PermissionBypassBlacklist = "give.bypassblacklist";

        private void Init()
        {
            permission.RegisterPermission(PermissionGive, this);
            permission.RegisterPermission(PermissionGiveAll, this);
#if REIGNOFKINGS || RUST
            permission.RegisterPermission(PermissionGiveArm, this);
#endif
            permission.RegisterPermission(PermissionGiveTo, this);
            permission.RegisterPermission(PermissionGiveKit, this);
            permission.RegisterPermission(PermissionGiveKitAll, this);
            permission.RegisterPermission(PermissionGiveKitTo, this);
            permission.RegisterPermission(PermissionBypassBlacklist, this);

            AddLocalizedCommand(nameof(CommandGive));
            AddCovalenceCommand(new[] { "inventory.give", "inventory.giveid", "giveid" }, nameof(CommandGive));
            AddLocalizedCommand(nameof(CommandGiveAll));
            AddCovalenceCommand("inventory.giveall", nameof(CommandGiveAll));
            AddLocalizedCommand(nameof(CommandGiveAll));
#if REIGNOFKINGS || RUST
            AddCovalenceCommand("inventory.givearm", nameof(CommandGiveArm));
#endif
            AddLocalizedCommand(nameof(CommandGiveTo));
            AddCovalenceCommand("inventory.giveto", nameof(CommandGiveTo));
            AddLocalizedCommand(nameof(CommandGiveKit));
            AddCovalenceCommand("inventory.givekit", nameof(CommandGiveKit));
            AddLocalizedCommand(nameof(CommandGiveKitAll));
            AddCovalenceCommand("inventory.givekitall", nameof(CommandGiveKitAll));
            AddLocalizedCommand(nameof(CommandGiveKitTo));
            AddCovalenceCommand("inventory.givekitto", nameof(CommandGiveKitTo));
        }

        #endregion Initialization

        #region Item Giving

#if HURTWORLD
        private T GetObjectByGuid<T>(string guid) where T : UnityEngine.Object
        {
            HurtAssetDatabaseRecord databaseRecord;
            if (RuntimeHurtDB.Instance._objectDatabase.TryGetValue(guid, out databaseRecord))
            {
                return databaseRecord.Object as T;
            }

            return null;
        }

        private ItemGeneratorAsset FindItem(string itemNameOrId)
        {
            ItemGeneratorAsset item;
            int itemId;

            if (int.TryParse(itemNameOrId, out itemId))
            {
                item = GetItemGeneratorAsset(null, itemId);
            }
            else
            {
                item = GetObjectByGuid<ItemGeneratorAsset>(itemNameOrId);
                if (item == null)
                {
                    item = GetItemGeneratorAsset(itemNameOrId);
                }
            }

            return item;
        }

        private ItemGeneratorAsset GetItemGeneratorAsset(string itemName, int itemId = -1)
        {
            foreach (KeyValuePair<int, ItemGeneratorAsset> itemGenerator in GlobalItemManager.Instance.GetGenerators())
            {
                if (itemId != -1 && itemGenerator.Value.GeneratorId == itemId)
                {
                    return itemGenerator.Value;
                }

                if (itemName != null && (itemName == itemGenerator.Value.DataProvider.NameKey || itemGenerator.Value.DataProvider.NameKey.ToLower().Contains(itemName)))
                {
                    return itemGenerator.Value;
                }
            }

            return null;
        }
#elif RUST
        private ItemDefinition FindItem(string itemNameOrId)
        {
            ItemDefinition itemDef = ItemManager.FindItemDefinition(itemNameOrId.ToLower());
            if (itemDef == null)
            {
                int itemId;
                if (int.TryParse(itemNameOrId, out itemId))
                {
                    itemDef = ItemManager.FindItemDefinition(itemId);
                }
            }
            return itemDef;
        }
#endif

        private object GiveItem(IPlayer player, string itemNameOrId, int amount = 1, string container = "main", float condition = 1f, ulong skinId = 0uL)
        {
            if (!player.IsConnected)
            {
                return false;
            }

            if (_config.ItemBlacklist.Contains(itemNameOrId, StringComparer.OrdinalIgnoreCase) && !player.HasPermission(PermissionBypassBlacklist))
            {
                return null;
            }

            string itemName = itemNameOrId;
#if SEVENDAYSTODIE
            EntityPlayer entityPlayer = player.Object as EntityPlayer;
            if (entityPlayer == null)
            {
                return false;
            }

            ItemValue itemValue = ItemClass.GetItem(itemName, true);
            if (itemValue.type == ItemValue.None.type)
            {
                return null;
            }

            ClientInfo clientInfo = ConsoleHelper.ParseParamEntityIdToClientInfo(entityPlayer.entityId.ToString());
            if (clientInfo != null)
            {
                if (!entityPlayer.IsSpawned() || entityPlayer.IsDead())
                {
                    return false;
                }

                EntityItem entityItem = (EntityItem)EntityFactory.CreateEntity(new EntityCreationData
                {
                    entityClass = EntityClass.FromString("item"),
                    id = EntityFactory.nextEntityID++,
                    itemStack = new ItemStack(new ItemValue(itemValue.type, 6, 6), amount),
                    pos = entityPlayer.position,
                    rot = new Vector3(20f, 0f, 20f),
                    lifetime = 60f,
                    belongsPlayerId = clientInfo.entityId
                });
                GameManager.Instance.World.SpawnEntityInWorld(entityItem);
                clientInfo.SendPackage(NetPackageManager.GetPackage<NetPackageEntityCollect>().Setup(entityItem.entityId, clientInfo.entityId));
                GameManager.Instance.World.RemoveEntity(entityItem.entityId, EnumRemoveEntityReason.Despawned);
                itemName = itemValue.ItemClass.GetLocalizedItemName() ?? itemValue.ItemClass.Name;
            }
#elif HURTWORLD
            ItemGeneratorAsset generator = FindItem(itemName.ToLower());
            if (generator == null)
            {
                return null;
            }

            PlayerSession session = player.Object as PlayerSession;
            if (session == null)
            {
                return false;
            }

            PlayerInventory inventory = session.WorldPlayerEntity.GetComponent<PlayerInventory>();
            ItemObject itemObj;
            for (int i = 0; amount > 0 && (amount <= generator.DataProvider.MaxStackSize || generator.DataProvider.MaxStackSize <= amount); i++)
            {
                itemObj = GlobalItemManager.Instance.CreateItem(generator, generator.DataProvider.MaxStackSize < amount ? generator.DataProvider.MaxStackSize : amount);
                if (!inventory.GiveItemServer(itemObj))
                {
                    GlobalItemManager.SpawnWorldItem(itemObj, inventory);
                }
                amount -= generator.DataProvider.MaxStackSize < amount ? generator.DataProvider.MaxStackSize : amount;
            }
#elif REIGNOFKINGS
            Player rokPlayer = player.Object as Player;
            if (rokPlayer == null)
            {
                return false;
            }

            Container itemContainer;
            switch (container.ToLower())
            {
                case "belt":
                    itemContainer = rokPlayer.CurrentCharacter.Entity.GetContainerOfType(CollectionTypes.Hotbar);
                    break;

                default:
                    itemContainer = rokPlayer.CurrentCharacter.Entity.GetContainerOfType(CollectionTypes.Inventory);
                    break;
            }

            InvItemBlueprint blueprint = InvDefinitions.Instance.Blueprints.GetBlueprintForName(itemName, true, true);
            if (blueprint == null)
            {
                return null;
            }

            ContainerManagement containerManagement = blueprint.TryGet<ContainerManagement>();
            int stackableAmount = containerManagement != null ? containerManagement.StackLimit : 0;
            int amountGiven = 0;
            while (amountGiven < amount)
            {
                int amountToGive = Mathf.Min(stackableAmount, amount - amountGiven);
                InvGameItemStack itemStack = new InvGameItemStack(blueprint, amountToGive, null);
                if (!ItemCollection.AutoMergeAdd(itemContainer.Contents, itemStack))
                {
                    int stackAmount = amountToGive - itemStack.StackAmount;
                    if (stackAmount != 0)
                    {
                        amountGiven += stackAmount;
                    }
                    else
                    {
                        break;
                    }
                }
                else
                {
                    amountGiven += amountToGive;
                }
                if (itemContainer.Contents.FreeSlotCount == 0)
                {
                    break;
                }
            }
#elif RUST
            Item item = ItemManager.Create(FindItem(itemNameOrId), amount, skinId);
            if (item == null)
            {
                return null;
            }

            BasePlayer basePlayer = player.Object as BasePlayer;
            if (basePlayer == null)
            {
                return false;
            }

            ItemContainer itemContainer = null;
            switch (container.ToLower())
            {
                case "belt":
                    itemContainer = basePlayer.inventory.containerBelt;
                    break;

                default:
                    itemContainer = basePlayer.inventory.containerMain;
                    break;
            }

            item.amount = amount;
            item.conditionNormalized = condition;
            item.OnVirginSpawn();
            if (!item.MoveToContainer(itemContainer) && !basePlayer.inventory.GiveItem(item))
            {
                item.Remove();
                return false;
            }

            itemName = item.info.displayName.english;
            if (_config.ShowPopupNotices)
            {
                player.Command("note.inv", item.info.itemid, amount);
                player.Command("gametip.showgametip", GetLang("ItemReceived", player.Id, itemName, amount));
                timer.Once(2f, () => player.Command("gametip.hidegametip"));
            }
#endif
            if (_config.ShowChatNotices)
            {
                Message(player, "ItemReceived", itemName, amount);
            }
            if (_config.LogToConsole)
            {
                Log($"{player.Name} {amount} x {itemName}");
            }

            return true;
        }

        private void CommandGive(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission(PermissionGive))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 1)
            {
                Message(player, "UsageGive", command);
                return;
            }

            if (player.IsServer)
            {
                Message(player, "PlayersOnly", command);
                return;
            }

            int amount = args.Length >= 2 && int.TryParse(args[1], out amount) ? amount : 1;
            float condition = args.Length >= 3 && float.TryParse(args[2], out condition) ? condition : 1f; // Currently only Rust
            ulong skinId = args.Length >= 4 && ulong.TryParse(args[3], out skinId) ? skinId : 0uL; // Currently only Rust
            object giveItem = GiveItem(player, args[0], amount, condition: condition, skinId: skinId);
            if (giveItem == null)
            {
                Message(player, "InvalidItem", args[0]);
            }
            else if (!(bool)giveItem)
            {
                Message(player, "GiveFail", args[0], amount);
            }
            else
            {
                Message(player, "GiveSuccess", args[0], amount);
            }
        }

        private void CommandGiveTo(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission(PermissionGiveTo))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 2)
            {
                Message(player, "UsageGiveTo", command);
                return;
            }

            IPlayer target = FindPlayer(args[0], player);
            if (target == null)
            {
                target = player;
                if (target.IsServer)
                {
                    Message(player, "PlayersOnly", command);
                    return;
                }
            }

            int amount = args.Length >= 3 && int.TryParse(args[2], out amount) ? amount : 1;
            ulong skinId = args.Length >= 4 && ulong.TryParse(args[3], out skinId) ? skinId : 0uL; // Currently only Rust
            object giveItem = GiveItem(target, args[1], amount, skinId: skinId);
            if (giveItem == null)
            {
                Message(player, "InvalidItem", args[1]);
            }
            else if (!(bool)giveItem)
            {
                Message(player, "GiveToFail", args[1], amount, target.Name);
            }
            else
            {
                Message(player, "GiveToSuccess", args[1], amount, target.Name);
            }
        }

        private void CommandGiveAll(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission(PermissionGiveAll))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 1)
            {
                Message(player, "UsageGive", command);
                return;
            }

            if (!players.Connected.Any())
            {
                Message(player, "NoPlayersConnected");
                return;
            }

            int itemsGiven = 0;
            int connectedPlayers = 0;
            int amount = args.Length >= 2 && int.TryParse(args[1], out amount) ? amount : 1;
            ulong skinId = args.Length >= 3 && ulong.TryParse(args[2], out skinId) ? skinId : 0uL; // Currently only Rust
            foreach (IPlayer target in players.Connected.ToArray())
            {
                object itemGiven = GiveItem(target, args[0], amount, skinId: skinId);
                if (itemGiven is bool && (bool)itemGiven)
                {
                    itemsGiven++;
                    connectedPlayers++;
                }
            }
            Message(player, itemsGiven > 0 ? "GiveAllSuccess" : "GiveAllFail", args[0], amount, connectedPlayers);
        }

#if REIGNOFKINGS || RUST
        private void CommandGiveArm(IPlayer player, string command, string[] args)
        {
            if (!player.HasPermission(PermissionGiveArm))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 1)
            {
                Message(player, "UsageGiveArm", command);
                return;
            }

            int amount = args.Length >= 2 && int.TryParse(args[1], out amount) ? amount : 1;
            ulong skinId = args.Length >= 3 && ulong.TryParse(args[2], out skinId) ? skinId : 0uL; // Currently only Rust
            object giveItem = GiveItem(player, args[0], amount, "belt", skinId: skinId);
            if (giveItem == null)
            {
                Message(player, "InvalidItem", args[0]);
            }
            else if (!(bool)giveItem)
            {
                Message(player, "GiveFail", args[0], amount);
            }
            else
            {
                Message(player, "GiveSuccess", args[0], amount);
            }
        }
#endif

        #endregion Item Giving

        #region Kit Giving

        private bool TryGiveKit(IPlayer player, string kitName)
        {
            if (!Kits.Call<bool>("isKit", kitName))
            {
                Message(player, "InvalidKit", kitName);
                return false;
            }

            if (!player.IsConnected)
            {
                Message(player, "PlayerNotConnected", player.Name, player.Id);
                return false;
            }

            bool giveKit = false;
#if HURTWORLD
            giveKit = Kits.Call<bool>("GiveKit", player.Object as PlayerSession, kitName);
#elif REIGNOFKINGS
            giveKit = Kits.Call<bool>("GiveKit", player.Object as Player, kitName);
#elif RUST
            object kitGiven = Kits.Call<object>("GiveKit", player.Object as BasePlayer, kitName);
            giveKit = kitGiven is bool ? (bool)kitGiven : kitGiven == null;
#elif SEVENDAYSTODIE
            giveKit = Kits.Call<bool>("GiveKit", player.Object as EntityAlive, kitName);
#endif
            return giveKit;
        }

        private void CommandGiveKit(IPlayer player, string command, string[] args)
        {
            if (Kits == null || !Kits.IsLoaded)
            {
                Message(player, "KitsUnavailable");
                return;
            }

            if (!player.HasPermission(PermissionGiveKit))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 1)
            {
                Message(player, "UsageGiveKit", command);
                return;
            }

            if (player.IsServer)
            {
                Message(player, "PlayersOnly", command);
                return;
            }

            bool kitGiven = TryGiveKit(player, args[0]);
            Message(player, kitGiven ? "GiveKitSuccess" : "GiveKitFail", args[0]);
        }

        private void CommandGiveKitTo(IPlayer player, string command, string[] args)
        {
            if (Kits == null || !Kits.IsLoaded)
            {
                Message(player, "KitsUnavailable");
                return;
            }

            if (!player.HasPermission(PermissionGiveKitTo))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 2)
            {
                Message(player, "UsageGiveKitTo", command);
                return;
            }

            IPlayer target = FindPlayer(args[0], player);
            if (target != null)
            {
                bool kitGiven = TryGiveKit(target, args[1]);
                Message(player, kitGiven ? "GiveKitToSuccess" : "GiveKitToFail", args[1], target.Name);
            }
        }

        private void CommandGiveKitAll(IPlayer player, string command, string[] args)
        {
            if (Kits == null || !Kits.IsLoaded)
            {
                Message(player, "KitsUnavailable");
                return;
            }

            if (!player.HasPermission(PermissionGiveKitAll))
            {
                Message(player, "NotAllowed", command);
                return;
            }

            if (args.Length < 1)
            {
                Message(player, "UsageGiveKitAll", command);
                return;
            }

            if (!Kits.Call<bool>("isKit", args[0]))
            {
                Message(player, "InvalidKit", args[0]);
                return;
            }

            if (!players.Connected.Any())
            {
                Message(player, "NoPlayersConnected");
                return;
            }

            int kitsGiven = 0;
            int connectedPlayers = 0;
            foreach (IPlayer target in players.Connected.ToArray())
            {
                if (TryGiveKit(target, args[0]))
                {
                    kitsGiven++;
                    connectedPlayers++;
                }
            }
            Message(player, kitsGiven > 0 ? "GiveKitAllSuccess" : "GiveKitAllFail", args[0], connectedPlayers);
        }

        #endregion Kit Giving

        #region Helpers

        private void AddLocalizedCommand(string command)
        {
            foreach (string language in lang.GetLanguages(this))
            {
                foreach (KeyValuePair<string, string> message in lang.GetMessages(language, this))
                {
                    if (message.Key.Equals(command) && !string.IsNullOrEmpty(message.Value))
                    {
                        AddCovalenceCommand(message.Value, command);
                    }
                }
            }
        }

        private IPlayer FindPlayer(string playerNameOrId, IPlayer player)
        {
            IPlayer[] foundPlayers = players.FindPlayers(playerNameOrId).ToArray();
            if (foundPlayers.Length > 1)
            {
                Message(player, "PlayersFound", string.Join(", ", foundPlayers.Select(p => p.Name).Take(10).ToArray()).Truncate(60));
                return null;
            }

            IPlayer target = foundPlayers.Length == 1 ? foundPlayers[0] : null;
            if (target == null)
            {
                Message(player, "NoPlayersFound", playerNameOrId);
                return null;
            }

            return target;
        }

        private string GetLang(string langKey, string playerId = null, params object[] args)
        {
            return string.Format(lang.GetMessage(langKey, this, playerId), args);
        }

        private void Message(IPlayer player, string textOrLang, params object[] args)
        {
            if (player.IsConnected)
            {
                string message = GetLang(textOrLang, player.Id, args);
                player.Reply(message != textOrLang ? message : textOrLang);
            }
        }

        #endregion Helpers
    }
}
