using UnityEngine;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using Rust;

namespace Oxide.Plugins
{
    [Info("Monuments Recycler", "VisEntities", "0.2.7")]
    [Description("Adds recyclers to monuments including the cargo ship.")]
    internal class MonumentsRecycler : RustPlugin
    {
        private bool _serverInitialized = false;
        private const string RecyclerPrefab = "assets/bundled/prefabs/static/recycler_static.prefab";
        private const string SmallOilRigKey = "oil_rig_small";
        private const string LargeOilRigKey = "large_oil_rig";
        private const string DomeKey = "dome_monument_name";
        const string FishingVillageLargePrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_a.prefab";
        const string FishingVillageSmallBPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_b.prefab";
        const string FishingVillageSmallAPrefab = "assets/bundled/prefabs/autospawn/monument/fishing_village/fishing_village_c.prefab";

        private List<BaseEntity> _recyclers = new List<BaseEntity>();
		private SpawnData _domeSpawnData = new SpawnData(new Vector3(19.9f, 37.8f, 16.57f), new Vector3(0, 235, 0));
        private readonly Dictionary<string, SpawnData> _smallOilRigRecyclerPositions = new Dictionary<string, SpawnData>
        {
            {"3",  new SpawnData(new Vector3(21.01f, 22.5f, -30.8f),new Vector3(0, 180, 0) )},
            {"4",new SpawnData(new Vector3(32.1f, 27f, -34.5f),new Vector3(0, 270, 0)) }
        };
        private readonly Dictionary<string, SpawnData> _largeOilRigRecyclerPositions = new Dictionary<string, SpawnData>
        {
            {"4",  new SpawnData(new Vector3(20.57f, 27.1f, -44.52f),new Vector3(0, 0, 0) )},
            {"6",new SpawnData(new Vector3(-13.6f, 36.1f, -3.4f),new Vector3(0, 180, 0)) }
        };
        private readonly Dictionary<string, SpawnData> _fishingVillageRecyclerPositions = new Dictionary<string, SpawnData>
        {
            {"smallB", new SpawnData(new Vector3(-21f,0.3f,6.5f),new Vector3(0f,130f,0f)) },
            {"smallA", new SpawnData(new Vector3(-8.5f,2f,16.06067f),new Vector3(0,90f,0)) },
            {"large", new SpawnData(new Vector3(-20.7f,0.2f,-9.6f), new Vector3(0,45f,0)) }
        };
        private Configuration _config;

        public class Configuration
        {
            [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Front")]
            public bool RecyclerPositionFront = true;

            [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Back")]
            public bool RecyclerPositionBack = true;

            [JsonProperty(PropertyName = "Cargo Ship - Recycler Position - Bottom")]
            public bool RecyclerPositionBottom = true;

            [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 4")]
            public bool LargeOilRigRecyclerPositionLevel4 = true;

            [JsonProperty(PropertyName = "Large Oil Rig - Recycler Position - Level 6")]
            public bool LargeOilRigRecyclerPositionLevel6 = true;

            [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 3")]
            public bool SmallOilRigRecyclerPositionLevel3 = true;

            [JsonProperty(PropertyName = "Small Oil Rig - Recycler Position - Level 4")]
            public bool SmallOilRigRecyclerPositionLevel4 = true;

            [JsonProperty(PropertyName = "Fishing Village - Recycler - Large")]
            public bool FishingVillageRecyclerLarge { get; set; } = true;

            [JsonProperty(PropertyName = "Fishing Village - Recycler - Small A")]
            public bool FishingVillageRecyclerSmallA { get; set; } = true;

            [JsonProperty(PropertyName = "Fishing Village - Recycler - Small B")]
            public bool FishingVillageRecyclerSmallB { get; set; } = true;

            [JsonProperty(PropertyName = "Dome - Recycler")]
            public bool DomeRecycler = true;
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                _config = Config.ReadObject<Configuration>();
                //if (_config != null && _serverInitialized)
                //{
                //    ShowRecyclers();
                //}
            }
            catch
            {
                PrintError("Loading default config! Error loading your config, it's corrupt!");
                LoadDefaultConfig();
            }

            if (_config == null)
            {
                LoadDefaultConfig();
            }

            SaveConfig();
            var pluginEnabled = _config.RecyclerPositionFront || _config.RecyclerPositionBack || _config.RecyclerPositionBottom ||
                                _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 ||
                                _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4 ||
                                _config.DomeRecycler || _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA
                                || _config.FishingVillageRecyclerSmallB;
            if (!pluginEnabled)
            {
                PrintWarning("No Recycler Position Found");
                Unsubscribe(nameof(OnEntitySpawned));
            }
        }

        protected override void LoadDefaultConfig() => _config = new Configuration();

        protected override void SaveConfig() => Config.WriteObject(_config);

        Tuple<Vector3, Quaternion> GetCargoFrontPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x - .2172264f, position.y + 9.574753f, position.z + 79.05922f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w));

        Tuple<Vector3, Quaternion> GetCargoBackPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x + .2407f, position.y + 9.5f, position.z - 36.87305f), rotation);

        Tuple<Vector3, Quaternion> GetCargoBottomPosition(Vector3 position, Quaternion rotation) => Tuple.Create(new Vector3(position.x, position.y + .57767f, position.z + 10f), new Quaternion(rotation.x, rotation.y + 180, rotation.z, rotation.w));

        private void OnEntitySpawned(CargoShip ship)
        {
            if (ship == null) return;
            Vector3 pos = ship.transform.position;
            Quaternion rot = new Quaternion();
            Timer thisTimer = null;

            thisTimer = timer.Every(0.01f, () =>
            {
                if (ship == null || ship.IsDestroyed)
                {
                    thisTimer.Destroy();
                    return;
                }

                ship.transform.position = pos;
                ship.transform.rotation = rot;
            });

            timer.Once(3, () =>
            {
                if (ship == null || ship.IsDestroyed)
                {
                    return;
                }

                if (_config.RecyclerPositionFront)
                {
                    Vector3 position = ship.transform.position;
                    Quaternion rotation = ship.transform.rotation;
                    SpawnRecycler(ship, GetCargoFrontPosition(position, rotation));
                }

                if (_config.RecyclerPositionBack)
                {
                    Vector3 position = ship.transform.position;
                    Quaternion rotation = ship.transform.rotation;
                    SpawnRecycler(ship, GetCargoBackPosition(position, rotation));
                }

                if (_config.RecyclerPositionBottom)
                {
                    Vector3 position = ship.transform.position;
                    Quaternion rotation = ship.transform.rotation;
                    SpawnRecycler(ship, GetCargoBottomPosition(position, rotation));
                }

                thisTimer.Destroy();
            });
        }

        private void OnServerInitialized()
        {
            _serverInitialized = true;
            NextTick(ShowRecyclers);
        }
        private void ShowRecyclers()
        {
            var oilRigEnabled = _config.LargeOilRigRecyclerPositionLevel4 || _config.LargeOilRigRecyclerPositionLevel6 ||
                               _config.SmallOilRigRecyclerPositionLevel3 || _config.SmallOilRigRecyclerPositionLevel4;
            var fishingVillageEnabled = _config.FishingVillageRecyclerLarge || _config.FishingVillageRecyclerSmallA || _config.FishingVillageRecyclerSmallB;
            if (oilRigEnabled)
            {

                SpawnData spawnData;
                var monuments = TerrainMeta.Path?.Monuments?
                    .Where(m =>
                        m != null &&
                        m.shouldDisplayOnMap &&
                        m.displayPhrase != null &&
                        !string.IsNullOrEmpty(m.displayPhrase.english) &&
                        m.displayPhrase.english.IndexOf("Oil Rig", StringComparison.OrdinalIgnoreCase) >= 0)
                    .ToList()
                    ?? new List<MonumentInfo>();

                foreach (var monument in monuments)
                {
                    if (monument.displayPhrase?.token == SmallOilRigKey)
                    {
                        if (_config.SmallOilRigRecyclerPositionLevel3)
                        {
                            if (_smallOilRigRecyclerPositions.TryGetValue("3", out spawnData))
                            {
                                SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                        if (_config.SmallOilRigRecyclerPositionLevel4)
                        {
                            if (_smallOilRigRecyclerPositions.TryGetValue("4", out spawnData))
                            {
                                SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                    }
                    else if (monument.displayPhrase?.token == LargeOilRigKey)
                    {
                        if (_config.LargeOilRigRecyclerPositionLevel4)
                        {
                            if (_largeOilRigRecyclerPositions.TryGetValue("4", out spawnData))
                            {
                                SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                        if (_config.LargeOilRigRecyclerPositionLevel6)
                        {
                            if (_largeOilRigRecyclerPositions.TryGetValue("6", out spawnData))
                            {
                                SpawnRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                    }
                }
            }

            if (_config.DomeRecycler)
            {
                var monuments = TerrainMeta.Path?.Monuments?
                    .Where(m =>
                        m != null &&
                        m.shouldDisplayOnMap &&
                        m.displayPhrase != null &&
                        !string.IsNullOrEmpty(m.displayPhrase.english) &&
                        m.displayPhrase.english.IndexOf("Dome", StringComparison.OrdinalIgnoreCase) >= 0)
                    .ToList()
                    ?? new List<MonumentInfo>();
                foreach (var monument in monuments)
                {
                    if (monument.displayPhrase?.token == DomeKey)
                    {
                        SpawnRecycler(monument.transform, _domeSpawnData.Position, _domeSpawnData.Rotation);
                    }
                }
            }
            if (fishingVillageEnabled)
            {
                SpawnData spawnData;
                var monuments = TerrainMeta.Path?.Monuments?
                    .Where(m =>
                        m != null &&
                        m.shouldDisplayOnMap &&
                        m.displayPhrase != null &&
                        !string.IsNullOrEmpty(m.displayPhrase.english) &&
                        m.displayPhrase.english.IndexOf("fishing", StringComparison.OrdinalIgnoreCase) >= 0)
                    .ToList()
                    ?? new List<MonumentInfo>();
                foreach (var monument in monuments)
                {
                    if (monument.name == FishingVillageLargePrefab)
                    {
                        if (_config.FishingVillageRecyclerLarge)
                        {
                            if (_fishingVillageRecyclerPositions.TryGetValue("large", out spawnData))
                            {
                                SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                    }
                    else if (monument.name == FishingVillageSmallAPrefab)
                    {
                        if (_config.FishingVillageRecyclerSmallA)
                        {
                            if (_fishingVillageRecyclerPositions.TryGetValue("smallA", out spawnData))
                            {
                                SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                    }
                    else if (monument.name == FishingVillageSmallBPrefab)
                    {
                        if (_config.FishingVillageRecyclerSmallB)
                        {
                            if (_fishingVillageRecyclerPositions.TryGetValue("smallB", out spawnData))
                            {
                                SpawnFishingVillageRecycler(monument.transform, spawnData.Position, spawnData.Rotation);
                            }
                        }
                    }
                }
            }
        }

        private void SpawnFishingVillageRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset)
        {

            var mtx = objTransform.localToWorldMatrix;
            var finalPos = mtx.MultiplyPoint3x4(spawnOffset);
            var rotation = mtx.rotation * Quaternion.Euler(rotationOffset);
            var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, rotation);
            if (entity != null)
            {
                entity.EnableSaving(false);
                entity.Spawn();
                _recyclers.Add(entity);
            }
        }
        private void SpawnRecycler(Transform objTransform, Vector3 spawnOffset, Vector3 rotationOffset)
        {

            var mtx = objTransform.localToWorldMatrix;
            var finalPos = mtx.MultiplyPoint3x4(spawnOffset);
            var oilRot = mtx.rotation * Quaternion.Euler(rotationOffset);
            if (!GamePhysics.CheckSphere(finalPos, .1f, Layers.Server.Deployed, QueryTriggerInteraction.Ignore))
            {
                var entity = GameManager.server.CreateEntity(RecyclerPrefab, finalPos, oilRot);
                if (entity != null)
                {
                    entity.EnableSaving(false);
                    entity.Spawn();
                    _recyclers.Add(entity);
                }
            }
        }
        private void SpawnRecycler(CargoShip ship, Tuple<Vector3, Quaternion> selectedTransform)
        {
            BaseEntity rec = GameManager.server.CreateEntity(RecyclerPrefab, selectedTransform.Item1, selectedTransform.Item2, true);
            if (rec != null)
            {
                rec.EnableSaving(false);
                rec.Spawn();
                rec.SetParent(ship, true, true);
                Rigidbody comp = rec.GetComponent<Rigidbody>();
                if (comp)
                {
                    comp.isKinematic = true;
                }

                _recyclers.Add(rec);
            }
            else
            {
                Puts("Unable to create RecyclerPrefab for Front position!");
            }
        }

        private void Unload()
        {

            foreach (var recycler in _recyclers)
            {
                if (recycler is Recycler)
                {
                    ((Recycler)recycler).DropItems();
                }
                recycler.Kill();
            }
        }

        private class SpawnData
        {
            public SpawnData(Vector3 position, Vector3 rotation)
            {
                Position = position;
                Rotation = rotation;
            }

            public Vector3 Position { get; set; }
            public Vector3 Rotation { get; set; }
        }
    }
}