using System.Collections.Generic;
using Newtonsoft.Json;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Animal Home" , "Krungh Crow" , "1.1.0")]
    [Description("Adding a animal roam radius")]
    public class AnimalHome : RustPlugin
    {
        #region Variables

        private static ConfigData config;
        private static HashSet<string> blockedPrefabs;

        #endregion

        #region Config

        private class ConfigData
        {
            [JsonProperty(PropertyName = "Time between checks")]
            public float timer;
            [JsonProperty(PropertyName = "Max distance between Home and NPC")]
            public float distance;
            [JsonProperty(PropertyName = "Blocked NPC types (logic will not work for them)")]
            public List<string> blocked;
        }

        private ConfigData GetDefaultConfig()
        {
            return new ConfigData
            {
                timer = 10f ,
                distance = 50f ,
                blocked = new List<string>
                {
                    "example.name"
                }
            };
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();

            try
            {
                config = Config.ReadObject<ConfigData>();
            }
            catch
            {
                LoadDefaultConfig();
            }

            if (config == null)
                config = GetDefaultConfig();

            if (config.blocked == null)
                config.blocked = new List<string>();

            SaveConfig();
        }

        protected override void LoadDefaultConfig()
        {
            PrintError("Configuration file is corrupt(or not existing), creating new one!");
            config = GetDefaultConfig();
        }

        protected override void SaveConfig()
        {
            Config.WriteObject(config);
        }

        #endregion

        #region Hooks

        private void OnServerInitialized()
        {
            blockedPrefabs = new HashSet<string>(config.blocked);

            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                AddLogic(entity);
            }
        }

        private void Unload()
        {
            foreach (BaseNetworkable entity in BaseNetworkable.serverEntities)
            {
                BaseAnimalNPC npc = entity as BaseAnimalNPC;
                if (npc == null) continue;

                OLogic logic = npc.GetComponent<OLogic>();
                if (logic != null)
                    UnityEngine.Object.Destroy(logic);
            }
        }

        private void OnEntitySpawned(BaseNetworkable entity)
        {
            AddLogic(entity);
        }

        #endregion

        #region Helpers

        private void AddLogic(BaseNetworkable entity)
        {
            if (entity == null || entity.IsDestroyed) return;

            BaseAnimalNPC npc = entity as BaseAnimalNPC;
            if (npc == null) return;

            if (blockedPrefabs != null && blockedPrefabs.Contains(npc.ShortPrefabName)) return;
            if (npc.GetComponent<OLogic>() != null) return;

            npc.gameObject.AddComponent<OLogic>();
        }

        #endregion

        #region Mono

        private class OLogic : MonoBehaviour
        {
            private Vector3 home;
            private BaseAnimalNPC npc;
            private Transform cachedTransform;
            private float distanceSqr;

            private void Awake()
            {
                npc = GetComponent<BaseAnimalNPC>();
                if (npc == null)
                {
                    Destroy(this);
                    return;
                }

                cachedTransform = npc.transform;
                home = cachedTransform.position;
                distanceSqr = config.distance * config.distance;

                InvokeRepeating(nameof(CheckDistance) , 1f , config.timer);
            }

            private void CheckDistance()
            {
                if (npc == null || npc.IsDestroyed) return;
                if (cachedTransform == null) return;

                if ((cachedTransform.position - home).sqrMagnitude <= distanceSqr) return;

                npc.AttackTarget = null;
                npc.UpdateDestination(home);
            }

            private void OnDestroy()
            {
                CancelInvoke(nameof(CheckDistance));
            }
        }

        #endregion
    }
}