using Server.ContextMenus;
using Server.Items;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Server.Mobiles
{
    public class BaseHire : BaseCreature
    {
        public override bool IsBondable => false;
        public override bool CanAutoStable => false;
        public override bool CanDetectHidden => false;
        public override bool KeepsItemsOnDeath => true;

        private bool _IsHired;

        [CommandProperty(AccessLevel.GameMaster)]
        public DateTime NextPay { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public int Pay => PerDayCost();

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

        public int GoldOnDeath { get; set; }

        [CommandProperty(AccessLevel.GameMaster)]
        public bool IsHired
        {
            get { return _IsHired; }
            set
            {
                _IsHired = value;

                Delta(MobileDelta.Noto);
                InvalidateProperties();
            }
        }

        public BaseHire(AIType AI)
            : base(AI, FightMode.Aggressor, 10, 1, 0.1, 4.0)
        {
            ControlSlots = 2;
            HoldGold = 8;
        }

        public BaseHire()
            : base(AIType.AI_Melee, FightMode.Aggressor, 10, 1, 0.1, 4.0)
        {
            ControlSlots = 2;
        }

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

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

            writer.Write(1);// version

            writer.Write(NextPay);
            writer.Write(IsHired);
            writer.Write(HoldGold);
        }

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

            int version = reader.ReadInt();

            switch (version)
            {
                case 1:
                    NextPay = reader.ReadDateTime();
                    goto case 0;
                case 0:
                    IsHired = reader.ReadBool();
                    HoldGold = reader.ReadInt();
                    break;
            }

            if (IsHired)
            {
                PayTimer.RegisterTimer(this);
            }
        }

        public override bool OnBeforeDeath()
        {
            if (Backpack != null)
            {
                Item[] AllGold = Backpack.FindItemsByType(typeof(Gold), true);

                if (AllGold != null)
                {
                    foreach (Gold g in AllGold)
                    {
                        GoldOnDeath += g.Amount;
                    }
                }
            }

            return base.OnBeforeDeath();
        }

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

            PayTimer.RemoveTimer(this);
        }

        public override void OnDeath(Container c)
        {
            if (GoldOnDeath > 0)
            {
                c.DropItem(new Gold(GoldOnDeath));
            }

            base.OnDeath(c);
        }

        #region [ GetOwner ]
        public virtual Mobile GetOwner()
        {
            if (!Controlled)
            {
                return null;
            }

            var owner = ControlMaster;
            IsHired = true;

            if (owner == null)
            {
                return null;
            }

            if (owner.Deleted)
            {
                Say(1005653, 0x3B2);// Hmmm.  I seem to have lost my master.
                SetControlMaster(null);
                return null;
            }

            return owner;
        }

        #endregion 

        #region [ AddHire ] 
        public virtual bool AddHire(Mobile m)
        {
            Mobile owner = GetOwner();

            if (owner != null)
            {
                m.SendLocalizedMessage(1043283, owner.Name);// I am following ~1_NAME~. 
                return false;
            }

            if (SetControlMaster(m))
            {
                IsHired = true;

                return true;
            }

            return false;
        }

        #endregion 

        #region [ PerDayCost ] 
        public int PerDayCost()
        {
            var pay = (int)Skills[SkillName.Anatomy].Value + (int)Skills[SkillName.Tactics].Value;
            pay += (int)Skills[SkillName.Macing].Value + (int)Skills[SkillName.Swords].Value;
            pay += (int)Skills[SkillName.Fencing].Value + (int)Skills[SkillName.Archery].Value;
            pay += (int)Skills[SkillName.MagicResist].Value + (int)Skills[SkillName.Healing].Value;
            pay += (int)Skills[SkillName.Magery].Value + (int)Skills[SkillName.Parry].Value;
            pay /= 35;
            pay += 1;

            return pay;
        }

        #endregion 

        #region [ OnDragDrop ]
        public override bool OnDragDrop(Mobile from, Item item)
        {
            if (Pay != 0)
            {
                // Is the creature already hired
                if (!Controlled)
                {
                    // Is the item the payment in gold
                    if (item is Gold)
                    {
                        // Is the payment in gold sufficient
                        if (item.Amount >= Pay)
                        {
                            if (from.Followers + ControlSlots > from.FollowersMax)
                            {
                                SayTo(from, 500896, 0x3B2); // I see you already have an escort.
                                return false;
                            }

                            // Try to add the hireling as a follower
                            if (AddHire(from))
                            {
                                SayTo(from, 1043258, string.Format("{0}", item.Amount / Pay), 0x3B2);//"I thank thee for paying me. I will work for thee for ~1_NUMBER~ days.", (int)item.Amount / Pay );
                                HoldGold += item.Amount;

                                NextPay = DateTime.UtcNow + PayTimer.GetInterval();

                                PayTimer.RegisterTimer(this);
                                return true;
                            }
                            else
                            {
                                return false;
                            }
                        }
                        else
                        {
                            SayHireCost();
                        }
                    }
                    else
                    {
                        SayTo(from, 1043268, 0x3B2);// Tis crass of me, but I want gold
                    }
                }
                else
                {
                    SayTo(from, 1042495, 0x3B2);// I have already been hired.
                }
            }
            else
            {
                SayTo(from, 500200, 0x3B2);// I have no need for that.
            }

            return base.OnDragDrop(from, item);
        }

        #endregion 

        #region [ OnSpeech ] 
        internal void SayHireCost()
        {
            Say(1043256, string.Format("{0}", Pay), 0x3B2);// "I am available for hire for ~1_AMOUNT~ gold coins a day. If thou dost give me gold, I will work for thee."
        }

        public override void OnSpeech(SpeechEventArgs e)
        {
            if (!e.Handled && e.Mobile.InRange(this, 6))
            {
                int[] keywords = e.Keywords;
                string speech = e.Speech;

                // Check for a greeting, a 'hire', or a 'servant'
                if (e.HasKeyword(0x003B) || e.HasKeyword(0x0162) || e.HasKeyword(0x000C))
                {
                    if (!Controlled)
                    {
                        e.Handled = true;
                        SayHireCost();
                    }
                    else
                    {
                        Say(1042495, 0x3B2);// I have already been hired.
                    }
                }
            }

            base.OnSpeech(e);
        }

        #endregion	

        #region [ GetContextMenuEntries ] 
        public override void GetContextMenuEntries(Mobile from, List<ContextMenuEntry> list)
        {
            if (Deleted)
                return;

            if (!Controlled)
            {
                if (CanPaperdollBeOpenedBy(from))
                    list.Add(new PaperdollEntry(this));

                list.Add(new HireEntry(from, this));
            }
            else
            {
                base.GetContextMenuEntries(from, list);
            }
        }

        #endregion

        #region [ Class PayTimer ]
        public class PayTimer : Timer
        {
            public static PayTimer Instance { get; set; }

            public List<BaseHire> Hires { get; set; } = new List<BaseHire>();

            public PayTimer()
                : base(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1))
            {
            }

            public static TimeSpan GetInterval()
            {
                return TimeSpan.FromMinutes(30.0);
            }

            protected override void OnTick()
            {
                var list = Hires.Where(v => v.NextPay <= DateTime.UtcNow).ToList();

                for (int i = 0; i < list.Count; i++)
                {
                    var hire = list[i];
                    hire.NextPay = DateTime.UtcNow + GetInterval();

                    int pay = hire.Pay;

                    if (hire.HoldGold <= pay)
                    {
                        hire.GetOwner();

                        hire.Say(503235, 0x3B2);// I regret nothing! 
                        hire.Delete();
                    }
                    else
                    {
                        hire.HoldGold -= pay;
                    }
                }

                ColUtility.Free(list);
            }

            public static void RegisterTimer(BaseHire hire)
            {
                if (Instance == null)
                {
                    Instance = new PayTimer();
                }

                if (!Instance.Running)
                {
                    Instance.Start();
                }

                if (!Instance.Hires.Contains(hire))
                {
                    Instance.Hires.Add(hire);
                }
            }

            public static void RemoveTimer(BaseHire hire)
            {
                if (Instance == null)
                {
                    return;
                }

                if (Instance.Hires.Contains(hire))
                {
                    Instance.Hires.Remove(hire);

                    if (Instance.Hires.Count == 0)
                    {
                        Instance.Stop();
                    }
                }
            }
        }
        #endregion 

        #region [ Class HireEntry ]
        public class HireEntry : ContextMenuEntry
        {
            private readonly Mobile m_Mobile;
            private readonly BaseHire m_Hire;

            public HireEntry(Mobile from, BaseHire hire)
                : base(6120, 3)
            {
                m_Hire = hire;
                m_Mobile = from;
            }

            public override void OnClick()
            {
                m_Hire.SayHireCost();
            }
        }
        #endregion
    }
}