using System;
using System.Collections.Generic;
using System.Linq;
using Server.ContextMenus;
using Server.Mobiles;
using Server.Services.Virtues;

namespace Server.Engines.Quests
{
    public class BaseEscort : MondainQuester
    {
        private static readonly TimeSpan m_EscortDelay = TimeSpan.FromMinutes(5.0);
        private static readonly Dictionary<Mobile, Mobile> m_EscortTable = new Dictionary<Mobile, Mobile>();
        private Timer m_DeleteTimer;
        private bool m_Checked;

        public BaseQuest Quest { get; set; }
        public DateTime LastSeenEscorter { get; set; }

        public BaseEscort()
            : base()
        {
            AI = AIType.AI_Melee;
            FightMode = FightMode.Aggressor;
            RangePerception = 22;
            RangeFight = 1;
            ActiveSpeed = 0.2;
            PassiveSpeed = 1.0;

            ControlSlots = 0;
        }

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

        public override bool InitialInnocent { get { return true; } }
        public override bool IsInvulnerable { get { return false; } }
        public override bool Commandable { get { return false; } }

        public override Type[] Quests { get { return null; } }

        public override bool CanAutoStable { get { return false; } }
        public override bool CanDetectHidden { get { return false; } }

        public override void OnTalk(PlayerMobile player)
        {
            if (AcceptEscorter(player))
                base.OnTalk(player);
        }

        public override bool CanBeRenamedBy(Mobile from)
        {
            return (from.AccessLevel >= AccessLevel.GameMaster);
        }

        public override void AddCustomContextEntries(Mobile from, List<ContextMenuEntry> list)
        {
            if (from.Alive && from == ControlMaster)
                list.Add(new AbandonEscortEntry(this));

            base.AddCustomContextEntries(from, list);
        }

        public override void OnAfterDelete()
        {
            if (Quest != null)
            {
                Quest.RemoveQuest();

                if (Quest.Owner != null)
                    m_EscortTable.Remove(Quest.Owner);
            }

            base.OnAfterDelete();
        }

        public override void OnThink()
        {
            base.OnThink();

            CheckAtDestination();
        }

        public override bool CanBeDamaged()
        {
            return true;
        }

        public override void InitBody()
        {
            SetStr(90, 100);
            SetDex(90, 100);
            SetInt(15, 25);

            Hue = Utility.RandomSkinHue();
            Female = Utility.RandomBool();
            Name = NameList.RandomName(Female ? "female" : "male");
            Race = Race.Human;

            Utility.AssignRandomHair(this);
            Utility.AssignRandomFacialHair(this);
        }

        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);

            writer.Write((int)0); // version

            writer.Write(m_DeleteTimer != null);

            if (m_DeleteTimer != null)
                writer.WriteDeltaTime(m_DeleteTimer.Next);
        }

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

            int version = reader.ReadInt();

            if (reader.ReadBool())
            {
                DateTime deleteTime = reader.ReadDeltaTime();
                m_DeleteTimer = Timer.DelayCall(deleteTime - DateTime.UtcNow, new TimerCallback(Delete));
            }
        }

        public void AddHash(PlayerMobile player)
        {
            m_EscortTable[player] = this;
        }

        public virtual void StartFollow()
        {
            StartFollow(ControlMaster);
        }

        public virtual void StartFollow(Mobile escorter)
        {
            ActiveSpeed = 0.1;
            PassiveSpeed = 0.2;

            ControlOrder = OrderType.Follow;
            ControlTarget = escorter;

            CurrentSpeed = 0.1;
        }

        public virtual void StopFollow()
        {
            ActiveSpeed = 0.2;
            PassiveSpeed = 1.0;

            ControlOrder = OrderType.None;
            ControlTarget = null;

            CurrentSpeed = 1.0;

            SetControlMaster(null);
        }

        public virtual void BeginDelete(Mobile m)
        {
            StopFollow();

            if (m != null)
                m_EscortTable.Remove(m);

            m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(45.0), new TimerCallback(Delete));
        }        

        public virtual bool AcceptEscorter(Mobile m)
        {
            if (!m.Alive)
            {
                return false;
            }
            else if (m_DeleteTimer != null)
            {
                Say(500898); // I am sorry, but I do not wish to go anywhere.
                return false;
            }
            else if (Controlled)
            {
                if (m == ControlMaster)
                    m.SendGump(new MondainQuestGump(Quest, MondainQuestGump.Section.InProgress, false));
                else
                    Say(500897); // I am already being led!

                return false;
            }
            else if (!m.InRange(Location, 5))
            {
                Say(500348); // I am too far away to do that.
                return false;
            }
            else if (m_EscortTable.ContainsKey(m))
            {
                Say(500896); // I see you already have an escort.
                return false;
            }
            else if (m is PlayerMobile && (((PlayerMobile)m).LastEscortTime + m_EscortDelay) >= DateTime.UtcNow)
            {
                int minutes = (int)Math.Ceiling(((((PlayerMobile)m).LastEscortTime + m_EscortDelay) - DateTime.UtcNow).TotalMinutes);

                Say("You must rest {0} minute{1} before we set out on this journey.", minutes, minutes == 1 ? "" : "s");
                return false;
            }

            return true;
        }

        public virtual EscortObjective GetObjective()
        {
            if (Quest != null)
            {
                for (int i = 0; i < Quest.Objectives.Count; i++)
                {
                    EscortObjective escort = Quest.Objectives[i] as EscortObjective;

                    if (escort != null && !escort.Completed && !escort.Failed)
                        return escort;
                }
            }

            return null;
        }

        public virtual Mobile GetEscorter()
        {
            Mobile master = ControlMaster;

            if (master == null || !Controlled)
            {
                return master;
            }
            else if (master.Map != Map || !master.InRange(Location, 30) || !master.Alive)
            {
                TimeSpan lastSeenDelay = DateTime.UtcNow - LastSeenEscorter;

                if (lastSeenDelay >= TimeSpan.FromMinutes(2.0))
                {
                    EscortObjective escort = GetObjective();

                    if (escort != null)
                    {
                        master.SendLocalizedMessage(1071194); // You have failed your escort quest…
                        master.PlaySound(0x5B3);
                        escort.Fail();
                    }

                    master.SendLocalizedMessage(1042473); // You have lost the person you were escorting.
                    Say(1005653); // Hmmm.  I seem to have lost my master.

                    StopFollow();
                    m_EscortTable.Remove(master);
                    m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete));

                    return null;
                }
                else
                {
                    ControlOrder = OrderType.Stay;
                }
            }
            else
            {
                if (ControlOrder != OrderType.Follow)
                    StartFollow(master);

                LastSeenEscorter = DateTime.UtcNow;
            }

            return master;
        }

        public virtual Region GetDestination()
        {
            return null;
        }

        public virtual bool CheckAtDestination()
        {
            if (Quest != null)
            {
                EscortObjective escort = GetObjective();

                if (escort == null)
                    return false;

                Mobile escorter = GetEscorter();

                if (escorter == null)
                    return false;

                if (escort.Region != null && escort.Region.Contains(Location))
                {
                    Say(1042809, escorter.Name); // We have arrived! I thank thee, ~1_PLAYER_NAME~! I have no further need of thy services. Here is thy pay.

                    escort.Complete();

                    if (Quest.Completed)
                    {
                        escorter.SendLocalizedMessage(1046258, null, 0x23); // Your quest is complete.		

                        if (QuestHelper.AnyRewards(Quest))
                            escorter.SendGump(new MondainQuestGump(Quest, MondainQuestGump.Section.Rewards, false, true));
                        else
                            Quest.GiveRewards();

                        escorter.PlaySound(Quest.CompleteSound);

                        StopFollow();
                        m_EscortTable.Remove(escorter);
                        m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete));

                        // fame
                        Misc.Titles.AwardFame(escorter, escort.Fame, true);

                        // compassion
                        bool gainedPath = false;

                        PlayerMobile pm = escorter as PlayerMobile;

                        if (pm != null)
                        {
                            if (pm.CompassionGains > 0 && DateTime.UtcNow > pm.NextCompassionDay)
                            {
                                pm.NextCompassionDay = DateTime.MinValue;
                                pm.CompassionGains = 0;
                            }

                            if (pm.CompassionGains >= 5) // have already gained 5 times in one day, can gain no more
                            {
                                pm.SendLocalizedMessage(1053004); // You must wait about a day before you can gain in compassion again.
                            }
                            else if (VirtueHelper.Award(pm, VirtueName.Compassion, escort.Compassion, ref gainedPath))
                            {
                                pm.SendLocalizedMessage(1074949, null, 0x2A);  // You have demonstrated your compassion!  Your kind actions have been noted.

                                if (gainedPath)
                                    pm.SendLocalizedMessage(1053005); // You have achieved a path in compassion!
                                else
                                    pm.SendLocalizedMessage(1053002); // You have gained in compassion.

                                pm.NextCompassionDay = DateTime.UtcNow + TimeSpan.FromDays(1.0); // in one day CompassionGains gets reset to 0
                                ++pm.CompassionGains;
                            }
                            else
                            {
                                pm.SendLocalizedMessage(1053003); // You have achieved the highest path of compassion and can no longer gain any further.
                            }
                        }
                    }
                    else
                    {
                        escorter.PlaySound(Quest.UpdateSound);
                    }

                    return true;
                }
            }
            else if (!m_Checked)
            {
                Region region = GetDestination();

                if (region != null && region.Contains(Location))
                {
                    m_DeleteTimer = Timer.DelayCall(TimeSpan.FromSeconds(5.0), new TimerCallback(Delete));
                    m_Checked = true;
                }
            }

            return false;
        }

        private class AbandonEscortEntry : ContextMenuEntry
        {
            private readonly BaseEscort m_Mobile;

            public AbandonEscortEntry(BaseEscort m)
                : base(6102, 3)
            {
                m_Mobile = m;
            }

            public override void OnClick()
            {
                Owner.From.SendLocalizedMessage(1071194); // You have failed your escort quest…
                Owner.From.PlaySound(0x5B3);
                m_Mobile.Delete();
            }
        }

        public static void DeleteEscort(Mobile owner)
        {
            PlayerMobile pm = owner as PlayerMobile;

            foreach (var escortquest in pm.Quests.Where(x => x.Quester is BaseEscort))
            {
                BaseEscort escort = (BaseEscort)escortquest.Quester;

                Timer.DelayCall(TimeSpan.FromSeconds(3), new TimerCallback(
                delegate
                {
                    escort.Say(500901); // Ack!  My escort has come to haunt me!
                    owner.SendLocalizedMessage(1071194); // You have failed your escort quest…
                    owner.PlaySound(0x5B3);
                    escort.Delete();
                }));
            }            
        }
    }
}
