using Server;
using System;
using System.Collections.Generic;
using Server.Items;
using Server.Mobiles;
using Server.Engines.Points;
using System.Linq;
using Server.Commands;
using Server.Gumps;

//TODO: Party: 1152064 You cannot invite other players in an arena to your party!
namespace Server.Engines.ArenaSystem
{
    public class PVPArenaSystem : PointsSystem
    {
        public static PVPArenaSystem Instance { get; set; }
        public static bool Enabled { get { return Core.HS; } }
        public static bool BlockSameIP { get { return true; } }

        public override PointsType Loyalty { get { return PointsType.PVPArena; } }
        public override TextDefinition Name { get { return m_Name; } }
        public override bool AutoAdd { get { return true; } }
        public override double MaxPoints { get { return double.MaxValue; } }

        public override bool ShowOnLoyaltyGump { get { return false; } }
        private TextDefinition m_Name = new TextDefinition("Arena Stats");

        public static List<PVPArena> Arenas { get; set; }
        public static List<string> BlockedArenas { get; set; }

        public static bool SystemInitialized { get; set; }

        public PVPArenaSystem()
        {
            Instance = this;

            if (Enabled)
            {
                CommandSystem.Register("ResetArenaStats", AccessLevel.Administrator, ResetStats_OnTarget);
                CommandSystem.Register("ArenaSetup", AccessLevel.Administrator, ArenaSetup);
            }
        }

        public void OnTick()
        {
            Arenas.ForEach(a => a.OnTick());
        }

        public List<ArenaDuel> GetBookedDuels()
        {
            List<ArenaDuel> booked = new List<ArenaDuel>();

            foreach (var arena in Arenas.Where(a => a.BookedDuels.Count > 0))
            {
                booked.AddRange(arena.BookedDuels);
            }

            return booked;
        }

        public ArenaDuel GetBookedDuel(PlayerMobile pm)
        {
            foreach (var arena in Arenas.Where(a => a.BookedDuels.Count > 0))
            {
                foreach (var duel in arena.BookedDuels.Where(d => d.IsParticipant(pm)))
                {
                    return duel;
                }
            }

            return null;
        }

        public override void SendMessage(PlayerMobile from, double old, double points, bool quest)
        {
        }

        public override TextDefinition GetTitle(PlayerMobile from)
        {
            return new TextDefinition("Arena Stats");
        }

        public override PointsEntry GetSystemEntry(PlayerMobile pm)
        {
            return new PlayerStatsEntry(pm);
        }

        public void Register(PVPArena arena)
        {
            if (Arenas == null)
                Arenas = new List<PVPArena>();

            if (!Arenas.Contains(arena))
                Arenas.Add(arena);
        }

        public void Unregister(PVPArena arena)
        {
            if (Arenas != null)
            {
                Arenas.Remove(arena);
            }

            arena.Unregister();
        }

        public void AddBlockedArena(PVPArena arena)
        {
            if (BlockedArenas == null)
            {
                BlockedArenas = new List<string>();
            }

            if (!IsBlocked(arena.Definition))
            {
                Utility.WriteConsoleColor(ConsoleColor.Green, "Adding blocked EA PVP Arena: {0}", arena.Definition.Name);
                BlockedArenas.Add(arena.Definition.Name);
                Unregister(arena);
            }
        }

        public void RemoveBlockedArena(ArenaDefinition def)
        {
            if (BlockedArenas == null)
            {
                return;
            }

            BlockedArenas.Remove(def.Name);

            if (Arenas == null || Arenas.Count == 0)
            {
                Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), () => Instance.OnTick());
            }

            var arena = new PVPArena(def);
            Instance.Register(arena);
            Utility.WriteConsoleColor(ConsoleColor.Green, "Removing blocked EA PVP Arena: {0}", arena.Definition.Name);
            arena.ConfigureArena();
        }

        public bool IsBlocked(ArenaDefinition def)
        {
            return BlockedArenas != null && BlockedArenas.Contains(def.Name);
        }

        public static bool IsEnemy(Mobile source, Mobile target)
        {
            var sourceRegion = Region.Find(source.Location, source.Map) as ArenaRegion;
            var targetRegion = Region.Find(target.Location, target.Map) as ArenaRegion;

            if (sourceRegion != null && sourceRegion.Arena.CurrentDuel != null && sourceRegion == targetRegion)
            {
                return sourceRegion.Arena.CurrentDuel.IsEnemy(source, target);
            }

            return false;
        }

        public static bool IsFriendly(Mobile source, Mobile target)
        {
            var sourceRegion = Region.Find(source.Location, source.Map) as ArenaRegion;
            var targetRegion = Region.Find(target.Location, target.Map) as ArenaRegion;

            if (sourceRegion != null && sourceRegion.Arena.CurrentDuel != null && sourceRegion == targetRegion)
            {
                return sourceRegion.Arena.CurrentDuel.IsFriendly(source, target);
            }

            return false;
        }

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

            writer.Write(2);

            writer.Write(BlockedArenas == null ? 0 : BlockedArenas.Count);

            if (BlockedArenas != null)
            {
                for (int i = 0; i < BlockedArenas.Count; i++)
                {
                    writer.Write(BlockedArenas[i]);
                }
            }

            writer.Write(Arenas == null ? 0 : Arenas.Count);

            if (Arenas != null)
            {
                for (int i = 0; i < Arenas.Count; i++)
                {
                    writer.Write(Arenas[i].Definition.Name);
                    Arenas[i].Serialize(writer);
                }
            }
        }

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

            int version = reader.ReadInt();

            if (version < 2)
            {
                InitializeArenas();
            }

            switch (version)
            {
                case 2:
                case 1:
                    int c = reader.ReadInt();

                    for (int i = 0; i < c; i++)
                    {
                        if (BlockedArenas == null)
                            BlockedArenas = new List<string>();

                        BlockedArenas.Add(reader.ReadString());
                    }
                    goto case 0;
                case 0:
                    int count = reader.ReadInt();

                    for (int i = 0; i < count; i++)
                    {
                        if (Arenas == null)
                            Arenas = new List<PVPArena>();

                        if (version >= 2)
                        {
                            var arena = new PVPArena(GetDefinition(reader.ReadString()));
                            Register(arena);
                            arena.Deserialize(reader);
                        }
                        else
                        {
                            Arenas[i].Deserialize(reader);
                        }
                    }
                    break;
            }
        }

        private ArenaDefinition GetDefinition(string name)
        {
            var def = ArenaDefinition.Definitions.FirstOrDefault(d => d.Name == name);

            if (def == null)
            {
                return ArenaDefinition.Definitions[0];
            }

            return def;
        }

        public static void SendMessage(Mobile from, string message, int hue = 0x1F)
        {
            from.SendMessage(hue, message);
        }

        public static void SendMessage(Mobile from, int message, string args = "", int hue = 0x1F)
        {
            from.SendLocalizedMessage(message, args, hue);
        }

        public static void SendParticipantMessage(ArenaDuel duel, int message, bool inRegion = false, string args = "", int hue = 0x1F)
        {
            foreach (var part in duel.GetParticipants(inRegion))
            {
                SendMessage(part.Key, message, args, hue);
            }
        }

        public void CheckTitle(PlayerMobile pm)
        {
            var entry = GetPlayerEntry<PlayerStatsEntry>(pm);
            int title = 0;

            switch (entry.TotalDuels)
            {
                case 1: title = 1152068 + (int)ArenaTitle.FledglingGladiator; break;
                case 50: title = 1152068 + (int)ArenaTitle.BuddingGladiator; break;
                case 100: title = 1152068 + (int)ArenaTitle.Gladiator; break;
                case 250: title = 1152068 + (int)ArenaTitle.WellKnownGladiator; break;
                case 500: title = 1152068 + (int)ArenaTitle.VeteranGladiator; break;
            }

            if (title > 0)
            {
                pm.AddRewardTitle(title);
                pm.SendLocalizedMessage(1152067, String.Format("#{0}", title.ToString())); // You have gotten a new subtitle, ~1_VAL~, in reward for your duel!
            }
        }

        public static bool HasSameIP(Mobile m, ArenaDuel duel)
        {
            if (duel == null || m.AccessLevel > AccessLevel.Player)
                return false;

            foreach (var kvp in duel.GetParticipants())
            {
                if (IsSameIP(m, kvp.Key))
                {
                    return true;
                }
            }

            return false;
        }

        public static bool IsSameIP(Mobile one, Mobile two)
        {
            if (one.NetState == null || two.NetState == null || one.AccessLevel > AccessLevel.Player || two.AccessLevel > AccessLevel.Player)
                return false;

            var oneAddress = one.NetState.Address;
            var twoAddress = two.NetState.Address;

            return one.NetState.Address == two.NetState.Address;
        }

        public static void Initialize()
        {
            if (Enabled)
            {
                if (!SystemInitialized)
                {
                    InitializeArenas();
                    SystemInitialized = true;
                }

                if (Arenas != null)
                {
                    foreach (var arena in Arenas)
                    {
                        arena.ConfigureArena();
                    }

                    Timer.DelayCall(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1), () => Instance.OnTick());
                }
            }
        }

        public static void InitializeArenas()
        {
            if (CanInitialize(ArenaDefinition.LostLandsTrammel))
            {
                Instance.Register(new PVPArena(ArenaDefinition.LostLandsTrammel));
            }

            if (CanInitialize(ArenaDefinition.LostLandsFelucca))
            {
                Instance.Register(new PVPArena(ArenaDefinition.LostLandsFelucca));
            }

            if (CanInitialize(ArenaDefinition.HavenTrammel))
            {
                Instance.Register(new PVPArena(ArenaDefinition.HavenTrammel));
            }

            if (CanInitialize(ArenaDefinition.HavenFelucca))
            {
                Instance.Register(new PVPArena(ArenaDefinition.HavenFelucca));
            }
        }

        private static bool CanInitialize(ArenaDefinition def)
        {
            return !Instance.IsBlocked(def) && (Arenas == null || !Arenas.Any(arena => arena.Definition == def));
        }

        [Usage("ArenaSetup")]
        [Description("Gives gump for arena setup.")]
        public static void ArenaSetup(CommandEventArgs e)
        {
            var pm = e.Mobile as PlayerMobile;

            if(pm != null)
            {
                BaseGump.SendGump(new PVPArenaSystemSetupGump(pm));
            }
        }

        [Usage("ResetArenaStats")]
        [Description("Target an arena stone to reset/wipe the stats associated with that arena.")]
        public static void ResetStats_OnTarget(CommandEventArgs e)
        {
            Mobile m = e.Mobile;

            m.BeginTarget(-1, false, Server.Targeting.TargetFlags.None, (fro, targeted) =>
                {
                    if (m is PlayerMobile && targeted is ArenaStone)
                    {
                        var stone = (ArenaStone)targeted;

                        if (stone.Arena != null)
                        {
                            var arena = stone.Arena;

                            BaseGump.SendGump(new GenericConfirmCallbackGump<PVPArena>((PlayerMobile)m,
                                String.Format("Reset {0} Statistics?", arena.Definition.Name),
                                "By selecting yes, you will permanently wipe the stats associated to this arena.",
                                arena,
                                null,
                                (from, a) =>
                                {
                                    ColUtility.Free(a.TeamRankings);
                                    ColUtility.Free(a.SurvivalRankings);
                                    from.SendMessage("Arena stats cleared.");
                                }));
                        }
                    }
                });
        }
    }

    public class PlayerStatsEntry : PointsEntry
    {
        public int SurvivalWins { get; set; }
        public int SurvivalLosses { get; set; }
        public int SurvivalDraws { get; set; }

        public int TeamWins { get; set; }
        public int TeamLosses { get; set; }
        public int TeamDraws { get; set; }

        public int Kills { get; set; }
        public int Deaths { get; set; }

        public bool IgnoreInvites { get; set; }
        public bool OpenStats { get; set; }

        public int TotalDuels { get { return SurvivalWins + SurvivalLosses + SurvivalDraws + TeamWins + TeamLosses + TeamDraws; } }

        public List<DuelRecord> Record { get; set; }

        public DuelProfile Profile { get; set; }

        public PlayerStatsEntry(PlayerMobile pm)
            : base(pm)
        {
            IgnoreInvites = true;
            OpenStats = true;

            Record = new List<DuelRecord>();
        }

        public void HandleDeath(Mobile m, bool killedBy)
        {
            Record.Add(new DuelRecord(m, killedBy));

            if (Record.Count > 12)
            {
                Record.RemoveAt(12);
            }
        }

        // Rating, seems to start at 10000, then +33 for win, -33 for loss
        public override void Serialize(GenericWriter writer)
        {
            base.Serialize(writer);
            writer.Write(0);

            if (Profile != null)
            {
                writer.Write(1);
                Profile.Serialize(writer);
            }
            else
            {
                writer.Write(0);
            }

            writer.Write(Record.Count);
            for (int i = 0; i < Record.Count; i++)
            {
                Record[i].Serialize(writer);
            }

            writer.Write(OpenStats);
            writer.Write(IgnoreInvites);

            writer.Write(SurvivalWins);
            writer.Write(SurvivalLosses);
            writer.Write(SurvivalDraws);

            writer.Write(TeamWins);
            writer.Write(TeamLosses);
            writer.Write(TeamDraws);

            writer.Write(Kills);
            writer.Write(Deaths);
        }

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

            Record = new List<DuelRecord>();

            if (reader.ReadInt() == 1)
            {
                Profile = new DuelProfile(reader);
            }

            int count = reader.ReadInt();
            for (int i = 0; i < count; i++)
            {
                Record.Add(new DuelRecord(reader));
            }

            OpenStats = reader.ReadBool();
            IgnoreInvites = reader.ReadBool();

            SurvivalWins = reader.ReadInt();
            SurvivalLosses = reader.ReadInt();
            SurvivalDraws = reader.ReadInt();

            TeamWins = reader.ReadInt();
            TeamLosses = reader.ReadInt();
            TeamDraws = reader.ReadInt();

            Kills = reader.ReadInt();
            Deaths = reader.ReadInt();
        }

        public class DuelRecord
        {
            public Mobile Opponent { get; set; }
            public bool KilledBy { get; set; }
            public DateTime DuelDate { get; set; }

            public DuelRecord(Mobile opponent, bool killedBy)
            {
                Opponent = opponent;
                KilledBy = killedBy;
                DuelDate = DateTime.Now;
            }

            public DuelRecord(GenericReader reader)
            {
                int version = reader.ReadInt();

                Opponent = reader.ReadMobile();
                KilledBy = reader.ReadBool();
                DuelDate = reader.ReadDateTime();
            }

            public void Serialize(GenericWriter writer)
            {
                writer.Write(0);

                writer.Write(Opponent);
                writer.Write(KilledBy);
                writer.Write(DuelDate);
            }
        }

        public class DuelProfile
        {
            public int Entries { get; set; }
            public RoomType RoomType { get; set; }
            public BattleMode BattleMode { get; set; }
            public bool Ranked { get; set; }
            public TimeLimit TimeLimit { get; set; }
            public EntryFee EntryFee { get; set; }
            public int PetSlots { get; set; }
            public bool RidingFlyingAllowed { get; set; }
            public bool RangedWeaponsAllowed { get; set; }
            public bool SummonSpellsAllowed { get; set; }
            public bool FieldSpellsAllowed { get; set; }
            public PotionRules PotionRules { get; set; }

            public DuelProfile(ArenaDuel duel)
            {
                Entries = duel.Entries;
                RoomType = duel.RoomType;
                BattleMode = duel.BattleMode;
                Ranked = duel.Ranked;
                TimeLimit = duel.TimeLimit;
                EntryFee = duel.EntryFee;
                PetSlots = duel.PetSlots;
                RidingFlyingAllowed = duel.RidingFlyingAllowed;
                RangedWeaponsAllowed = duel.RangedWeaponsAllowed;
                SummonSpellsAllowed = duel.SummonSpellsAllowed;
                FieldSpellsAllowed = duel.FieldSpellsAllowed;
                PotionRules = duel.PotionRules;
            }

            public DuelProfile(GenericReader reader)
            {
                int version = reader.ReadInt();

                Entries = reader.ReadInt();
                RoomType = (RoomType)reader.ReadInt();
                BattleMode = (BattleMode)reader.ReadInt();
                Ranked = reader.ReadBool();
                TimeLimit = (TimeLimit)reader.ReadInt();
                EntryFee = (EntryFee)reader.ReadInt();
                PetSlots = reader.ReadInt();
                RidingFlyingAllowed = reader.ReadBool();
                RangedWeaponsAllowed = reader.ReadBool();
                SummonSpellsAllowed = reader.ReadBool();
                FieldSpellsAllowed = reader.ReadBool();
                PotionRules = (PotionRules)reader.ReadInt();
            }

            public void Serialize(GenericWriter writer)
            {
                writer.Write(0);

                writer.Write(Entries);
                writer.Write((int)RoomType);
                writer.Write((int)BattleMode);
                writer.Write(Ranked);
                writer.Write((int)TimeLimit);
                writer.Write((int)EntryFee);
                writer.Write(PetSlots);
                writer.Write(RidingFlyingAllowed);
                writer.Write(RangedWeaponsAllowed);
                writer.Write(SummonSpellsAllowed);
                writer.Write(FieldSpellsAllowed);
                writer.Write((int)PotionRules);
            }
        }
    }
}

namespace Server.Mobiles
{
    public enum ArenaTitle
    {
        FledglingGladiator = 0,
        BuddingGladiator = 1,
        Gladiator = 3,
        WellKnownGladiator = 4,
        VeteranGladiator =5
    }
}
