package dezhoupoker

import (
	"fmt"
	"math/rand"
	"mongo.games.com/algorithm"
)

var cardKindValue = make(map[int32]int32)

/*
散牌分值=散牌里单牌分值最大的分数
一对子分值=对子分值
二对分值=对子1分值+对子2分值+单张分值
三条分值=三张分值+两个单张分值
顺子分值=对应顺子分值
同花分值=对应同花分值
葫芦分值=（三张分值+对子分值）*2
四条分值=四张分值+单张分值
同花顺分值=同花顺分值
皇家同花顺=皇家同花顺分值
*/
func CalCardsKindScore(cardsInfo *CardsInfo) int32 {
	switch cardsInfo.Kind {
	case KindOfCard_RoyalFlush:
		return CalScoreByKindAndCard(KindOfCard_RoyalFlush, cardsInfo.KindCards[4])
	case KindOfCard_StraightFlush:
		return CalScoreByKindAndCard(KindOfCard_StraightFlush, cardsInfo.KindCards[4])
	case KindOfCard_FourKind:
		return CalScoreByKindAndCard(KindOfCard_FourKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4])
	case KindOfCard_Fullhouse:
		return (CalScoreByKindAndCard(KindOfCard_ThreeKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_TwoPair, cardsInfo.KindCards[3])) * 2
	case KindOfCard_Flush:
		return CalScoreByKindAndCard(KindOfCard_Flush, cardsInfo.KindCards[0])
	case KindOfCard_Straight:
		return CalScoreByKindAndCard(KindOfCard_Straight, cardsInfo.KindCards[4])
	case KindOfCard_ThreeKind:
		return CalScoreByKindAndCard(KindOfCard_ThreeKind, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[3]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4])
	case KindOfCard_TwoPair:
		return CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[0]) + CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[2]) + CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[4])
	case KindOfCard_OnePair:
		return CalScoreByKindAndCard(KindOfCard_OnePair, cardsInfo.KindCards[0])
	case KindOfCard_HighCard:
		return CalScoreByKindAndCard(KindOfCard_HighCard, cardsInfo.KindCards[0])
	default:
	}
	return 0
}

func MakeCardKindKey(kind int32, card int32) int32 {
	return kind*10000 + card
}

func SetCardKindValue(key, value int32) {
	cardKindValue[key] = value
}

func CalScoreByKindAndCard(kind int32, card int32) int32 {
	if v, exist := cardKindValue[MakeCardKindKey(kind, card)]; exist {
		return v
	}
	return 0
}

type PlayerCard struct {
	HandCard           []int32
	UserData           interface{}
	WinningProbability int32
	AllCardKind        [KindOfCard_Max]int32
	WinCardKind        [KindOfCard_Max]int32
	winTimes           int32
	CI                 *CardsInfo
}

type GameCtx struct {
	PlayerCards   []*PlayerCard
	CommonCard    []int32
	RestCard      []int32
	CommonCardCnt int
	Possibilities int
}

func (gctx *GameCtx) GetMaxKindOfCard() int32 {
	maxKind := KindOfCard_HighCard
	for _, pc := range gctx.PlayerCards {
		if pc != nil && pc.CI != nil && pc.CI.Kind > maxKind {
			maxKind = pc.CI.Kind
		}
	}
	return maxKind
}

func (gctx *GameCtx) GetMaxCardInfo() *CardsInfo {
	var max *CardsInfo
	for _, pc := range gctx.PlayerCards {
		if pc != nil && pc.CI != nil {
			if max == nil {
				max = pc.CI
			} else if pc.CI.Value > max.Value {
				max = pc.CI
			}
		}
	}
	return max
}

func CalWinningProbability(ctx *GameCtx) {
	if ctx == nil {
		return
	}

	possibilities := uint64(0)
	max := int64(0)
	maxIdx := make([]int, 0, len(ctx.PlayerCards))
	oldCC := len(ctx.CommonCard)
	m := 5 - oldCC
	if m == 0 { //手牌和公牌全部确定
		for k := 0; k < len(ctx.PlayerCards); k++ {
			ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard)
			ctx.PlayerCards[k].CI.CalValue()
			if ctx.PlayerCards[k].CI.Value > max {
				max = ctx.PlayerCards[k].CI.Value
				maxIdx = maxIdx[0:0]
				maxIdx = append(maxIdx, k)
			} else if ctx.PlayerCards[k].CI.Value == max {
				maxIdx = append(maxIdx, k)
			}
			ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++
		}
		if len(maxIdx) != 0 {
			for _, idx := range maxIdx {
				ctx.PlayerCards[idx].winTimes++
				ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++
			}
		}

		possibilities = uint64(len(maxIdx))
	} else { //计算各种组合的可能
		//先填充够5张公牌的位置
		for i := oldCC; i < 5; i++ {
			ctx.CommonCard = append(ctx.CommonCard, -1)
		}

		n := len(ctx.RestCard)
		atable := make([]int32, n)
		for i := 0; i < n; i++ {
			atable[i] = int32(i)
		}

		possibilities = algorithm.CombNumber(uint64(n), uint64(m))
		if possibilities > 10000 {
			possibilities = 10000
		}

		c := algorithm.CombinerSelectUseRecursion(atable, m)
		if len(c) > int(possibilities) {
			for i := 0; i < int(possibilities); i++ {
				r := rand.Intn(len(c))
				//设置公牌
				for j := 0; j < len(c[r]); j++ {
					ctx.CommonCard[oldCC+j] = ctx.RestCard[c[r][j]]
				}

				max = 0
				maxIdx = maxIdx[0:0]
				for k := 0; k < len(ctx.PlayerCards); k++ {
					ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard)
					ctx.PlayerCards[k].CI.CalValue()
					if ctx.PlayerCards[k].CI.Value > max {
						max = ctx.PlayerCards[k].CI.Value
						maxIdx = maxIdx[0:0]
						maxIdx = append(maxIdx, k)
					} else if ctx.PlayerCards[k].CI.Value == max {
						maxIdx = append(maxIdx, k)
					}
					ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++
				}
				if len(maxIdx) != 0 {
					for _, idx := range maxIdx {
						ctx.PlayerCards[idx].winTimes++
						ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++
					}
				}
			}
		} else {
			for i := 0; i < len(c); i++ {
				//设置公牌
				for j := 0; j < len(c[i]); j++ {
					ctx.CommonCard[oldCC+j] = ctx.RestCard[c[i][j]]
				}

				max = 0
				maxIdx = maxIdx[0:0]
				for k := 0; k < len(ctx.PlayerCards); k++ {
					ctx.PlayerCards[k].CI = KindOfCardFigureUpSington.figureUp(ctx.PlayerCards[k].HandCard, ctx.CommonCard)
					ctx.PlayerCards[k].CI.CalValue()
					if ctx.PlayerCards[k].CI.Value > max {
						max = ctx.PlayerCards[k].CI.Value
						maxIdx = maxIdx[0:0]
						maxIdx = append(maxIdx, k)
					} else if ctx.PlayerCards[k].CI.Value == max {
						maxIdx = append(maxIdx, k)
					}
					ctx.PlayerCards[k].AllCardKind[ctx.PlayerCards[k].CI.Kind]++
				}
				if len(maxIdx) != 0 {
					for _, idx := range maxIdx {
						ctx.PlayerCards[idx].winTimes++
						ctx.PlayerCards[idx].WinCardKind[ctx.PlayerCards[idx].CI.Kind]++
					}
				}
			}
		}
	}

	fmt.Printf("Common Card:")
	if oldCC > 0 {
		for i := 0; i < oldCC; i++ {
			fmt.Printf("%s", Card(ctx.CommonCard[i]))
		}
		ci := KindOfCardFigureUpExSington.FigureUpByCard(ctx.CommonCard[:oldCC])
		if ci != nil {
			fmt.Printf(":%s", ci.KindStr())
		}
	}
	fmt.Println()

	ctx.Possibilities = int(possibilities)
	for i := 0; i < len(ctx.PlayerCards); i++ {
		ctx.PlayerCards[i].WinningProbability = int32(int64(ctx.PlayerCards[i].winTimes) * 10000 / int64(possibilities))
		fmt.Printf("UserData<%#v> %s%s WinningProbability=%.2f%% \n", ctx.PlayerCards[i].UserData, Card(ctx.PlayerCards[i].HandCard[0]), Card(ctx.PlayerCards[i].HandCard[1]), float32(ctx.PlayerCards[i].WinningProbability)/100)
	}
}

func PossibleKindOfCards(ctx *GameCtx) []int32 {
	kinds := make([]int32, KindOfCard_Max)
	if ctx == nil {
		return kinds
	}

	if ctx.CommonCardCnt < 5 {
		return kinds
	}

	n := len(ctx.RestCard)
	atable := make([]int32, n)
	for i := 0; i < n; i++ {
		atable[i] = int32(i)
	}

	var handCard [2]int32
	c := algorithm.CombinerSelectUseRecursion(atable, 2)
	for i := 0; i < len(c); i++ {
		//设置公牌
		for j := 0; j < len(c[i]); j++ {
			handCard[j] = ctx.RestCard[c[i][j]]
		}

		ci := KindOfCardFigureUpSington.figureUp(handCard[:], ctx.CommonCard)
		if ci != nil {
			kinds[ci.Kind]++
		}
	}
	return kinds
}

func CaculKindOfCardCount(kinds []int32, expectKind []int32) (int32, int32) {
	var cnt int32
	for _, k := range expectKind {
		cnt += kinds[k]
	}
	var total int32
	for _, v := range kinds {
		total += v
	}
	return total, cnt
}

func CaculGreaterKindOfCardCount(kinds []int32, cmpKind int32) (int32, int32) {
	var cnt int32
	var total int32
	for k, v := range kinds {
		total += v
		if k >= int(cmpKind) {
			cnt++
		}
	}
	return total, cnt
}

func PossibleGreaterKindOfCards(ctx *GameCtx, cmpCI *CardsInfo) (int, int) {
	if ctx == nil {
		return 0, 1
	}

	if ctx.CommonCardCnt < 5 {
		return 0, 1
	}

	if cmpCI == nil {
		return 0, 1
	}

	n := len(ctx.RestCard)
	atable := make([]int32, n)
	for i := 0; i < n; i++ {
		atable[i] = int32(i)
	}

	var cnt int
	var handCard [2]int32
	c := algorithm.CombinerSelectUseRecursion(atable, 2)
	for i := 0; i < len(c); i++ {
		//设置公牌
		for j := 0; j < len(c[i]); j++ {
			handCard[j] = ctx.RestCard[c[i][j]]
		}

		ci := KindOfCardFigureUpSington.figureUp(handCard[:], ctx.CommonCard)
		if ci != nil {
			ci.CalValue()
		}

		if ci.Value > cmpCI.Value {
			cnt++
		}
	}
	return cnt, len(c)
}
