using System;
using Server;
using Server.Mobiles;
using Server.ContextMenus;
using System.Collections.Generic;
using Server.Network;

namespace Server.Items
{
    public class UndertakersStaff : GnarledStaff
    {
        private static Dictionary<Mobile, CorpseRetrieveTimer> _Timers = new Dictionary<Mobile, CorpseRetrieveTimer>();

        private int _Charges;
        private bool _SummonAll;

        [CommandProperty(AccessLevel.GameMaster)]
        public int Charges { get { return _Charges; } set { _Charges = value; InvalidateProperties(); } }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool SummonAll { get { return _SummonAll; } set { _SummonAll = value; InvalidateProperties(); } }

        public override int LabelNumber { get { return 1071498; } } // Undertaker's Staff
        public override bool IsArtifact { get { return true; } }
		public override int InitMinHits { get { return 255; } }
        public override int InitMaxHits { get { return 255; } }

        [Constructable]
        public UndertakersStaff()
        {
            if (!Siege.SiegeShard)
                LootType = LootType.Blessed;

            Charges = 100;
            SummonAll = true;
        }

        public override void GetProperties(ObjectPropertyList list)
        {
            base.GetProperties(list);
            list.Add(1071518, String.Format("#{0}", _SummonAll ? "1071508" : "1071507"));
            list.Add(1060584, _Charges.ToString());
        }

        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> entries)
        {
            base.GetContextMenuEntries(from, entries);

            if (IsChildOf(from.Backpack))
            {
                var entry1 = new SimpleContextMenuEntry(from, 1071507, m =>
                    {
                        SummonAll = false;
                        InvalidateProperties();
                    }); // Summon Most Recent Corpse Only

                var entry2 = new SimpleContextMenuEntry(from, 1071508, m => 
                    {
                        _SummonAll = true;
                        InvalidateProperties();
                    }); // Summon All Corpses

                if(_SummonAll)
                    entry2.Flags |= CMEFlags.Highlighted;
                else
                    entry1.Flags |= CMEFlags.Highlighted;

                entry1.Enabled = !IsSummoning();
                entry2.Enabled = !IsSummoning();

                entries.Add(entry1);
                entries.Add(entry2);
            }
        }

        public override void OnDoubleClick(Mobile m)
        {
            if (!_Timers.ContainsKey(m) && (IsChildOf(m.Backpack) || m.FindItemOnLayer(Layer) == this))
            {
                TryGetCorpse(m);
            }
        }

        private void TryGetCorpse(Mobile m)
        {
            if (CanGetCorpse(m))
            {
                m.PlaySound(0xF5);

                if (_SummonAll)
                {
                    var corpses = GetCorpses(m);

                    if (corpses != null)
                    {
                        m.SendLocalizedMessage(1071527, corpses.Count.ToString()); // The staff reaches out to ~1_COUNT~ of your corpses and tries to draw them to you...

                         _Timers[m] = new CorpseRetrieveTimer(m, corpses, this);
                    }
                    else
                    {
                        m.SendLocalizedMessage(1071511); // The staff glows slightly, then fades. Its magic is unable to locate a corpse of yours to recover.
                    }
                }
                else
                {
                    var corpse = GetCorpse(m);

                    if (corpse != null)
                    {
                        m.SendLocalizedMessage(1071528); // The staff reaches out to your corpse and tries to draw it to you...
                    
                        _Timers[m] = new CorpseRetrieveTimer(m, new List<Corpse> { corpse }, this);
                    }
                    else
                    {
                        m.SendLocalizedMessage(1071511); // The staff glows slightly, then fades. Its magic is unable to locate a corpse of yours to recover.
                    }
                }
            }
        }

        private bool CanGetCorpse(Mobile m, bool firstCheck = true)
        {
            if(m.Criminal)
            {
                m.SendLocalizedMessage(1071510); // You are a criminal and cannot use this item...
                return false;
            }
            else if (Spells.SpellHelper.CheckCombat(m))
            {
                m.SendLocalizedMessage(1071514); // You cannot use this item during the heat of battle.
                return false;
            }

            return true;
        }

        public void TryEndSummon(Mobile m, List<Corpse> corpses)
        {
            if (_Timers.ContainsKey(m))
                _Timers.Remove(m);

            if (corpses == null || corpses.Count == 0)
            {
                m.SendLocalizedMessage(1071511); // The staff glows slightly, then fades. Its magic is unable to locate a corpse of yours to recover.
                return;
            }

            bool tooFar = false;
            bool notEnoughTime = false;
            bool tooManySummons = false;
            bool success = true;

            if (_SummonAll)
            {
                List<Corpse> copy = new List<Corpse>(corpses);

                foreach (var c in copy)
                {
                    bool remove = false;

                    if (c.Map != m.Map)
                    {
                        remove = true;
                        tooFar = true;
                    }

                    if (c.Killer is PlayerMobile && c.Killer != m && c.TimeOfDeath + TimeSpan.FromSeconds(180) > DateTime.UtcNow)
                    {
                        remove = true;
                        notEnoughTime = true;
                    }

                    if (Corpse.PlayerCorpses.ContainsKey(c) && Corpse.PlayerCorpses[c] >= 3)
                    {
                        remove = true;
                        tooManySummons = true;
                    }

                    if (remove)
                        corpses.Remove(c);
                }

                if (corpses.Count == 0)
                    success = false;
            }
            else
            {
                Corpse c = corpses[0];

                if (c.Map != m.Map)
                    tooFar = true;

                if (c.Killer is PlayerMobile && c.Killer != m && c.TimeOfDeath + TimeSpan.FromSeconds(180) > DateTime.UtcNow)
                    notEnoughTime = true;

                if (Corpse.PlayerCorpses != null && Corpse.PlayerCorpses.ContainsKey(c) && Corpse.PlayerCorpses[c] >= 3)
                    tooManySummons = true;

                if (tooFar || notEnoughTime || tooManySummons)
                {
                    if (tooFar)
                        m.SendLocalizedMessage(1071512); // ...but the corpse is too far away!
                    else if (notEnoughTime)
                        m.SendLocalizedMessage(1071515); // ...but not enough time has passed since you were slain in battle!
                    else
                        m.SendLocalizedMessage(1071517); // ...but the corpse has already been summoned too many times!

                    success = false;
                }
            }

            if (success)
            {
                m.PlaySound(0xFA);

                foreach (var c in corpses)
                {
                    c.MoveToWorld(m.Location, m.Map);

                    if (Corpse.PlayerCorpses != null && Corpse.PlayerCorpses.ContainsKey(c))
                        Corpse.PlayerCorpses[c]++;
                }

                if (_SummonAll)
                {
                    m.SendLocalizedMessage(1071530, corpses.Count.ToString()); // ...and succeeds in summoning ~1_COUNT~ of them!

                    if (tooFar)
                        m.SendLocalizedMessage(1071513); // ...but one of them is too far away!
                    else if (notEnoughTime)
                        m.SendLocalizedMessage(1071516); // ...but one of them deflects the magic because of the stain of war!
                    else if (tooManySummons)
                        m.SendLocalizedMessage(1071519); // ...but one of them has already been summoned too many times!
                }
                else
                    m.SendLocalizedMessage(1071529); // ...and succeeds in the summoning of it!

                if (Charges <= 0)
                {
                    m.SendLocalizedMessage(1071509); // The staff has been reduced to pieces!
                    Delete();
                }
                else
                {
                    Charges--;
                }
            }
        }

        private int GetCorpseCount(Mobile m)
        {
            if (Corpse.PlayerCorpses == null)
                return 0;

            int count = 0;

            foreach (var kvp in Corpse.PlayerCorpses)
            {
                if (kvp.Key.Owner == m && kvp.Value < 3)
                    count++;
            }

            return count;
        }

        private List<Corpse> GetCorpses(Mobile m)
        {
            if (Corpse.PlayerCorpses == null)
                return null;

            List<Corpse> list = null;

            foreach (var kvp in Corpse.PlayerCorpses)
            {
                if (kvp.Key.Owner == m && kvp.Value < 3)
                {
                    if (list == null)
                        list = new List<Corpse>();

                    if(!list.Contains(kvp.Key))
                        list.Add(kvp.Key);
                }

                if (list != null && list.Count >= 15)
                    break;
            }

            return list;
        }

        private Corpse GetCorpse(Mobile m)
        {
            var corpse = m.Corpse as Corpse;

            if (corpse == null || Corpse.PlayerCorpses == null || !Corpse.PlayerCorpses.ContainsKey(corpse))
                return null;

            return corpse;
        }

        public static bool TryRemoveTimer(Mobile m)
        {
            if (_Timers.ContainsKey(m))
            {
                _Timers[m].Stop();
                _Timers.Remove(m);

                m.FixedEffect(0x3735, 6, 30);
                m.PlaySound(0x5C);

                m.SendLocalizedMessage(1071525); // You have been disrupted while attempting to pull your corpse!
                return true;
            }

            return false;
        }

        public bool IsSummoning()
        {
            foreach (var timer in _Timers.Values)
            {
                if (timer.Staff == this)
                    return true;
            }

            return false;
        }

        public class CorpseRetrieveTimer : Timer
        {
            public Mobile From { get; set; }
            public List<Corpse> Corpses { get; set; }
            public UndertakersStaff Staff { get; set; }

            public CorpseRetrieveTimer(Mobile from, List<Corpse> corpses, UndertakersStaff staff)
                : base(TimeSpan.FromSeconds(3))
            {
                From = from;
                Corpses = corpses;
                Staff = staff;

                Start();
            }

            protected override void OnTick()
            {
                Staff.TryEndSummon(From, Corpses);
            }
        }

        public UndertakersStaff(Serial serial)
            : base(serial)
        {
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);
            writer.Write((int)0); // version

            writer.Write(_Charges);
            writer.Write(_SummonAll);
        }

        public override void Deserialize(GenericReader reader)
        {
            base.Deserialize(reader);
            reader.ReadInt();

            _Charges = reader.ReadInt();
            _SummonAll = reader.ReadBool();
        }
    }
}
