using System;
using System.Collections.Generic;

using Server;
using Server.Commands;
using Server.Gumps;
using Server.Items;
using Server.Mobiles;
using Server.Targeting;

namespace Server.Poker
{
    public class PokerDealer : Mobile
    {
        public static void Initialize()
        {
            CommandSystem.Register("AddPokerSeat", AccessLevel.Administrator, new CommandEventHandler(AddPokerSeat_OnCommand));
            CommandSystem.Register("PokerKick", AccessLevel.Seer, new CommandEventHandler(PokerKick_OnCommand));

            EventSink.Disconnected += new DisconnectedEventHandler(EventSink_Disconnected);
        }

        private double m_Rake;
        private int m_RakeMax;
        private int m_MinBuyIn;
        private int m_MaxBuyIn;
        private int m_SmallBlind;
        private int m_BigBlind;
        private int m_MaxPlayers;
        private bool m_Active;
        private bool m_TournamentMode;
        private PokerGame m_Game;
        private List<Point3D> m_Seats;
        private Point3D m_ExitLocation;
        private Map m_ExitMap;

        private static int m_Jackpot;
        public static int Jackpot { get { return m_Jackpot; } set { m_Jackpot = value; } }

        [CommandProperty(AccessLevel.Seer)]
        public bool TournamentMode { get { return m_TournamentMode; } set { m_TournamentMode = value; } }
        [CommandProperty(AccessLevel.Administrator)]
        public bool ClearSeats { get { return false; } set { m_Seats.Clear(); } }
        [CommandProperty(AccessLevel.Administrator)]
        public int RakeMax { get { return m_RakeMax; } set { m_RakeMax = value; } }
        [CommandProperty(AccessLevel.Seer)]
        public int MinBuyIn { get { return m_MinBuyIn; } set { m_MinBuyIn = value; } }
        [CommandProperty(AccessLevel.Seer)]
        public int MaxBuyIn { get { return m_MaxBuyIn; } set { m_MaxBuyIn = value; } }
        [CommandProperty(AccessLevel.Seer)]
        public int SmallBlind { get { return m_SmallBlind; } set { m_SmallBlind = value; } }
        [CommandProperty(AccessLevel.Seer)]
        public int BigBlind { get { return m_BigBlind; } set { m_BigBlind = value; } }
        [CommandProperty(AccessLevel.Administrator)]
        public Point3D ExitLocation { get { return m_ExitLocation; } set { m_ExitLocation = value; } }
        [CommandProperty(AccessLevel.Administrator)]
        public Map ExitMap { get { return m_ExitMap; } set { m_ExitMap = value; } }
        [CommandProperty(AccessLevel.Administrator)]
        public double Rake
        {
            get { return m_Rake; }
            set
            {
                if (value > 1)
                    m_Rake = 1;
                else if (value < 0)
                    m_Rake = 0;
                else
                    m_Rake = value;
            }
        }
        [CommandProperty(AccessLevel.Seer)]
        public int MaxPlayers
        {
            get { return m_MaxPlayers; }
            set
            {
                if (value > 22) m_MaxPlayers = 22;
                else if (value < 0) m_MaxPlayers = 0;
                else m_MaxPlayers = value;
            }
        }

        // tissemand edit, helps a bit while debugging :|
        [CommandProperty(AccessLevel.Counselor)]
        public int CurrentSeatCount
        {
            get { return m_Seats.Count; }
        }
        [CommandProperty(AccessLevel.Seer)]
        public bool Active
        {
            get { return m_Active; }
            set
            {
                List<PokerPlayer> toRemove = new List<PokerPlayer>();

                if (!value)
                    foreach (PokerPlayer player in m_Game.Players.Players)
                        if (player.Mobile != null)
                            toRemove.Add(player);

                for (int i = 0; i < toRemove.Count; ++i)
                {
                    toRemove[i].Mobile.SendMessage(0x22, "The poker dealer has been set to inactive by a game master, and you are now being removed from the poker game and being refunded the money that you currently have.");
                    m_Game.RemovePlayer(toRemove[i]);
                }

                m_Active = value;
            }
        }

        public PokerGame Game { get { return m_Game; } set { m_Game = value; } }
        public List<Point3D> Seats { get { return m_Seats; } set { m_Seats = value; } }

        [Constructable]
        public PokerDealer()
            : this(10)
        {
        }

        [Constructable]
        public PokerDealer(int maxPlayers)
        {
            Blessed = true;
            Frozen = true;
            InitStats(100, 100, 100);

            Title = "the poker dealer";
            Hue = Utility.RandomSkinHue();
            NameHue = 0x35;

            if (this.Female = Utility.RandomBool())
            {
                this.Body = 0x191;
                this.Name = NameList.RandomName("female");
            }
            else
            {
                this.Body = 0x190;
                this.Name = NameList.RandomName("male");
            }

            Dress();

            MaxPlayers = maxPlayers;
            m_Seats = new List<Point3D>();
            m_Rake = 0.10;		//10% rake default
            m_RakeMax = 5000;	//5k maximum rake default
            m_Game = new PokerGame(this);
        }

        private void Dress()
        {
            AddItem(new FancyShirt(0));

            Item pants = new LongPants();
            pants.Hue = 1;
            AddItem(pants);

            Item shoes = new Shoes();
            shoes.Hue = 1;
            AddItem(shoes);

            Item sash = new BodySash();
            sash.Hue = 1;
            AddItem(sash);

            Utility.AssignRandomHair(this);
        }

        private static JackpotInfo m_JackpotWinners;
        public static JackpotInfo JackpotWinners { get { return m_JackpotWinners; } set { m_JackpotWinners = value; } }

        public static void AwardJackpot()
        {
            if (m_JackpotWinners != null && m_JackpotWinners.Winners != null && m_JackpotWinners.Winners.Count > 0)
            {
                int award = m_Jackpot / m_JackpotWinners.Winners.Count;

                if (award <= 0)
                    return;

                foreach (PokerPlayer m in m_JackpotWinners.Winners)
                {
                    if (m != null && m.Mobile != null && m.Mobile.BankBox != null)
                    {
                        m.Mobile.BankBox.DropItem(new BankCheck(award));
                        World.Broadcast(1161, true, "{0} has won the poker jackpot of {1} gold with {2}", m.Mobile.Name, award.ToString("#,###"), HandRanker.RankString(m_JackpotWinners.Hand));
                    }
                }

                m_Jackpot = 0;
                m_JackpotWinners = null;
            }
        }

        public override void OnDoubleClick(Mobile from)
        {
            if (!m_Active)
                from.SendMessage(0x9A, "This table is inactive");
            else if (!InRange(from.Location, 8))
                from.PrivateOverheadMessage(Server.Network.MessageType.Regular, 0x22, true, "I am too far away to do that", from.NetState);
            else if (m_MinBuyIn == 0 || m_MaxBuyIn == 0)
                from.SendMessage(0x9A, "This table is inactive");
            else if (m_MinBuyIn > m_MaxBuyIn)
                from.SendMessage(0x9A, "This table is inactive");
            else if (m_Seats.Count < m_MaxPlayers)
                from.SendMessage(0x9A, "This table is inactive");
            else if (m_Game.GetIndexFor(from) != -1)
                return; //TODO: Grab more chips from the player's bankbox
            else if (m_Game.Players.Count >= m_MaxPlayers)
            {
                from.SendMessage(0x22, "This table is full");
                base.OnDoubleClick(from);
            }
            else if (m_Game.Players.Count < m_MaxPlayers)
            {
                //TODO: Send player the poker join gump
                from.CloseGump(typeof(PokerJoinGump));
                from.SendGump(new PokerJoinGump(from, m_Game));
            }
        }

        public override void OnDelete()
        {
            List<PokerPlayer> toRemove = new List<PokerPlayer>();

            foreach (PokerPlayer player in m_Game.Players.Players)
                if (player.Mobile != null)
                    toRemove.Add(player);

            for (int i = 0; i < toRemove.Count; ++i)
            {
                toRemove[i].Mobile.SendMessage(0x22, "The poker dealer has been deleted, and you are now being removed from the poker game and being refunded the money that you currently have.");
                m_Game.RemovePlayer(toRemove[i]);
            }

            base.OnDelete();
        }

        public static void PokerKick_OnCommand(CommandEventArgs e)
        {
            Mobile from = e.Mobile;

            if (from == null)
                return;

            foreach (Mobile m in from.GetMobilesInRange(0))
            {
                if (m is PlayerMobile)
                {
                    PlayerMobile pm = (PlayerMobile)m;

                    PokerGame game = pm.PokerGame;

                    if (game != null)
                    {
                        PokerPlayer player = game.GetPlayer(m);

                        if (player != null)
                        {
                            game.RemovePlayer(player);
                            from.SendMessage("They have been removed from the poker table");
                            return;
                        }
                    }
                }
            }

            from.SendMessage("No one found to kick from a poker table. Make sure you are standing on top of them.");
        }

        static void EventSink_Disconnected(DisconnectedEventArgs e)
        {
            Mobile from = e.Mobile;

            if (from == null)
                return;

            if (from is PlayerMobile)
            {
                PlayerMobile pm = (PlayerMobile)from;

                PokerGame game = pm.PokerGame;

                if (game != null)
                {
                    PokerPlayer player = game.GetPlayer(from);

                    if (player != null)
                        game.RemovePlayer(player);
                }
            }
        }

        // tissemand add:
        private class DealerTarget : Target
        {
            public DealerTarget()
                : base(10, false, TargetFlags.None)
            {
            }

            protected override void OnTarget(Mobile from, object target)
            {
                if (!(target is PokerDealer))
                {
                    from.SendMessage("That isn't a poker dealer!");
                    return;
                }

                PokerDealer dealer = (PokerDealer)target;
                if (dealer.AddPokerSeat(from, from.Location))
                {
                    // add a stool just because :3
                    Stool stool = new Stool();
                    stool.Visible = false;
                    stool.Movable = false;
                    stool.MoveToWorld(from.Location, from.Map);

                    from.SendMessage(0x22, "A new seat was successfully created.");
                }
                else
                    from.SendMessage(0x22, "There is no more room at that table for another seat. Try increasing the value of MaxPlayers first.");
            }
        }

        // tissemand edit:
        public static void AddPokerSeat_OnCommand(CommandEventArgs e)
        {
            Mobile from = e.Mobile;

            if (from == null)
                return;

            from.SendMessage(0x22, "Please select the dealer.");

            from.Target = new DealerTarget();

        }

        // tisseemand edit... why was this return type of int? bool has better logic imo
        public bool AddPokerSeat(Mobile from, Point3D seat)
        {
            if (m_Seats.Count >= m_MaxPlayers)
                return false;

            m_Seats.Add(seat);
            return true;
        }

        public bool SeatTaken(Point3D seat)
        {
            for (int i = 0; i < m_Game.Players.Count; ++i)
                if (m_Game.Players[i].Seat == seat)
                    return true;

            return false;
        }

        public int RakeGold(int gold)
        {
            double amount = gold * m_Rake;
            return (int)(amount > m_RakeMax ? m_RakeMax : amount);
        }

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

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

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

            writer.Write(m_Active);
            writer.Write(m_SmallBlind);
            writer.Write(m_BigBlind);
            writer.Write(m_MinBuyIn);
            writer.Write(m_MaxBuyIn);
            writer.Write(m_ExitLocation);
            writer.Write(m_ExitMap);
            writer.Write(m_Rake);
            writer.Write(m_RakeMax);
            writer.Write(m_MaxPlayers);

            writer.Write(m_Seats.Count);

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

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

            int version = reader.ReadInt();

            switch (version)
            {
                case 0:
                    m_Active = reader.ReadBool();
                    m_SmallBlind = reader.ReadInt();
                    m_BigBlind = reader.ReadInt();
                    m_MinBuyIn = reader.ReadInt();
                    m_MaxBuyIn = reader.ReadInt();
                    m_ExitLocation = reader.ReadPoint3D();
                    m_ExitMap = reader.ReadMap();
                    m_Rake = reader.ReadDouble();
                    m_RakeMax = reader.ReadInt();
                    m_MaxPlayers = reader.ReadInt();

                    int count = reader.ReadInt();
                    m_Seats = new List<Point3D>();

                    for (int i = 0; i < count; ++i)
                        m_Seats.Add(reader.ReadPoint3D());

                    break;
            }

            m_Game = new PokerGame(this);
        }

        public class JackpotInfo
        {
            private List<PokerPlayer> m_Winners;
            private ResultEntry m_Hand;
            private DateTime m_Date;

            public List<PokerPlayer> Winners { get { return m_Winners; } }
            public ResultEntry Hand { get { return m_Hand; } }
            public DateTime Date { get { return m_Date; } }

            public JackpotInfo(List<PokerPlayer> winners, ResultEntry hand, DateTime date)
            {
                m_Winners = winners;
                m_Hand = hand;
                m_Date = date;
            }
        }
    }
}
