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

namespace GameFix.Poker
{
    public static class PokerLogic
    {
        public class XY
        {
            public int x; //牌面大小
            public int y; //次数
        }

        /// <summary>
        /// 获取所有的单张
        /// </summary>
        /// <returns></returns>
        public static List<T> GetDanZhang<T>(IPlayOutCardType1 outCard, T[] cards, bool chaiDan = false)
            where T : ICardInfoType1
        {
            if (outCard.GameNum <= 0 || cards == null || cards.Length <= 0) return null;
            Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
            List<T> result = new List<T>();
            foreach (var item in cards)
            {
                if (item.GameNum <= outCard.GameNum) continue;
                if (dic.ContainsKey(item.GameNum))
                {
                    if (dic[item.GameNum] == 1)
                    {
                        dic[item.GameNum]++;
                        if (!chaiDan || (chaiDan && dic[item.GameNum] >= 4))
                            result.RemoveAll(x => x.GameNum == item.GameNum);
                    }
                }
                else
                {
                    dic[item.GameNum] = 1;
                    result.Add(item);
                }
            }

            return result;
        }

        /// <summary>
        /// 获取连续的相同大小的张数
        /// </summary>
        /// <param name="outCard"></param>
        /// <param name="cards">手里没有打下去的牌</param>
        /// <param name="count">几张大小相同的个数</param>
        /// <param name="shouPai">手里全部的牌</param>
        /// <returns></returns>
        public static List<List<T>> GetLianCount<T>(IPlayOutCardType1 outCard, T[] cards, int count,
            T[] shouPai) where T : ICardInfoType1
        {
            if (outCard.GameNum <= 0 || cards == null || cards.Length <= 3) return null;
            int lian = 0; //连了几下
            if (outCard.Type == CardType1.LianDui)
            {
                lian = outCard.Ids.Length / 2; //是几连对
            }

            if (outCard.Type == CardType1.FeiJi)
            {
                lian = GetFeiJiCount(GetCardByIds(shouPai, outCard.Ids)); //是几连飞机
            }

            List<List<T>> result = new List<List<T>>();
            var countCard = GetCountCard(outCard, cards, count, true, true, lian - 2);
            if (countCard.Count < lian) return result;
            for (int i = 0; i < countCard.Count - 1; i++)
            {
                List<T> temp = new List<T>();
                int c = 1;
                int num = countCard[i][0].GameNum;
                temp.AddRange(countCard[i]);
                for (int j = i + 1; j < countCard.Count; j++)
                {
                    if (countCard[j][0].GameNum + 1 == num)
                    {
                        temp.AddRange(countCard[j]);
                        c++;
                        num = countCard[j][0].GameNum;
                    }

                    if (c == lian) break;
                }

                if (c == lian)
                {
                    result.Add(temp);
                }
            }

            return result;
        }

        /// <summary>
        /// 根据牌的绝对Id返回牌的详细信息
        /// </summary>
        /// <param name="wanshouPai">玩家手牌</param>
        /// <param name="ids">牌的Id集合</param>
        /// <returns></returns>
        public static T[] GetCardByIds<T>(T[] wanshouPai, byte[] ids) where T : ICardInfoType1
        {
            if (ids == null || ids.Length <= 0) return null;
            if (wanshouPai == null || wanshouPai.Length <= 0) return null;
            List<T> result = new List<T>();
            foreach (var id in ids)
            {
                foreach (var item in wanshouPai)
                {
                    if (item.ID == id)
                    {
                        result.Add(item);
                        break;
                    }
                }
            }

            if (ids.Length != result.Count)
            {
                throw new Exception($"获取玩家的牌张数不对 idslen:{ids.Length} result len:{result.Count} ");
            }

            return result.ToArray();
        }

        /// <summary>
        /// 获取飞机的连的个数，长度
        /// </summary>
        /// <param name="selectCards"></param>
        /// <returns></returns>
        public static int GetFeiJiCount<T>(T[] selectCards) where T : ICardInfoType1
        {
            if (selectCards == null || selectCards.Length < 6) return 0;
            List<XY> list = new List<XY>();
            foreach (var item in selectCards)
            {
                var t = list.Find(x => x.x == item.GameNum);
                if (t != null)
                {
                    t.y++;
                }
                else
                {
                    list.Add(new XY { x = item.GameNum, y = 1 });
                }
            }

            int num = list.Count(x => x.y >= 3);
            if (num < 2) return 0;
            list = list.Where(x => x.y >= 3).ToList();
            List<int> fj = new List<int>();
            for (int i = 0; i < list.Count - 1; i++)
            {
                if (list[i].x - 1 == list[i + 1].x)
                {
                    if (!fj.Contains(list[i].x))
                    {
                        fj.Add(list[i].x);
                    }

                    if (!fj.Contains(list[i + 1].x))
                    {
                        fj.Add(list[i + 1].x);
                    }
                }
            }

            return fj.Count;
        }

        /// <summary>
        /// 获取玩家手里几张相同的牌型
        /// </summary>
        /// <param name="outCard">玩家出的牌</param>
        /// <param name="cards">玩家手里没有出下去的牌</param>
        /// <param name="count">要获取相同大小的张数</param>
        /// <param name="chai">是否拆牌 false不拆 true拆</param>
        /// <param name="deng">是否获取跟出牌大小相等的牌 true是 false不是</param>
        /// <returns></returns>
        public static List<List<T>> GetCountCard<T>(IPlayOutCardType1 outCard, T[] cards, int count,
            bool chai = false, bool deng = false, int cha = 0) where T : ICardInfoType1
        {
            if (count <= 0 || outCard.GameNum <= 0 || cards == null || cards.Length < count) return null;
            List<List<T>> result = new List<List<T>>();
            Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
            foreach (var item in cards)
            {
                if (deng)
                {
                    if (item.GameNum < outCard.GameNum - cha) continue;
                }
                else
                {
                    if (item.GameNum <= outCard.GameNum - cha) continue;
                }

                if (dic.ContainsKey(item.GameNum))
                {
                    dic[item.GameNum]++;
                }
                else
                {
                    dic[item.GameNum] = 1;
                }
            }

            foreach (var item in dic)
            {
                if (item.Value >= 4 && count < 4 && chai) continue; //炸弹就不拆
                if (!chai)
                {
                    if (item.Value != count) continue;
                    result.Add(cards.Where(x => x.GameNum == item.Key).ToList());
                }
                else
                {
                    if (item.Value < count) continue;
                    result.Add(cards.Where(x => x.GameNum == item.Key).Take(count).ToList());
                }
            }

            return result;
        }

        /// <summary>
        /// 是否是对子
        /// 跑得快朋友房使用了
        /// </summary>
        /// <param name="selectCards"></param>
        /// <returns></returns>
        public static bool IsDuiZi<T>(T[] selectCards) where T : ICardInfoType1
        {
            if (selectCards == null || selectCards.Length != 2) return false;
            return selectCards[0].GameNum == selectCards[1].GameNum;
        }

        /// <summary>
        /// 获取顺子
        /// </summary>
        /// <param name="playCard"></param>
        /// <returns></returns>
        public static List<List<T>> GetShunZi<T>(T[] playCard) where T : ICardInfoType1
        {
            if (playCard == null || playCard.Length < 5) return null;
            var cards = playCard.ToList();
            List<List<T>> result = new List<List<T>>();
            for (int i = 14; i > 6; i--)
            {
                List<T> shunzi = new List<T>();
                for (int j = 0; j < playCard.Length; j++)
                {
                    T temp = cards.FirstOrDefault(x => x.GameNum == (i - j));
                    if (temp.ID > 0)
                    {
                        shunzi.Add(temp);
                    }
                    else
                    {
                        break;
                    }
                }

                if (shunzi.Count >= 5)
                {
                    result.Add(shunzi);
                }
            }

            return result;
        }

        /// <summary>
        /// 获取顺子
        /// </summary>
        /// <param name="outCard"></param>
        /// <param name="playCard"></param>
        /// <returns></returns>
        public static List<List<T>> GetShunZi<T>(IPlayOutCardType1 outCard, T[] playCard) where T : ICardInfoType1
        {
            if (outCard.GameNum <= 0 || outCard.Type != CardType1.ShunZi) return null;
            if (playCard == null || playCard.Length < outCard.Ids.Length) return null;
            var cards = playCard.ToList();
            List<List<T>> result = new List<List<T>>();
            for (int i = 14; i > outCard.GameNum; i--)
            {
                List<T> shunzi = new List<T>();
                for (int j = 0; j < outCard.Ids.Length; j++)
                {
                    T temp = cards.FirstOrDefault(x => x.GameNum == (i - j));
                    if (temp.ID > 0)
                    {
                        shunzi.Add(temp);
                    }
                    else
                    {
                        break;
                    }
                }

                if (shunzi.Count == outCard.Ids.Length)
                {
                    result.Add(shunzi);
                }
            }

            return result;
        }

        /// <summary>
        /// 是否是飞机 跑得快朋友房使用了
        /// </summary>
        /// <param name="selectCards">选中的牌</param>
        /// <param name="isPaiBuGou">true不够 false 够</param>
        /// <param name="canDaiPia">true可以带牌 false不可以带牌</param>
        /// <param name="gameNum">返回飞机面值</param>
        /// <returns></returns>
        public static bool IsFeiJi<T>(T[] selectCards, bool isPaiBuGou, bool canDaiPia, out byte gameNum)
            where T : ICardInfoType1
        {
            gameNum = 0;
            if (selectCards == null || selectCards.Length < 6) return false;
            List<XY> list = new List<XY>();
            foreach (var item in selectCards)
            {
                var t = list.Find(x => x.x == item.GameNum);
                if (t != null)
                {
                    t.y++;
                    if (t.y >= 3)
                    {
                        if (gameNum == 0)
                            gameNum = item.GameNum;
                    }
                }
                else
                {
                    list.Add(new XY { x = item.GameNum, y = 1 });
                }
            }

            int num = list.Count(x => x.y >= 3);
            if (num < 2)
                return false; //if (canDaiPia && isPaiBuGou && selectCards.Length < num * 3) return false; //功能相同
            list = list.Where(x => x.y >= 3).ToList();
            List<int> fj = new List<int>();
            for (int i = 0; i < list.Count - 1; i++)
            {
                if (list[i].x - 1 == list[i + 1].x)
                {
                    if (!fj.Contains(list[i].x))
                    {
                        fj.Add(list[i].x);
                    }

                    if (!fj.Contains(list[i + 1].x))
                    {
                        fj.Add(list[i + 1].x);
                    }
                }
            }

            num = fj.Count;
            if (num < 2) return false;
            gameNum = (byte)fj[0];
            if (!canDaiPia && num * 3 != selectCards.Length) return false; //如果不能带牌
            if (selectCards.Length > num * 3 + num * 2) return false; //带多了
            if (canDaiPia && !isPaiBuGou && num * 3 + num * 2 != selectCards.Length) return false;
            num--;
            for (int i = 0; i < fj.Count; i++)
            {
                if (num == 1)
                {
                    //2连的飞机
                    return fj[i] - num == fj[i + num];
                }
                else if (num == 2)
                {
                    //3连的飞机
                    return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1;
                }
                else if (num == 3)
                {
                    //4连的飞机
                    return fj[i] == fj[i + 1] + 1 && fj[i + 1] == fj[i + 2] + 1 && fj[i + 2] == fj[i + 3] + 1;
                }
                else
                {
                    if (fj[i] - num != fj[i + num])
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 判断是否是顺子，使用该函数前请先排序 跑得快朋友房使用了
        /// </summary>
        /// <param name="selectCards"></param>
        /// <returns></returns>
        public static bool IsShunzi<T>(T[] selectCards) where T : ICardInfoType1
        {
            if (selectCards == null || selectCards.Length < 5) return false;
            T temp = selectCards[0];
            for (int i = 1; i < selectCards.Length; i++)
            {
                if (selectCards[i].GameNum != temp.GameNum - 1) return false; //不是连续的
                temp = selectCards[i];
            }

            return true;
        }

        /// <summary>
        /// 给牌排序 跑得快朋友房使用了
        /// </summary>
        /// <param name="cards"></param>
        public static void SortCardByGameNum<T>(ref T[] cards) where T : ICardInfoType1
        {
            if (cards == null || cards.Length < 2) return;
            Array.Sort(cards, (x, y) => y.GameNum.CompareTo(x.GameNum));
        }
        
        /// <summary>
        /// 是否是连对，，使用该函数前请先排序 跑得快朋友房使用了
        /// </summary>
        /// <param name="selectCards"></param>
        /// <returns></returns>
        public static bool IsLianDui<T>(T[] selectCards) where T : ICardInfoType1
        {
            if (selectCards == null || selectCards.Length < 4 || selectCards.Length % 2 != 0) return false;
            T temp = selectCards[0];
            for (int i = 0; i < selectCards.Length; i += 2)
            {
                if (selectCards[i].GameNum != selectCards[i + 1].GameNum) return false; //不是一对的牌
                if (i + 2 <= selectCards.Length - 1)
                {
                    if (temp.GameNum != selectCards[i + 2].GameNum + 1) return false; //不是顺子
                    temp = selectCards[i + 2];
                }
            }

            return true;
        }
        
        /// <summary>
        /// 获取玩家炸弹，没有炸弹则out就是废王的牌
        /// 需要先排序才可以运算
        /// </summary>
        /// <param name="playCards">玩家手牌</param>
        /// <param name="cards">炸弹的牌 或者 废王的牌</param>
        /// <returns>true有炸弹  false没有炸弹</returns>
        public static bool GetPlayZhaDan<T>(T[] playCards, out List<T> cards) where T : ICardInfoType1
        {
            cards = new List<T>();
            if (playCards == null || playCards.Length <= 0) return false;
            List<T> result = new List<T>();
            List<T> temp = new List<T>();
            List<T> wang = new List<T>();
            byte count = 0;
            byte num = 0;
            foreach (var item in playCards)
            {
                if (item.GameNum == 98 || item.GameNum == 100)
                {
                    wang.Add(item);
                    continue;
                }

                if (num == 0 && count == 0)
                {
                    num = item.GameNum;
                    count++;
                    temp.Add(item);
                }
                else
                {
                    if (num == item.GameNum)
                    {
                        count++;
                        temp.Add(item);
                    }
                    else
                    {
                        num = item.GameNum;
                        count = 1;
                        if (temp.Count >= 4)
                        {
                            result.AddRange(temp);
                        }

                        temp.Clear();
                        temp.Add(item);
                    }
                }
            }

            if (temp.Count >= 4) //防止最后一张牌遗漏
            {
                result.AddRange(temp);
            }

            temp.Clear();
            if (result.Count >= 4)
            {
                result.AddRange(wang);
                cards.AddRange(result);
                return true;
            }

            cards = wang;
            if (wang.Count >= 4)
            {
                return true;
            }

            return false;
        }
        
        /// <summary>
        /// 获取玩家的炸弹
        /// </summary>
        /// <param name="playCards">玩家手牌</param>
        /// <param name="cards">炸弹的牌</param>
        /// <returns>true有炸弹  false没有炸弹</returns>
        public static bool GetPlayZhaDans<T>(T[] playCards, out List<List<T>> cards) where T : ICardInfoType1
        {
            cards = new List<List<T>>();
            if (playCards == null || playCards.Length <= 0) return false;

            List<T> temp = new List<T>();
            byte count = 0;
            byte num = 0;
            foreach (var item in playCards)
            {

                if (num == 0 && count == 0)
                {
                    num = item.GameNum;
                    count++;
                    temp.Add(item);
                }
                else
                {
                    if (num == item.GameNum)
                    {
                        count++;
                        temp.Add(item);
                    }
                    else
                    {
                        num = item.GameNum;
                        count = 1;
                        if (temp.Count >= 4)
                        {
                            cards.Add(temp.ToList());
                        }
                        temp.Clear();
                        temp.Add(item);
                    }
                }
            }
            if (temp.Count >= 4) //防止最后一张牌遗漏
            {
                cards.Add(temp.ToList());
            }
            return cards.Count > 0;
        }
        
        /// <summary>
        /// 是否是三带二 跑得快朋友房使用了
        /// </summary>
        /// <param name="selectCards">要判断的牌</param>
        /// <param name="isPaiBuGou">玩家手牌是不是不够张数带牌true不够 false够</param>
        /// <returns></returns>
#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
        public static bool IsSanDaiEr<T>(T[] selectCards, bool isPaiBuGou, out byte gameNum) where T : ICardInfoType1
#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
        {
            gameNum = 0;
            if (selectCards == null || selectCards.Length < 4 || selectCards.Length > 5) return false;
            if (!isPaiBuGou && selectCards.Length != 5) return false;
            Dictionary<byte, byte> dic = new Dictionary<byte, byte>();
            foreach (var item in selectCards)
            {
                if (dic.ContainsKey(item.GameNum))
                {
                    dic[item.GameNum]++;
                }
                else
                {
                    dic[item.GameNum] = 1;
                }
            }

            if (dic.Count > 3 || dic.Count < 2) return false;
            var b = false;
            foreach (var item in dic)
            {
                if (item.Value >= 3 && item.Key < 90) //大王不能当成3个一起出
                {
                    gameNum = item.Key;
                    b = true;
                    break;
                }
            }

            return b;
        }
        
        /// <summary>
        /// 检测是否有相同的ID，因为牌的ID是唯一的，不会相同
        /// </summary>
        /// <param name="ids"></param>
        /// <returns>true有 false没有</returns>
        public static bool CheckSameId(byte[] ids)
        {
            if (ids == null || ids.Length <= 1) return false;
            Dictionary<byte, int> did = new Dictionary<byte, int>();
            for (int i = 0; i < ids.Length; i++)
            {
                if (did.ContainsKey(ids[i]))
                {
                    did[ids[i]]++;
                }
                else
                {
                    did[ids[i]] = 1;
                }
            }
            foreach (var id in did)
            {
                if (id.Value > 1)
                {
                    return true;
                }
            }
            return false;
        }
        
        /// <summary>
        /// 给牌排序，按照个数从大到小
        /// </summary>
        /// <param name="cards"></param>
        /// <returns></returns>
        public static T[] SortCardByCount<T>(T[] cards) where T : ICardInfoType1
        {
            if (cards == null || cards.Length <= 0) return null;
            List<T> result = new List<T>();
            Dictionary<int, List<T>> dics = new Dictionary<int, List<T>>();
            for (int i = 0; i < cards.Length; i++)
            {
                if (cards[i].GameNum > 20)
                {
                    result.Add(cards[i]);
                    continue;
                }

                if (dics.ContainsKey(cards[i].GameNum))
                {
                    dics[cards[i].GameNum].Add(cards[i]);
                }
                else
                {
                    var temp = new List<T>();
                    temp.Add(cards[i]);
                    dics.Add(cards[i].GameNum, temp);
                }
            }

            var newdics = dics.OrderByDescending(x => x.Value.Count);
            foreach (var item in newdics)
            {
                result.AddRange(item.Value);
            }

            return result.ToArray();
        }
        
        public static string GetGameNumToString(byte gameNum)
        {
            string result = "";
            if (gameNum <= 0) return result;
            if (gameNum >= 3 && gameNum <= 10)
            {
                result = gameNum.ToString();
            }
            else
            {
                switch (gameNum)
                {
                    case 11:
                        result = "J";
                        break;
                    case 12:
                        result = "Q";
                        break;
                    case 13:
                        result = "K";
                        break;
                    case 14:
                        result = "A";
                        break;
                    case 16:
                        result = "2";
                        break;
                    //叫牌中不会有大小王
                }
            }

            return result;
        }
    }
}