using System;
using System.Xml;
using System.Linq;

using Server;
using Server.Mobiles;
using Server.Items;
using Server.Multis;
using System.Collections.Generic;
using Server.Commands;
using Server.Engines.Quests;
using Server.Spells;

namespace Server.Regions
{
    public class SeaMarketRegion : BaseRegion
    {
        private static readonly TimeSpan KickDuration = TimeSpan.FromMinutes(20);

        private static SeaMarketRegion m_Region1;
        private static SeaMarketRegion m_Region2;

        private Timer m_Timer;

        private static Timer m_BlabTimer;
        private static bool m_RestrictBoats;

        private Dictionary<BaseBoat, DateTime> m_BoatTable = new Dictionary<BaseBoat, DateTime>();
        public Dictionary<BaseBoat, DateTime> BoatTable { get { return m_BoatTable; } }

        public static bool RestrictBoats
        {
            get { return m_RestrictBoats; }
            set
            {
                m_RestrictBoats = value;

                if (value)
                {
                    if (m_Region1 != null)
                        m_Region1.StartTimer();

                    if (m_Region2 != null)
                        m_Region2.StartTimer();
                }
                else
                {
                    if (m_Region1 != null)
                        m_Region1.StopTimer();

                    if (m_Region2 != null)
                        m_Region2.StopTimer();
                }
            }
        }

        public static Rectangle2D[] Bounds { get { return m_Bounds; } }
        private static Rectangle2D[] m_Bounds = new Rectangle2D[]
        {
            new Rectangle2D(4529, 2296, 45, 112),
        };

        public SeaMarketRegion(XmlElement xml, Map map, Region parent)
            : base(xml, map, parent)
        {
        }

        public override void OnRegister()
        {
            if (m_Region1 == null)
            {
                m_Region1 = this;
            }
            else if (m_Region2 == null)
            {
                m_Region2 = this;
            }
        }

        public override bool CheckTravel(Mobile traveller, Point3D p, TravelCheckType type)
        {
            switch (type)
            {
                case TravelCheckType.RecallTo:
                case TravelCheckType.GateTo:
                    {
                        return BaseBoat.FindBoatAt(p, Map) != null;
                    }
                case TravelCheckType.Mark:
                    {
                        return false;
                    }
            }

            return base.CheckTravel(traveller, p, type);
        }

        public override bool AllowHousing(Mobile from, Point3D p)
        {
            return false;
        }

        #region Pirate Blabbing
        public static Dictionary<Mobile, DateTime> m_PirateBlabTable = new Dictionary<Mobile, DateTime>();
        private static readonly TimeSpan BlabDuration = TimeSpan.FromSeconds(60);

        public static void TryPirateBlab(Mobile from, Mobile npc)
        {
            if (m_PirateBlabTable.ContainsKey(from) && m_PirateBlabTable[from] > DateTime.UtcNow || BountyQuestSpawner.Bounties.Count <= 0)
                return;

            //Make of list of bounties on their map
            List<Mobile> bounties = new List<Mobile>();
            foreach (Mobile mob in BountyQuestSpawner.Bounties.Keys)
            {
                if (mob.Map == from.Map && mob is PirateCaptain && !bounties.Contains(mob))
                    bounties.Add(mob);
            }

            if (bounties.Count > 0)
            {
                Mobile bounty = bounties[Utility.Random(bounties.Count)];

                if (bounty != null)
                {
                    PirateCaptain capt = (PirateCaptain)bounty;

                    int xLong = 0, yLat = 0;
                    int xMins = 0, yMins = 0;
                    bool xEast = false, ySouth = false;
                    Point3D loc = capt.Location;
                    Map map = capt.Map;

                    string locArgs;
                    string combine;

                    if (Sextant.Format(loc, map, ref xLong, ref yLat, ref xMins, ref yMins, ref xEast, ref ySouth))
                        locArgs = String.Format("{0}°{1}'{2},{3}°{4}'{5}", yLat, yMins, ySouth ? "S" : "N", xLong, xMins, xEast ? "E" : "W");
                    else
                        locArgs = "?????";

                    combine = String.Format("{0}\t{1}", capt.PirateName > -1 ? String.Format("#{0}", capt.PirateName) : capt.Name, locArgs);

                    int cliloc = Utility.RandomMinMax(1149856, 1149865);
                    npc.SayTo(from, cliloc, combine);

                    m_PirateBlabTable[from] = DateTime.UtcNow + BlabDuration;
                }
            }

            ColUtility.Free(bounties);
        }

        public static void CheckBlab_Callback()
        {
            CheckBabble(m_Region1);
            CheckBabble(m_Region2);
            CheckBabble(TokunoDocksRegion.Instance);
        }

        public static void CheckBabble(Region r)
        {
            if (r == null)
                return;

            foreach (var player in r.GetEnumeratedMobiles().Where(p => p is PlayerMobile && 
                                                                       p.Alive /*&&
                                                                       QuestHelper.GetQuest((PlayerMobile)p, typeof(ProfessionalBountyQuest)) != null*/))
            {
                    IPooledEnumerable eable = player.GetMobilesInRange(4);

                    foreach (Mobile mob in eable)
                    {
                        if (mob is BaseVendor || mob is MondainQuester || mob is GalleonPilot)
                        {
                            TryPirateBlab(player, mob);
                            break;
                        }
                    }
                    eable.Free();
            }
        }
        #endregion

        #region Boat Restriction
        public void StartTimer()
        {
            if (m_Timer != null)
                m_Timer.Stop();

            m_Timer = new InternalTimer(this);
            m_Timer.Start();
        }

        public void StopTimer()
        {
            if (m_Timer != null)
                m_Timer.Stop();

            m_Timer = null;
        }

        public List<BaseBoat> GetBoats()
        {
            List<BaseBoat> list = new List<BaseBoat>();

            foreach (BaseBoat boat in this.GetEnumeratedMultis().OfType<BaseBoat>())
                list.Add(boat);

            return list;
        }

        public void OnTick()
        {
            if (!m_RestrictBoats)
            {
                StopTimer();
                return;
            }

            List<BaseBoat> boats = GetBoats();
            List<BaseBoat> toRemove = new List<BaseBoat>();

            foreach (KeyValuePair<BaseBoat, DateTime> kvp in m_BoatTable)
            {
                BaseBoat boat = kvp.Key;
                DateTime moveBy = kvp.Value;

                if (boat == null || !boats.Contains(boat) || boat.Deleted)
                    toRemove.Add(boat);
                else if (DateTime.UtcNow >= moveBy && KickBoat(boat))
                    toRemove.Add(boat);
                else
                {
                    if (boat.Owner != null && boat.Owner.NetState != null)
                    {
                        TimeSpan ts = moveBy - DateTime.UtcNow;

                        if ((int)ts.TotalMinutes <= 10)
                        {
                            int rem = Math.Max(1, (int)ts.TotalMinutes);
                            boat.Owner.SendLocalizedMessage(1149787 + (rem - 1));
                        }
                    }
                }
            }

            foreach (BaseBoat boat in boats)
            {
                if (!m_BoatTable.ContainsKey(boat) && !boat.IsMoving && boat.Owner != null && boat.Owner.AccessLevel == AccessLevel.Player)
                    AddToTable(boat);
            }

            foreach (BaseBoat b in toRemove)
                m_BoatTable.Remove(b);

            ColUtility.Free(toRemove);
            ColUtility.Free(boats);
        }

        public void AddToTable(BaseBoat boat)
        {
            if (m_BoatTable.ContainsKey(boat))
                return;

            m_BoatTable.Add(boat, DateTime.UtcNow + KickDuration);

            if (boat.Owner != null && boat.Owner.NetState != null)
                boat.Owner.SendMessage("You can only dock your boat here for {0} minutes.", (int)KickDuration.TotalMinutes);
        }

        private Rectangle2D[] m_KickLocs = new Rectangle2D[]
        {
	        new Rectangle2D(m_Bounds[0].X - 100, m_Bounds[0].X - 100, 200 + m_Bounds[0].Width, 100),
	        new Rectangle2D(m_Bounds[0].X - 100, m_Bounds[0].Y, 100, m_Bounds[0].Height + 100),
	        new Rectangle2D(m_Bounds[0].X, m_Bounds[0].Y + m_Bounds[0].Height, m_Bounds[0].Width + 100, 100),
	        new Rectangle2D(m_Bounds[0].X + m_Bounds[0].Width, m_Bounds[0].Y, 100, m_Bounds[0].Height),
        };

        public bool KickBoat(BaseBoat boat)
        {
            if (boat == null || boat.Deleted)
                return false;

            for (int i = 0; i < 25; i++)
            {
                Rectangle2D rec = m_KickLocs[Utility.Random(m_KickLocs.Length)];

                int x = Utility.RandomMinMax(rec.X, rec.X + rec.Width);
                int y = Utility.RandomMinMax(rec.Y, rec.Y + rec.Height);
                int z = boat.Z;

                Point3D p = new Point3D(x, y, z);

                if (boat.CanFit(p, boat.Map, boat.ItemID))
                {
                    boat.Teleport(x - boat.X, y - boat.Y, z - boat.Z);

                    if (boat.Owner != null && boat.Owner.NetState != null)
                        boat.SendMessageToAllOnBoard(1149785); //A strong tide comes and carries your boat to deeper water.
                    return true;
                }
            }
            return false;
        }

        private class InternalTimer : Timer
        {
            private SeaMarketRegion m_Region;

            public InternalTimer(SeaMarketRegion reg)
                : base(TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1))
            {
                m_Region = reg;
            }

            protected override void OnTick()
            {
                if (m_Region != null)
                    m_Region.OnTick();
            }
        }

        public static void StartTimers_Callback()
        {
            RestrictBoats = m_RestrictBoats;

            m_BlabTimer = Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), new TimerCallback(CheckBlab_Callback));
            m_BlabTimer.Priority = TimerPriority.OneSecond;
        }
        #endregion

        public static void Save(GenericWriter writer)
        {
            writer.Write((int)0);

            writer.Write(m_RestrictBoats);
        }

        public static void Load(GenericReader reader)
        {
            int version = reader.ReadInt();

            m_RestrictBoats = reader.ReadBool();

            Timer.DelayCall(TimeSpan.FromSeconds(30), new TimerCallback(StartTimers_Callback));
        }

        public static void GetBoatInfo_OnCommand(CommandEventArgs e)
        {
            List<BaseBoat> boats = new List<BaseBoat>(m_Region1.BoatTable.Keys);
            List<DateTime> times = new List<DateTime>(m_Region1.BoatTable.Values);

            e.Mobile.SendMessage("========Boat Info for Felucca as Follows===========");
            e.Mobile.SendMessage("Boats: {0}", boats.Count);

            if (!m_RestrictBoats)
                e.Mobile.SendMessage("Boat restriction is currenlty disabled.");

            Console.WriteLine("========Boat Info as Follows===========");
            Console.WriteLine("Boats: {0}", boats.Count);

            if (!m_RestrictBoats)
                Console.WriteLine("Boat restriction is currenlty disabled.");

            for (int i = 0; i < boats.Count; i++)
            {
                BaseBoat boat = boats[i];

                if (boat == null || boat.Deleted)
                    continue;

                e.Mobile.SendMessage("Boat Name: {0}; Boat Owner: {1}; Expires: {2}", boat.ShipName, boat.Owner, times[i]);

                Console.WriteLine("Boat Name: {0}; Boat Owner: {1}; Expires: {2}", boat.ShipName, boat.Owner, times[i]);
            }

            boats.Clear();
            times.Clear();

            boats = new List<BaseBoat>(m_Region2.BoatTable.Keys);
            times = new List<DateTime>(m_Region2.BoatTable.Values);

            e.Mobile.SendMessage("========Boat Info for Trammel as Follows===========");
            e.Mobile.SendMessage("Boats: {0}", boats.Count);

            if (!m_RestrictBoats)
                e.Mobile.SendMessage("Boat restriction is currenlty disabled.");

            Console.WriteLine("========Boat Info as Follows===========");
            Console.WriteLine("Boats: {0}", boats.Count);

            if (!m_RestrictBoats)
                Console.WriteLine("Boat restriction is currenlty disabled.");

            for (int i = 0; i < boats.Count; i++)
            {
                BaseBoat boat = boats[i];

                if (boat == null || boat.Deleted)
                    continue;

                e.Mobile.SendMessage("Boat Name: {0}; Boat Owner: {1}; Expires: {2}", boat.ShipName, boat.Owner, times[i]);

                Console.WriteLine("Boat Name: {0}; Boat Owner: {1}; Expires: {2}", boat.ShipName, boat.Owner, times[i]);
            }
        }

        public static void SetRestriction_OnCommand(CommandEventArgs e)
        {
            if (m_RestrictBoats)
            {
                RestrictBoats = false;

                e.Mobile.SendMessage("Boat restriction in the sea market is no longer active.");
            }
            else
            {
                RestrictBoats = true;

                e.Mobile.SendMessage("Boat restriction in the sea market is now active.");
            }
        }
    }
}
