using System.Collections.Generic;
using System.Linq;
using GameFix.Poker;
using GameMessage;
using GameMessage.PaoDeKuaiF;

namespace GameFix.PaoDeKuaiF
{
    /// <summary>
    /// 跑得快牌运算逻辑
    /// </summary>
    public class PdkCardAlgorithm
    {
        /// <summary>
        /// 获取提示的牌
        /// </summary>
        /// <param name="outCard">当前出的牌</param>
        /// <param name="shoupai">玩家在手里的牌，没有出下去的牌</param>
        /// <param name="maxShouPai">当前出牌人的手牌</param>
        /// <param name="aaaIsZhaDan">3A是不是炸弹，默认不是</param>
        /// <param name="isOne">只要一种提示 true是 false不是</param>
        /// <returns></returns>
        public static List<List<TCardInfoPdkF>> GetTipCardByBtn(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai,
            TCardInfoPdkF[] maxShouPai, bool aaaIsZhaDan = false, bool isOne = false)
        {
            if (shoupai == null || shoupai.Length <= 0) return null;
            List<List<TCardInfoPdkF>> result = new List<List<TCardInfoPdkF>>();
            if (outCard.GameNum <= 0)
            {
                //如果是自己先出的话， 就从尾部一张一张提示过去            
                for (int i = shoupai.Length - 1; i >= 0; i--)
                {
                    List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
                    chu.Add(shoupai[i]);
                    result.Add(chu);
                }

                return result;
            }

            switch (outCard.Type)
            {
                case CardType1.DanZhang:
                    var danzhangs = PokerLogic.GetDanZhang(outCard, shoupai);
                    if (danzhangs == null || danzhangs.Count <= 0)
                    {
                        danzhangs = PokerLogic.GetDanZhang(outCard, shoupai, true);
                    }

                    if (danzhangs != null && danzhangs.Count > 0)
                    {
                        danzhangs.Sort((x, y) => x.GameNum.CompareTo(y.GameNum));
                        foreach (var item in danzhangs)
                        {
                            List<TCardInfoPdkF> chu = new List<TCardInfoPdkF> { item };
                            result.Add(chu);
                        }
                    }

                    break;
                case CardType1.DuiZi:
                    result = PokerLogic.GetCountCard(outCard, shoupai, 2);
                    if (result == null || result.Count <= 0)
                    {
                        result = PokerLogic.GetCountCard(outCard, shoupai, 2, true);
                    }

                    if (result != null)
                    {
                        result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
                    }

                    break;
                case CardType1.SanDai2:
                    result = PokerLogic.GetCountCard(outCard, shoupai, 3, true);
                    if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
                    break;
                case CardType1.ShunZi:
                    result = PokerLogic.GetShunZi(outCard, shoupai);
                    if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
                    break;
                case CardType1.LianDui:
                    result = PokerLogic.GetLianCount(outCard, shoupai, 2, null);
                    if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
                    break;
                case CardType1.FeiJi:
                    result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai);
                    if (result != null) result.Sort((x, y) => x[0].GameNum.CompareTo(y[0].GameNum));
                    break;
                case CardType1.ZhaDan:
                    PokerLogic.GetPlayZhaDan(shoupai, out var cards);
                    if (cards.Count > 4)
                    {
                        int c = cards.Count / 4;
                        for (int i = 0; i < c; i++)
                        {
                            var tx = cards.Skip(i * 4).Take(4).ToList();
                            if (tx != null && tx[0].GameNum > outCard.GameNum)
                            {
                                result.Add(tx);
                            }
                        }
                    }
                    else
                    {
                        if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum)
                        {
                            result.Add(cards);
                        }
                    }

                    break;
            }

            if (isOne && result != null && result.Count > 0)
            {
                //只要一种提示
            }
            else
            {
                if (result == null) result = new List<List<TCardInfoPdkF>>();
                if (aaaIsZhaDan)
                {
                    var aaa = GetAAAZhaDan(shoupai);
                    if (aaa != null && aaa.Count >= 3)
                    {
                        result.Add(aaa);
                    }
                }

                if (outCard.Type != CardType1.ZhaDan)
                {
                    PokerLogic.GetPlayZhaDan(shoupai, out var cards);
                    if (cards != null && cards.Count > 0)
                    {
                        int c = cards.Count / 4;
                        if (c <= 0)
                        {
                            result.Add(cards);
                        }
                        else
                        {
                            for (int i = 0; i < c; i++)
                            {
                                result.Add(cards.Skip(i * 4).Take(4).ToList());
                            }
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 获取提示的牌
        /// </summary>
        /// <param name="outCard">当前出的牌</param>
        /// <param name="shoupai">玩家在手里的牌，没有出下去的牌</param>
        /// <param name="maxShouPai">当前出牌人的手牌</param>
        /// <param name="aaaIsZhaDan">3A是不是炸弹，默认不是</param>
        /// <param name="isOne">只要一种提示 true是 false不是</param>
        /// <returns></returns>
        public static List<List<TCardInfoPdkF>> GetTipCard(PlayOutCardPdkF outCard, TCardInfoPdkF[] shoupai, TCardInfoPdkF[] maxShouPai,
            bool aaaIsZhaDan = false, bool isOne = false)
        {
            if (shoupai == null || shoupai.Length <= 0) return null;
            List<List<TCardInfoPdkF>> result = new List<List<TCardInfoPdkF>>();
            if (outCard.GameNum <= 0)
            {
                //如果是自己先出的话， 就从尾部一张一张提示过去            
                for (int i = shoupai.Length - 1; i >= 0; i--)
                {
                    List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
                    chu.Add(shoupai[i]);
                    result.Add(chu);
                }

                return result;
            }

            switch (outCard.Type)
            {
                //飞机带翅膀、炸弹，四带三、四带二
                case CardType1.DanZhang:
                    var danzhangs = shoupai.Where(x => x.GameNum > outCard.GameNum);
                    if (danzhangs.Count() > 0)
                    {
                        foreach (var item in danzhangs)
                        {
                            List<TCardInfoPdkF> chu = new List<TCardInfoPdkF>();
                            chu.Add(item);
                            result.Add(chu);
                        }
                    }

                    break;
                case CardType1.DuiZi:
                    result = PokerLogic.GetCountCard(outCard, shoupai, 2, true);
                    break;
                case CardType1.SanDai2:
                    result = PokerLogic.GetCountCard(outCard, shoupai, 3, true);
                    break;
                case CardType1.ShunZi:
                    result = PokerLogic.GetShunZi(outCard, shoupai);
                    break;
                case CardType1.LianDui:
                    result = PokerLogic.GetLianCount(outCard, shoupai, 2, null);
                    break;
                case CardType1.FeiJi:
                    result = PokerLogic.GetLianCount(outCard, shoupai, 3, maxShouPai);
                    break;
                case CardType1.ZhaDan:
                    PokerLogic.GetPlayZhaDan(shoupai, out var cards);
                    if (cards != null && cards.Count > 0 && cards[0].GameNum > outCard.GameNum)
                    {
                        result.Add(cards);
                    }

                    break;
            }

            if (result == null) result = new List<List<TCardInfoPdkF>>();
            if (isOne && result != null && result.Count > 0)
            {
                //只要一种提示
            }
            else
            {
                if (aaaIsZhaDan)
                {
                    var aaa = GetAAAZhaDan(shoupai);
                    if (aaa != null && aaa.Count >= 3)
                    {
                        result.Add(aaa);
                    }
                }

                if (outCard.Type != CardType1.ZhaDan)
                {
                    PokerLogic.GetPlayZhaDan(shoupai, out var cards);
                    if (cards != null && cards.Count > 0)
                    {
                        result.Add(cards);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 判断是否是炸弹
        /// </summary>
        /// <param name="cards"></param>
        /// <param name="aaaIsZhaDan"></param>
        /// <param name="isCheckPaiCount"></param>
        /// <returns></returns>
        public static bool IsZhaDan(TCardInfoPdkF[] cards, bool aaaIsZhaDan, bool isCheckPaiCount = true)
        {
            if (cards == null || cards.Length < 3 || cards.Length > 4) return false;
            if (aaaIsZhaDan && cards.Length == 3)
            {
                return Is3A(cards);
            }

            if (cards.Length == 4)
            {
                return IsZhaDan(cards, isCheckPaiCount);
            }

            return false;
        }

        /// <summary>
        /// 检查玩家是否有比出的牌更大的牌
        /// </summary>
        /// <param name="outCard">玩家出的牌</param>
        /// <param name="shouPai">自己的手牌-在手里的牌，并且是排序过的。</param>
        /// <param name="maxShouPai">出牌人的手牌</param>
        /// <param name="aaaIsZhaDan">3A是不是炸弹，默认不是</param>
        /// <returns>true有比玩家大的牌 false没有</returns>
        public static bool CheckHaveBigger(PlayOutCardPdkF outCard, TCardInfoPdkF[] shouPai, TCardInfoPdkF[] maxShouPai,
            bool aaaIsZhaDan = false)
        {
            if (shouPai == null || shouPai.Length <= 0) return false;
            List<List<TCardInfoPdkF>> cs = null;
            List<TCardInfoPdkF> cards = null;
            switch (outCard.Type)
            {
                case CardType1.DanZhang:
                    if (shouPai.Where(x => x.GameNum > outCard.GameNum).Count() > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.DuiZi:
                    cs = PokerLogic.GetCountCard(outCard, shouPai, 2, true);
                    if (cs != null && cs.Count > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.SanDai2:
                    cs = PokerLogic.GetCountCard(outCard, shouPai, 3, true);
                    if (cs != null && cs.Count > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.ShunZi:
                    cs = PokerLogic.GetShunZi(outCard, shouPai);
                    if (cs != null && cs.Count > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.LianDui:
                    cs = PokerLogic.GetLianCount(outCard, shouPai, 2, null);
                    if (cs != null && cs.Count > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.FeiJi:
                    cs = PokerLogic.GetLianCount(outCard, shouPai, 3, maxShouPai);
                    if (cs != null && cs.Count > 0)
                    {
                        return true;
                    }

                    break;
                case CardType1.ZhaDan:
                    var b = PokerLogic.GetPlayZhaDan(shouPai, out cards);
                    if (!b) return false;
                    foreach (var item in cards)
                    {
                        if (item.GameNum > outCard.GameNum) return true;
                    }

                    break;
            }

            if (aaaIsZhaDan)
            {
                var aaa = GetAAAZhaDan(shouPai);
                if (aaa != null && aaa.Count >= 3)
                {
                    return true;
                }
            }

            if (outCard.Type != CardType1.ZhaDan)
            {
                return PokerLogic.GetPlayZhaDan(shouPai, out cards);
            }

            return false;
        }

        /// <summary>
        /// 通过选中的牌返回出下去牌的类型 否则选中的牌就不能出下去
        /// 一般客户端用
        /// </summary>
        /// <param name="selectCards">选中的牌</param>
        /// <param name="shouPai">玩家手牌,不包含已经出下去的牌</param>
        /// <param name="wangfa">规则玩法</param>
        /// <param name="paycard">返回出牌内容</param>
        /// <returns>true可以出牌 false不能出牌</returns>
        public static bool GetOutCard(TCardInfoPdkF[] selectCards, TCardInfoPdkF[] shouPai, PdkRule wangfa,
            out PlayOutCardPdkF paycard)
        {
            paycard = new PlayOutCardPdkF();
            if (selectCards == null || selectCards.Length <= 0 || shouPai == null || shouPai.Length <= 0) return false;
            var chaCard = shouPai.Except(selectCards); //获取差集，如果个数大于0说明牌的张数是够的，如果是0，说明牌不够
            PokerLogic.SortCardByGameNum(ref selectCards);
            var bugou = chaCard.Count() <= 0;
            byte gameNum = 0;
            if (selectCards.Length == 1)
            {
                //单张可以出
                paycard.Ids = new byte[] { selectCards[0].ID };
                paycard.GameNum = selectCards[0].GameNum;
                paycard.Type = CardType1.DanZhang;
                return true;
            }
            else if (selectCards.Length == 2)
            {
                //对子
                if (PokerLogic.IsDuiZi(selectCards))
                {
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    paycard.GameNum = selectCards[0].GameNum;
                    paycard.Type = CardType1.DuiZi;
                    return true;
                }
            }
            else if (selectCards.Length == 3)
            {
                if (wangfa.AAAIsZhaDan && Is3A(selectCards)) //3A是炸弹
                {
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    paycard.GameNum = selectCards[0].GameNum;
                    paycard.Type = CardType1.ZhaDan;
                    return true;
                }
                else
                {
                    bool xt = selectCards[0].GameNum == selectCards[1].GameNum &&
                              selectCards[2].GameNum == selectCards[1].GameNum;
                    if (xt && bugou)
                    {
                        paycard.GameNum = selectCards[0].GameNum;
                        paycard.Type = CardType1.SanDai2;
                        paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                        return true;
                    }
                }
            }
            else
            {
                if (IsZhaDan(selectCards)) //炸弹
                {
                    paycard.GameNum = selectCards[0].GameNum; //因为该值会根据大小排序
                    paycard.Type = CardType1.ZhaDan;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (wangfa.SiDai2 && IsSiDai2(selectCards, !bugou, out gameNum))
                {
                    //四带二
                    paycard.GameNum = gameNum;
                    paycard.Type = CardType1.SiDai2;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (wangfa.SiDai3 && IsSiDai3(selectCards, !bugou, out gameNum))
                {
                    //四带二
                    paycard.GameNum = gameNum;
                    paycard.Type = CardType1.SiDai3;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (PokerLogic.IsShunzi(selectCards))
                {
                    //顺子
                    paycard.GameNum = selectCards[0].GameNum;
                    paycard.Type = CardType1.ShunZi;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (PokerLogic.IsLianDui(selectCards)) //连对
                {
                    paycard.GameNum = selectCards[0].GameNum;
                    paycard.Type = CardType1.LianDui;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (PokerLogic.IsSanDaiEr(selectCards, bugou, out gameNum)) //三带二、
                {
                    paycard.GameNum = gameNum;
                    paycard.Type = CardType1.SanDai2;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }

                if (PokerLogic.IsFeiJi(selectCards, bugou, true, out gameNum)) //飞机带翅膀
                {
                    paycard.GameNum = gameNum;
                    paycard.Type = CardType1.FeiJi;
                    paycard.Ids = selectCards.Select(x => x.ID).ToArray();
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// 是否是3张A
        /// </summary>
        /// <param name="cards"></param>
        /// <returns>true是 false不是</returns>
        static bool Is3A(TCardInfoPdkF[] cards)
        {
            if (cards == null || cards.Length != 3) return false;
            return cards[0].GameNum == 14 && cards[1].GameNum == 14 && cards[2].GameNum == 14;
        }

        /// <summary>
        /// 获取3A炸弹
        /// </summary>
        /// <param name="cards">玩家手里没有打下去的牌</param>
        /// <returns></returns>
        static List<TCardInfoPdkF> GetAAAZhaDan(TCardInfoPdkF[] cards)
        {
            if (cards == null || cards.Length < 3) return null;
            List<TCardInfoPdkF> result = new List<TCardInfoPdkF>();
            for (int i = 0; i < cards.Length; i++)
            {
                if (cards[i].GameNum == 14)
                {
                    result.Add(cards[i]);
                }
            }

            if (result.Count >= 3)
            {
                return result;
            }

            return null;
        }

        static bool IsSiDai3(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum)
        {
            gameNum = 0;
            if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 7) return false;
            if (isPaiBuGou && selectCards.Length != 7) return false;
            var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
            if (!b) return b;
            gameNum = cards[0].GameNum;
            return true;
        }

        /// <summary>
        /// 是否是四带二
        /// </summary>
        /// <param name="selectCards"></param>
        /// <param name="isPaiBuGou">true牌够，false牌不够</param>
        /// <returns>true是 false不是</returns>
#pragma warning disable CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
        static bool IsSiDai2(TCardInfoPdkF[] selectCards, bool isPaiBuGou, out byte gameNum)
#pragma warning restore CS1573 // 参数在 XML 注释中没有匹配的 param 标记(但其他参数有)
        {
            gameNum = 0;
            if (selectCards == null || selectCards.Length < 5 || selectCards.Length > 6) return false;
            if (isPaiBuGou && selectCards.Length != 6) return false;
            var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
            if (!b) return b;
            gameNum = cards[0].GameNum;
            return true;
        }

        /// <summary>
        /// 判断是否是炸弹
        /// </summary>
        /// <param name="selectCards"></param>
        /// <param name="isCheckPaiCount">是否要校验牌的张数对不对</param>
        /// <returns></returns>
        static bool IsZhaDan(TCardInfoPdkF[] selectCards, bool isCheckPaiCount = true)
        {
            if (selectCards == null || selectCards.Length < 4) return false;
            int gameNum = 0;
            foreach (var item in selectCards)
            {
                if (gameNum == 0)
                {
                    gameNum = item.GameNum;
                    continue;
                }

                if (gameNum != item.GameNum)
                {
                    //炸弹的牌不是一样的牌
                    return false;
                }
            }

            var b = PokerLogic.GetPlayZhaDan(selectCards, out var cards);
            if (!b) return b;
            if (isCheckPaiCount)
            {
                //校验返回的炸弹牌数量和传入的牌数量是不是一样多
                return selectCards.Length == cards.Count;
            }

            return true;
        }

        /// <summary>
        /// 判断是否有炸弹
        /// </summary>
        /// <param name="selectCards"></param>
        /// <param name="aaaIsBomb">AAA是否是炸弹 true是 false不是</param>
        /// <returns></returns>
        public static bool IsHaveBomb(TCardInfoPdkF[] selectCards, bool aaaIsBomb)
        {
            if (selectCards == null || selectCards.Length < 3) return false;
            if (!aaaIsBomb && selectCards.Length < 4) return false;
            Dictionary<int, int> dic = new Dictionary<int, int>();
            for (int i = 0; i < selectCards.Length; i++)
            {
                if (dic.ContainsKey(selectCards[i].GameNum))
                {
                    dic[selectCards[i].GameNum]++;
                }
                else
                {
                    dic[selectCards[i].GameNum] = 1;
                }
            }

            foreach (var item in dic)
            {
                if (aaaIsBomb && item.Key == 14 && item.Value == 3) return true;
                if (item.Value >= 4) return true;
            }

            return false;
        }
    }
}