using System;
using System.Runtime.CompilerServices;
using Server.Engines.CannedEvil;

namespace Server.Mobiles
{
    [PropertyObject]
    public class ChampionTitleInfo
    {
        private const int LossAmount = 90;
        private static TimeSpan LossDelay = TimeSpan.FromDays(1.0);

        private TitleInfo[] m_Values;

        public ChampionTitleInfo()
        {
        }

        public ChampionTitleInfo(IGenericReader reader)
        {
            var version = reader.ReadEncodedInt();

            switch (version)
            {
                case 0:
                    {
                        Harrower = reader.ReadEncodedInt();

                        var length = reader.ReadEncodedInt();

                        if (length == 0)
                        {
                            break;
                        }

                        m_Values = new TitleInfo[length];

                        for (var i = 0; i < length; i++)
                        {
                            m_Values[i] = new TitleInfo(reader);
                        }

                        if (m_Values.Length != ChampionSpawnInfo.Table.Length)
                        {
                            var oldValues = m_Values;
                            m_Values = new TitleInfo[ChampionSpawnInfo.Table.Length];

                            for (var i = 0; i < m_Values.Length && i < oldValues.Length; i++)
                            {
                                m_Values[i] = oldValues[i];
                            }
                        }

                        break;
                    }
            }
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Pestilence
        {
            get => GetValue(ChampionSpawnType.Pestilence);
            set => SetValue(ChampionSpawnType.Pestilence, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Abyss
        {
            get => GetValue(ChampionSpawnType.Abyss);
            set => SetValue(ChampionSpawnType.Abyss, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Arachnid
        {
            get => GetValue(ChampionSpawnType.Arachnid);
            set => SetValue(ChampionSpawnType.Arachnid, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int ColdBlood
        {
            get => GetValue(ChampionSpawnType.ColdBlood);
            set => SetValue(ChampionSpawnType.ColdBlood, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int ForestLord
        {
            get => GetValue(ChampionSpawnType.ForestLord);
            set => SetValue(ChampionSpawnType.ForestLord, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int SleepingDragon
        {
            get => GetValue(ChampionSpawnType.SleepingDragon);
            set => SetValue(ChampionSpawnType.SleepingDragon, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int UnholyTerror
        {
            get => GetValue(ChampionSpawnType.UnholyTerror);
            set => SetValue(ChampionSpawnType.UnholyTerror, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int VerminHorde
        {
            get => GetValue(ChampionSpawnType.VerminHorde);
            set => SetValue(ChampionSpawnType.VerminHorde, value);
        }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Harrower { get; set; }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int GetValue(ChampionSpawnType type) => GetValue((int)type);

        public int GetValue(int index)
        {
            if (index < 0 || index >= m_Values.Length)
            {
                return 0;
            }

            return m_Values?[index]?.Value ?? 0;
        }

        public DateTime GetLastDecay(int index)
        {
            if (index < 0 || index >= m_Values.Length)
            {
                return DateTime.MinValue;
            }

            return m_Values?[index]?.LastDecay ?? DateTime.MinValue;
        }

        public void SetValue(ChampionSpawnType type, int value)
        {
            var index = (int)type;
            if (index < 0 || index >= ChampionSpawnInfo.Table.Length)
            {
                return;
            }

            m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length];
            var title = m_Values[index];
            if (title == null)
            {
                if (value > 0)
                {
                    m_Values[index] = new TitleInfo(value, Core.Now);
                }
                return;
            }

            title.Value = Math.Max(value, 0);
            title.LastDecay = title.Value == 0 ? DateTime.MinValue : Core.Now;
        }

        public void Award(ChampionSpawnType type, int value)
        {
            var index = (int)type;
            if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length)
            {
                return;
            }

            m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length];
            var title = m_Values[index];
            if (title == null)
            {
                m_Values[index] = new TitleInfo(value, Core.Now);
                return;
            }

            title.Value += value;
            if (title.LastDecay == DateTime.MinValue)
            {
                title.LastDecay = Core.Now;
            }
        }

        public void Atrophy(int index, int value)
        {
            if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length)
            {
                return;
            }

            var title = m_Values?[index];
            if (title == null)
            {
                return;
            }

            var before = title.Value;

            title.Value -= Math.Min(value, title.Value);

            if (before != title.Value)
            {
                title.LastDecay = Core.Now;
            }
        }

        public override string ToString() => "...";

        public static void Serialize(IGenericWriter writer, ChampionTitleInfo titles)
        {
            writer.WriteEncodedInt(0); // version

            writer.WriteEncodedInt(titles.Harrower);

            var length = titles.m_Values?.Length ?? 0;
            writer.WriteEncodedInt(length);

            for (var i = 0; i < length; i++)
            {
                titles.m_Values![i] ??= new TitleInfo();

                TitleInfo.Serialize(writer, titles.m_Values[i]);
            }
        }

        public static bool ShouldAtrophy(PlayerMobile pm)
        {
            var t = pm.ChampionTitles;
            if (t?.m_Values == null)
            {
                return false;
            }

            for (var i = 0; i < t.m_Values.Length; i++)
            {
                var decay = t.GetLastDecay(i);
                if (decay > DateTime.MinValue && decay + LossDelay < Core.Now)
                {
                    return true;
                }
            }

            return false;
        }

        public static void CheckAtrophy(PlayerMobile pm)
        {
            var t = pm.ChampionTitles;
            if (t?.m_Values == null)
            {
                return;
            }

            for (var i = 0; i < t.m_Values.Length; i++)
            {
                var decay = t.GetLastDecay(i);
                if (decay > DateTime.MinValue && decay + LossDelay < Core.Now)
                {
                    t.Atrophy(i, LossAmount);
                }
            }
        }

        // Called when killing a harrower. Will give a minimum of 1 point.
        public static void AwardHarrowerTitle(PlayerMobile pm)
        {
            var t = pm.ChampionTitles;
            if (t == null)
            {
                return;
            }

            if (t.m_Values == null)
            {
                if (t.Harrower == 0)
                {
                    t.Harrower = 1;
                }
                return;
            }

            t.m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length];

            var count = 1;
            for (var i = 0; i < t.m_Values.Length; i++)
            {
                if (t.m_Values[i].Value > 900)
                {
                    count++;
                }
            }

            t.Harrower = Math.Max(count, t.Harrower); // Harrower titles never decay.
        }

        private class TitleInfo
        {
            public TitleInfo()
            {
            }

            public TitleInfo(int value, DateTime lastDecay)
            {
                Value = value;
                LastDecay = lastDecay;
            }

            public TitleInfo(IGenericReader reader)
            {
                var version = reader.ReadEncodedInt();

                switch (version)
                {
                    case 0:
                        {
                            Value = reader.ReadEncodedInt();
                            LastDecay = reader.ReadDateTime();
                            break;
                        }
                }
            }

            public int Value { get; set; }

            public DateTime LastDecay { get; set; }

            public static void Serialize(IGenericWriter writer, TitleInfo info)
            {
                writer.WriteEncodedInt(0); // version

                writer.WriteEncodedInt(info.Value);
                writer.Write(info.LastDecay);
            }
        }
    }
}
