package samloc

import (
	"errors"
	"sort"
)

type line struct {
	min int //最小牌
	num int // 主牌 333
	len int // 主牌数量 333444 -->2
	tks []*line
}

func (l *line) incr(m map[int]int) {
	for i := l.min; i < l.min+l.len; i++ {
		m[i] += l.num
	}
}

func (l *line) decr(m map[int]int) {
	for i := l.min; i < l.min+l.len; i++ {
		m[i] -= l.num
	}
}

func (l *line) deepEqual(r *line) bool {
	// 地址相等必相同
	if l == r {
		return true
	}
	if l.min != r.min || l.num != r.num || l.len != r.len {
		return false
	}
	if len(l.tks) != len(r.tks) {
		return false
	}
	for i := 0; i < len(l.tks); i++ {
		if !l.tks[i].deepEqual(r.tks[i]) {
			return false
		}
	}
	return true
}

// 权重
func (l *line) Type() int {
	switch l.num {
	case 4:
		// 四张
		if len(l.tks) != 0 {
			// 双四张
			return TwinFour
		}
		return Four
	case 3:
		// 三牌
		return Triple
	case 2:
		// 对牌
		return Twin
	case 1:

		if l.len >= 3 {
			// 单连
			return Straight
		}
		// 单牌
		return Single
	}
	return 0
}

// 三牌四牌带牌
func (l *line) canTake() int {
	if l.tks != nil {
		return 0
	}
	/*if l.num == 4 {
		return 2
	} else if l.num == 3 {
		return l.len
	}*/
	return 0
}

// 能否出牌
func (l *line) canOut(pL int) bool {

	return true
}

func (l *line) isTake(b int) bool {
	for i := 0; i < l.len; i++ {
		if b == l.min+i {
			return false
		}
	}
	return true
}

func (l *line) hintType() int {
	switch l.num {
	case 4:
		if len(l.tks) == 0 {
			if l.len == 2 {
				// 双四张
				return TwinFour
			}
			return Four
		}

		// return Pass
	case 3:

		if len(l.tks) == 0 {
			return Triple
		}

		// return Pass
	case 2:

		if l.len == 1 {
			return Twin
		}
		// return Pass
	case 1:
		if l.len == 1 {
			return Single
		}
		if l.len >= 3 {
			// 单连
			return Straight
		}
	}
	return Pass
}

func (l *line) hintLen() (n int) {
	for _, v := range l.tks {
		n += v.hintLen()
	}
	n += int(l.num * l.len)
	return
}

// l > o 不同类型，小于等于为false 大于true
func (l *line) isBattle(o *line) bool {
	tp := l.hintType()
	ts := o.hintType()
	tpl := l.hintLen()
	tsl := o.hintLen()
	if tp == ts {
		if tpl == tsl {
			return l.min > o.min
		}
		return false
	}
	if o.min == int(POKER_2) {

		if ts == Single && tp == Four { // 0
			return true
		} else if ts == Twin && tp == TwinFour {
			return true
		}

	}

	return false
}

func (l *line) isBigThan(lastShowCard *KindOfCard) bool {
	if lastShowCard == nil {
		return true
	}
	tp := l.hintType()
	if tp == lastShowCard.GetKind() {
		if l.hintLen() == int(lastShowCard.GetLen()*lastShowCard.GetSame()) { // 同类型，同数量

			return l.min > int(lastShowCard.GetMin())
		}
		return false
	}

	if lastShowCard.GetMin() == POKER_2 {

		if lastShowCard.GetKind() == Single && tp == Four {
			return true
		} else if lastShowCard.GetKind() == Twin && tp == TwinFour {
			return true
		}

	}
	return false
}

func (l *line) outType(m int) int {
	if l.len > 1 || (l.len > 1 && l.num == 3) {
		if l.tks != nil && l.min <= m+0x8 {
			return 5 // 飞机带
		}
		if l.min <= m+0x8-l.num*2 {
			return 4 // 成连
		}
	}
	if l.num > 2 && l.min <= m {
		return 3 // 可带
	}
	for _, v := range l.tks {
		if v.outType(m) == 1 {
			return 2 // 被带
		}
	}
	if l.min <= m {
		return 1 // 普通
	}
	return 0
}

func (l *line) firstTake() *line {
	for _, v := range l.tks {
		return v // 最小的
	}
	return nil
}

/*
	func (l *line) Print(w io.Writer) {
		var trans = map[int]string{
			0:  "3",
			1:  "4",
			2:  "5",
			3:  "6",
			4:  "7",
			5:  "8",
			6:  "9",
			7:  "10",
			8:  "J",
			9:  "Q",
			10: "K",
			11: "A",
			12: "2",
		}

		l.nestPrint(w, trans, "")

		if l != nil {
			for _, v := range l.tks {
				v.nestPrint(w, trans, " 带")
			}
		}
		// fmt.Fprintln(w)
	}

func (l *line) nestPrint(w io.Writer, trans map[int]string, take string) {

		if l != nil {
			switch l.Type() {
			case TwinFour:
				fmt.Fprintf(w, "两组四牌 ")
			case Triple:
				fmt.Fprint(w, "三牌: ")
			case Four:
				fmt.Fprint(w, "四牌: ")
			case Straight:
				fmt.Fprintf(w, "%d单连: ", l.len)
			case Twin:
				fmt.Fprintf(w, "%s对牌: ", take)
			case Single:
				fmt.Fprintf(w, "%s单牌: ", take)
			}

			for i := l.min; i < l.min+l.len; i++ {
				fmt.Fprint(w, trans[i], ", ")
			}
		} else {
			fmt.Fprintf(w, "没有找到该出手牌 ")
		}
	}
*/
func (l *line) outPokers(poker []int32) ([]int32, []int32) {
	pok := make([]int32, len(poker))
	copy(pok, poker)
	var ret []int32
	if l != nil {
		for i := l.min; i != l.min+l.len; i++ {
			for j := 0; j != l.num; j++ {
				for k, v := range pok {
					if realVle(v) == i {
						ret = append(ret, v)
						pok = append(pok[:k], pok[k+1:]...) // del
						break
					}
				}
			}
		}
		for _, ll := range l.tks {
			for i := ll.min; i != ll.min+ll.len; i++ {
				for j := 0; j != ll.num; j++ {
					for k, v := range pok {
						if realVle(v) == i {
							ret = append(ret, v)
							pok = append(pok[:k], pok[k+1:]...) // del
							break
						}
					}
				}
			}
		}
	}
	// fmt.Println(poker, pok)
	return ret, pok
}

type cutex struct {
	l   []*line
	h   int   // 手数
	adv bool  // 2属于绝对优势牌 h <1 先出2
	b   int   // 炸弹
	m   []int // 哪几手非最大（方便测试人员，后在selectOut用于确认是否最大）
	f   bool  // 四带标志（炸弹带）
}

func (c *cutex) takeOther(max int) bool {
	var n int
	for k := len(c.l) - 1; k >= 0; k-- {
		if c.l[k].len != 1 || c.l[k].num > 2 {
			break
		}
		if c.l[k].min >= max {
			continue
		}
		if n++; n >= 2 {
			return true
		}
	}
	return false
}

/*
通过他人手牌推算出目前自己本手手牌是否为最大，最大不计入手数
*/
type enemy struct {
	otherPokers [][]int32
}

func newEnemy(pokers [][]int32) *enemy {
	var e enemy
	e.otherPokers = make([][]int32, len(pokers))
	for i, v := range pokers {
		e.otherPokers[i] = append(e.otherPokers[i], v...)
	}
	return &e
}

func (e *enemy) isMax(ls *line) bool {
	for _, v := range e.otherPokers {
		m := ptm(v)
		lines := findSameNum(m, 4)
		tp := ls.hintType()

		if len(v) < ls.hintLen() {
			continue
		}
		switch tp {
		case Straight:
			lines = findPlane(m, 1, ls.num)
			for _, v1 := range lines {
				if v1.isBattle(ls) {
					return false
				}
			}

		case Triple:
			lines = findSameNum(m, 3)
			for _, v1 := range lines {
				if v1.isBattle(ls) {
					return false
				}
			}
		case Four:
			lines = findSameNum(m, 4)
			for _, v1 := range lines {
				if v1.isBattle(ls) {
					return false
				}
			}

		default:
			if ls.min == int(POKER_2) {
				if tp == Twin {
					lines = findTwoSameNum(m, 4, 2)
					for _, v1 := range lines {
						if v1.isBattle(ls) {
							return false
						}
					}
				} else if tp == Single {
					lines = findSameNum(m, 4)
					for _, v1 := range lines {
						if v1.isBattle(ls) {
							return false
						}
					}
				}
			} else {
				lines = findSameType(m, ls.num)
				for _, v1 := range lines {
					if v1.isBattle(ls) {
						return false
					}
				}
			}
		}

	}
	return true
}

type cutexSlice []*cutex

func (c cutexSlice) Len() int {
	return len(c)
}
func (c cutexSlice) Less(i, j int) bool {
	if c[i].h != c[j].h {
		return c[i].h < c[j].h // 少->多
	}
	if c[i].b != c[j].b {
		return c[i].b > c[j].b // 8888 7 6 全场最大时不带着出
	}
	return len(c[i].l) < len(c[j].l) // 888 7 6 全场最大时带着出
}
func (c cutexSlice) Swap(i, j int) {
	c[i], c[j] = c[j], c[i]
}

func realVle(v int32) int { // 0-12 3~ A 2

	return int(v) % PER_CARD_COLOR_MAX

}

func ptm(in []int32) map[int]int {
	m := make(map[int]int)
	for _, v := range in {
		m[realVle(v)]++
	}
	return m
}

func haveLine(m map[int]int, num int, len int, min int) bool {
	if min+len > int(POKER_2) { // 2不能带
		return false
	}
	for i := min; i < min+len; i++ {
		if m[i] < num {
			return false
		}
	}
	return true
}

func makeLine(m, n, l int, s ...*line) *line {
	return &line{
		min: m, // 最小牌值
		num: n, // 单张数量
		len: l, // 长度
		tks: s,
	}
}

// 寻找相同个数的
func findSameNum(m map[int]int, n int) (res []*line) {
	maxcard := int(POKER_2)
	for i := 0; i <= maxcard; i++ {
		if m[i] >= n {
			res = append(res, makeLine(i, m[i], 1))
			res[len(res)-1].decr(m)
		}
	}

	return
}

// 寻找两4张
func findTwoSameNum(m map[int]int, n int, l int) (res []*line) {

	re := makeLine(-1, -1, -1)
	// re = makeLine(i, m[i], 1)
	maxcard := int(POKER_2)
	for i := 0; i <= maxcard; i++ {

		if m[i] >= n {
			if re.min == -1 {
				re.min = i
				re.num = m[i]
				re.len = 1
			} else {
				re.tks = append(re.tks, makeLine(i, m[i], l))
				// re.max = i
				re.len++
			}
			if re.len == l {
				res = append(res, re)
				return
			}

		}

	}

	return
}

// 连牌
func findPlane(m map[int]int, num int, len int) (res []*line) {
	for i := len; ; i++ {
		var flag bool // 默认false 单联判断成功就继续
		for j := 0; j <= 11; j++ {
			if haveLine(m, num, i, j) {
				res = append(res, makeLine(j, num, i))
				flag = true
			}
		}
		if !flag {
			break
		}
	}
	return
}

func findCommon(m map[int]int, n int) (res []*line) {
	max := int(POKER_2)

	for i := 0; i <= max; i++ {
		if m[i] >= n {
			res = append(res, makeLine(i, m[i], 1))
		}
	}
	return
}

func findSame(m map[int]int, n int) (res []*line) {
	max := int(POKER_2)

	for i := 0; i <= max; i++ {
		if m[i] >= n {
			res = append(res, makeLine(i, n, 1))
		}
	}
	return
}

func findSameType(m map[int]int, n int) (res []*line) {
	maxcard := int(POKER_2)
	for i := 0; i <= maxcard; i++ {
		if m[i] >= n {
			res = append(res, makeLine(i, n, 1))
		}
	}
	return
}

// 遍历选定某连，找出与该连能同时存在的连，递归以上操作直至找不到，这些选定的连则是一种合法选择
// 请给参数t指向的切片足够的容量，如此以来在递归调用中将不会因扩展容量而重新申请内存
// 可以把递归想象成前序遍历树，由根向左，t用来记录由根到当前的所有节点
// 参数l和局部变量ll用来表示当前节点的子节点
// 参数m用来限制子节点的生成
// 参数d用来记录深度
func selectLine(m map[int]int, l []*line, t []*line, s *[][]*line, d int) {
	for _, v := range l {
		t = append(t[:d], v)

		v.decr(m)

		var ll []*line
		for _, vv := range l {
			// 不考虑自己（增加为压而拆后这里存在再找自己有效的情况，譬如对2被拆v是单2）
			if v == vv {
				continue
			}
			if haveLine(m, vv.num, vv.len, vv.min) {
				ll = append(ll, vv) // 能匹配的向来不多，这里不事先make
			}
		}

		if len(ll) == 0 {
			// 必须拷贝出来，因为参数t随着递归会变，前个合法选择会被下个覆盖
			tt := make([]*line, 0, len(t))
			tt = append(tt, t...)
			*s = append(*s, tt)
		} else {
			selectLine(m, ll, t, s, d+1)
		}

		v.incr(m)
	}
}

// 0： 相等， 1： 大于， -1： 小于
type compareFunc func(p, q *line) int

type multiSorter struct {
	ls []*line
	fs []compareFunc
}

func (ms *multiSorter) Sort(ls []*line) {
	ms.ls = ls
	sort.Sort(ms)
}

func (ms *multiSorter) Len() int {
	return len(ms.ls)
}

func (ms *multiSorter) Swap(i, j int) {
	ms.ls[i], ms.ls[j] = ms.ls[j], ms.ls[i]
}

// 小 -> 大
func (ms *multiSorter) Less(i, j int) bool {
	for k := 0; k < len(ms.fs); k++ {
		if n := ms.fs[k](ms.ls[i], ms.ls[j]); n == 1 {
			return false
		} else if n == -1 {
			return true
		}
	}
	return false
}

func orderedBy(fs ...compareFunc) *multiSorter {
	return &multiSorter{
		fs: fs,
	}
}

// selectLine 后很容易[33 44][44 33]重复
// 但[33]均属于相同实例，排序后可以通过地址快速判定是否相同
// combineTake 后很容易带与被带均相同但由于带牌是拷贝而地址不等
// 必要时可为 selectLine 后单独实现 fastEqualLine 里面仅比较地址
func equalLine(l []*line, r []*line) bool {
	if len(l) != len(r) {
		return false
	}
	for i := 0; i < len(l); i++ {
		if !l[i].deepEqual(r[i]) {
			return false
		}
	}
	return true
}

func distinctLine(s [][]*line) [][]*line {
	// 排序便于比较相同
	for _, v := range s {
		orderedBy(rSortType, rSortMin, rSortLen).Sort(v)
	}
	//删除第二层重复的
	for p, lines := range s {
		arr_len := len(lines)
		for i := 0; i < arr_len; i++ {
			for j := i + 1; j < arr_len; j++ {
				l := lines[i]
				r := lines[j]
				if l.min != r.min || l.num != r.num || l.len != r.len {
					continue
				}
				s[p] = append(s[p][:i], lines[i+1:]...)
				arr_len--
				i--
				break
			}
		}
	}
	//删除第一层重复的
	arr_len := len(s)
	for i := 0; i < arr_len; i++ {
		for j := i + 1; j < arr_len; j++ {
			l := s[i]
			r := s[j]
			CompareSlice := func(l, r []*line) bool {
				if len(l) != len(r) {
					return false
				}
				var n int
				for i := 0; i < len(l); i++ {
					for j := 0; j < len(r); j++ {
						if l[i].min != r[j].min || l[i].num != r[j].num || l[i].len != r[j].len {
							n++
							break
						}
					}
				}
				if n == len(r) {
					return true
				}
				return false
			}
			if CompareSlice(l, r) {
				continue
			}
			s = append(s[:i], s[i+1:]...)
			arr_len--
			i--
			break
		}
	}
	//for i := 0; i < len(s); i++ {
	//	for j := i + 1; j < len(s); j++ {
	//		if equalLine(s[i], s[j]) {
	//			s = append(s[:j], s[j+1:]...)
	//			j--
	//		}
	//	}
	//}
	return s
}
func moreSplitTake(s *[][]*line, pL int, lastShowCard *KindOfCard) {
	ls := len(*s)

	for i := 0; i < ls; i++ {
		var ct int    // 可带牌数量
		p := (*s)[i]  // 始终指向即将拆对的
		pos := len(p) // 初始位置为长度预防无单对

		for k, v := range p {
			ct += v.canTake()

			if pos == len(p) && v.len == 1 && v.num <= 2 {
				pos = k // 记录首个单对
			}
		}

		// 单对不分类型按大小排序
		orderedBy(rSortMin).Sort(p[pos:])

		var bt int  // 被带牌数量
		var t []int // 记录对牌位置

		for j := len(p) - 1; j >= 0; j-- {
			if p[j].len != 1 || p[j].num > 2 {
				break
			}

			if bt++; bt >= ct {
				break
			}

			if p[j].num == 2 {
				t = append(t, j)
				bt++ // 对牌充当两单牌
			}
		}

		// 从小到大拆对
		for _, v := range t {
			n := make([]*line, 0, len(p)+1)
			l := makeLine(p[v].min, 1, 1)
			n = append(n, p[:v]...)
			n = append(n, p[v+1:]...)
			n = append(n, l, l)

			// 新增一种切牌
			*s = append(*s, n)
			orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:])
			p = n // 下次以本次结果为基础
		}
		orderedBy(rSortType, rSortMin, rSortLen).Sort(p[pos:])
	}
}

func combineTake(s *[][]*line, n int, pL int, lastShowCard *KindOfCard) {
	ln := len(*s)

	for i := n; i < ln; i++ {
		for j := 0; j < len((*s)[i]); j++ {
			// 已没有可带牌
			if (*s)[i][j].num < 3 {
				break
			}
			// 此牌已带过
			ct := (*s)[i][j].canTake()
			// fmt.Println("Here", ct)
			if ct == 0 {
				continue
			}

			// 记录被带单或对的位置
			is := make([]int, 0, ct)
			id := make([]int, 0, ct)
			for k := len((*s)[i]) - 1; k >= 0; k-- {
				// 已没有单或对
				if (*s)[i][k].len != 1 || (*s)[i][k].num > 2 {
					break
				}
				// 校验带牌与被带牌不能相同
				if !(*s)[i][j].isTake((*s)[i][k].min) {
					continue
				}
				if (*s)[i][k].num == 1 && len(is) < int(ct) {
					is = append(is, k)
				}
				if (*s)[i][k].num == 2 && len(id) < int(ct) {
					id = append(id, k)
				}
				// 已找够单和对
				if len(is) == ct || len(id) == ct {
					break
				}
			}
			// fmt.Println("Here", ct, is, id)
			if len(is) == ct {
				*s = append(*s, pickupTake((*s)[i], j, is))
			}
			if len(id) == ct {
				*s = append(*s, pickupTake((*s)[i], j, id))
			}
		}
	}

	// 有新增则递归
	if len(*s) > ln {
		combineTake(s, ln, pL, lastShowCard)
	}
}

// 带与被带之外为复制需考虑四点
// [#1][带][#2][被带2][#3][被带1][#4]
// 为便于理解刻意举需要考虑#3的例子
// [KKKAAA][555666][9][8][6][3]
// 当切成这样并考虑为56带38时
// m = 1; t = [53]
func pickupTake(s []*line, m int, t []int) []*line {
	r := make([]*line, 0, len(s)-len(t))
	r = append(r, s[:m]...) // #1
	tks := make([]*line, 0, len(t))
	for _, v := range t {
		tks = append(tks, s[v])
	}
	r = append(r, makeLine(s[m].min, s[m].num, s[m].len, tks...))
	for i := len(t) - 1; i >= 0; i-- {
		if i == len(t)-1 {
			r = append(r, s[m+1:t[i]]...) // #2
		} else {
			r = append(r, s[t[i+1]+1:t[i]]...) // #3
		}
	}
	return append(r, s[t[0]+1:]...) // #4
}

func splitThree(lines []*line) bool {
	min := -2
	flag := false
	for _, v := range lines {
		if v.num != 3 || v.min == 11 {
			flag = false
			break
		} else {
			if min == -2 {
				min = v.min
			} else if !flag && v.min-1 != min && v.min+1 != min { // 不是飞机的三带
				flag = true
			}
		}
	}
	return flag
}

// cutPokers pokers 自己手牌 lastShowCard 上家出牌信息 otherPokers 别人剩余手牌
func cutPokers(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32) []*cutex {
	m := ptm(pokers)
	pLen := len(pokers)

	var lines []*line

	// line 代表该名玩家能组成的所有牌型
	lines = append(lines, findPlane(m, 1, 3)...)         // 单连， 未去重
	lines = append(lines, findCommon(m, 2)...)           // 四牌 三牌 对牌
	lines = append(lines, findPress(m, lastShowCard)...) // 如果上家有牌就拆开

	var s [][]*line
	t := make([]*line, 0, len(lines))

	selectLine(m, lines, t, &s, 0)

	// 去重
	s = distinctLine(s)

	for i := 0; i < len(s); i++ {
		n := len(s[i])
		for j := 0; j < n; j++ {
			s[i][j].decr(m)
		}
		// 拆四余三、拆三余对、拆对余单、原本的单牌
		s[i] = append(s[i], findCommon(m, 1)...)
		for j := 0; j < n; j++ {
			s[i][j].incr(m)
		}
		orderedBy(rSortType, rSortMin, rSortLen).Sort(s[i])
	}

	// 只有散牌
	if len(s) == 0 {
		s = append(s, findCommon(m, 1))
		s = distinctLine(s)
	} else {

		moreSplitTake(&s, pLen, lastShowCard)

		// 带牌
		combineTake(&s, 0, pLen, lastShowCard)

		s = distinctLine(s)
	}

	cs := make([]*cutex, 0, len(s))
	enemy := newEnemy(otherPokers)
	var isMaxFlag bool
	if /*lastShowCard != nil ||*/ len(otherPokers) == 0 { // 由于首出牌敌人手牌传入为空，不限制会出错
		isMaxFlag = true
	}
	for _, v := range s {
		c := &cutex{
			l: v,
			h: len(v),
		}
		if m[int(POKER_2)] != 0 {
			c.adv = true
		}
		for k, vv := range v {
			if !isMaxFlag && enemy.isMax(vv) {
				if lastShowCard != nil && vv.isBigThan(lastShowCard) {
					c.h--
					continue
				} else if lastShowCard == nil {
					c.h--
					continue
				}
			} else if vv.min == int(POKER_2) {
				c.h--
				continue
			}
			c.m = append(c.m, k)
			continue

		}

		cs = append(cs, c)
	}
	sort.Sort(cutexSlice(cs))

	return cs
}

func headHintOut(pokers []int32, lastShowCard *KindOfCard) (lines []*line, retErr error) { // 提示
	defer func() { // 增加容错处理
		if err := recover(); err != nil {
			retErr = errors.New("headHintOut is err")
			lines = nil
		}
	}()
	if lastShowCard != nil && lastShowCard.len == 0 {
		//logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard)
		lastShowCard = nil
	}
	//var lines []*line
	m := ptm(pokers)
	lines = append(lines, findPlane(m, 1, 3)...) // 单连， 未去重
	lines = append(lines, findSame(m, 4)...)     // 四牌
	lines = append(lines, findSame(m, 3)...)     // 三牌
	lines = append(lines, findSame(m, 2)...)     // 对牌
	lines = append(lines, findSame(m, 1)...)     // 单牌

	return
}

/*
outType:
暂时考虑...
0 各自为战
1 给下个人喂牌,保证自己手数减少情况下管牌
2 管牌,保证自己手数情况下压牌
*/
func selectOut(pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool, outType int) (ls *line, retErr error) { // 选择手牌
	defer func() { // 增加容错处理
		if err := recover(); err != nil {
			retErr = errors.New("selectOut is err")
			ls = nil
		}
	}()
	if lastShowCard != nil && lastShowCard.len == 0 {
		//logger.Logger.Tracef("lastShowCard num is zero %v", lastShowCard)
		lastShowCard = nil
	}
	cs := cutPokers(pokers, lastShowCard, otherPokers)
	/*for _, v := range cs {
		for _, v1 := range v.l {
			fmt.Printf("CS: min:%v num:%v len:%v\n", v1.min, v1.num, v1.len)
			for _, v2 := range v1.tks {
				fmt.Printf(" tks:%v ", *v2)
			}
			v1.Print(os.Stdout)
			fmt.Println()
		}
		fmt.Printf(" cs h:%v b:%v m:%v abv:%v \n\n", v.h, v.b, v.m, v.adv)
	}*/
	// pL := len(pokers)
	// var one *line
	var offset int
	// h==0 全是最大，找到直接出
	// h==1 先找最大，并记录首个非最大，若没有最大则出非最大，此时会赢
	for _, c := range cs {
		if c.h > 1 { // 不能一手出完就跳过
			break
		}
		for k, l := range c.l {
			if !l.isBigThan(lastShowCard) {
				continue
			}
			// 没有非最大或这个不是非最大，说明这个是最大 考虑如果剩下一手情况下
			if len(c.m) == 0 || c.m[0] != k { // 后续考虑代码优化
				if len(c.l) > 2 { //真实手数超过两手
					for i, v := range c.l {
						if !v.isBigThan(lastShowCard) {
							continue
						}
						if len(c.m) == 0 {
							if i == k {
								continue
							}
						} else {
							if i == k || i == c.m[0] {
								continue
							}
						}
						ls = v
						// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
						return
					}
				}
				ls = l
				// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
				return
			}
			// 记录非最大
			if ls == nil {
				if outType == 0 { // 压牌
					ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn)
				} else {
					ls = l
				}
			}
		}
		offset++
	}

	// 出非最大
	if ls != nil {
		// ls = calcOut(ls, pokers, lastShowCard, otherPokers)
		return //one, ret
	}

	if len(cs[offset:]) != 0 {
		cs = cs[offset:]
	}

	// 首出
	// var ls *line
	if lastShowCard == nil {
		ls, retErr = headOut(cs, pokers)
	} else {
		ls = pressOut(cs, lastShowCard, outType)
	}
	if outType == 0 { // 压牌
		ls = calcOut(ls, pokers, lastShowCard, otherPokers, warn)
	}
	return //ls, ret //pressOut(cs, lastShowCard)
}

// 这里主要限制AAA带牌问题
func containBoom(ls []*line) bool {
	if len(ls) == 0 {
		return false
	}
	orderedBy(sortMin, sortTks).Sort(ls)
	if ls[0].num == 3 && ls[0].min == 13 {
		return true
	}
	return false
}

func pressOut(cs []*cutex, lastShowCard *KindOfCard, outType int) *line {
	var h int
	var ls []*line

	// 首个能压手数中可出牌
	// 本身就是根据h排序过的
	for _, c := range cs {
		if h != 0 && c.h > h {
			/*if containBoom(ls) { // 判断管牌有没有包含
				ls = ls[:0]
			} else {*/
			break // continue
			//}
		}
		for _, l := range c.l {

			if !l.isBigThan(lastShowCard) {
				// fmt.Printf("cs: l%v  type:%v\n", *l, l.hintType())
				continue
			}

			ls = append(ls, l)
			h = c.h
		}
	}
	if len(ls) == 0 {
		return nil
	}

	if outType != 0 {
		if h > cs[0].h+2 {
			return nil
		}
	}

	/*// 无牌可出
	if len(ls) == 0 {
		return nil
	}*/

	// 小 -> 小带
	orderedBy(sortMin, sortTks).Sort(ls)
	return ls[0]
}

func calcOut(ls *line, pokers []int32, lastShowCard *KindOfCard, otherPokers [][]int32, warn bool) *line {

	if (warn || len(otherPokers) != 0 && len(otherPokers[0]) == 1) && ls != nil { // 报单出最大
		if ls.hintType() != Single {
			return ls
		}
		m := ptm(pokers)
		if lastShowCard == nil { // 自动出牌出对牌
			for i := 0; i <= 11; i++ {
				if m[i] == 2 {
					return makeLine(i, m[i], 1)
				}
			}
		}
		// 没有对牌，出大单

		maxcard := int(POKER_2)
		for i := maxcard; i >= 0; i-- { // ls不为nil且为单牌 肯定能压牌
			if m[i] != 0 {
				return makeLine(i, 1, 1)
			}
		}
	}

	return ls
}

func headOut(cs []*cutex, pokers []int32) (l *line, retErr error) {
	defer func() { // 增加容错处理
		if err := recover(); err != nil {
			retErr = errors.New("headOut is err")
			l = nil
		}
	}()
	tps := make(map[int][]*line)

	// 最小逻辑牌值
	min := int(POKER_2)
	for _, v := range pokers {
		if n := realVle(v); n < min {
			min = n
		}
	}

	// 从最少手数中选出携带最小牌值的出牌按牌型记录
	for _, c := range cs {
		if c.h > cs[0].h {
			break
		}
		for _, l := range c.l {
			t := l.outType(min) // 暂不修改
			if t == 0 {         // 不携带最小牌值
				continue
			}
			if t == 2 && c.takeOther(l.min) {
				// 最小牌属于被带且当前切牌有别的被带可选
				tps[t] = append(tps[t], l.firstTake())
			} else {
				tps[t] = append(tps[t], l)
			}
		}
	}

	var i int
	var ls []*line

	// 飞机带 -> 成连 -> 可带 -> 被带 -> 普通
	for i = 5; i > 0; i-- {
		if v, ok := tps[i]; ok {
			ls = v
			break
		}
	}

	lLen := len(ls)
	if lLen == 0 { // 自动出牌判断出错，使用老处理逻辑重新判断
		l = nil
		retErr = errors.New("headOut is err")
		return
	}

	switch i {
	case 5:
		// 小 -> 长 -> 小带
		orderedBy(sortMin, rSortLen, sortTks).Sort(ls)
	case 4:
		// 小 -> 宽 -> 短
		orderedBy(sortMin, rSortNum, sortLen).Sort(ls)
	case 3:
		// 小带
		orderedBy(sortTks).Sort(ls)
	case 2:
		// 带 -> 小 -> 长
		orderedBy(rSortTake, sortMin, rSortLen).Sort(ls)
	case 1:
		// 宽
		orderedBy(rSortNum).Sort(ls)
	}

	l = ls[0]
	//retErr = errors.New("headOut is err") // 返回错误，使用老处理逻辑
	return // ls[10], ret
}

func findPress(m map[int]int, lastShowCard *KindOfCard) []*line {
	if lastShowCard == nil {
		return nil
	}
	n := 4
	var ret []*line
	switch lastShowCard.GetKind() {
	case Single:
		n = 1
		if lastShowCard.GetMin() == POKER_2 {
			n = 4
		}
	case Twin:
		n = 2
		if lastShowCard.GetMin() == POKER_2 {
			n = 4
		}
	case Triple:
		n = 3
	case Four:
		n = 4
	}

	for i := int(POKER_2); i > 0; i-- {
		if i <= int(lastShowCard.GetMin()) {
			break
		}
		if m[i] >= n {
			ret = append(ret, makeLine(i, n, 1))
			// return []*line{makeLine(i, n, 1)}
		}
	}

	return ret
}

var rSortLen = func(p, q *line) int {
	return sortLen(q, p)
}
var sortLen = func(p, q *line) int {
	if p.len == q.len {
		return 0
	}
	if p.len > q.len {
		return 1
	}
	return -1
}

var rSortNum = func(p, q *line) int {
	return sortNum(q, p)
}
var sortNum = func(p, q *line) int {
	if p.num == q.num {
		return 0
	}
	if p.num > q.num {
		return 1
	}
	return -1
}

var rSortMin = func(p, q *line) int {
	return sortMin(q, p)
}
var sortMin = func(p, q *line) int {
	if p.min == q.min {
		return 0
	}
	if p.min > q.min {
		return 1
	}
	return -1
}

var rSortTks = func(p, q *line) int {
	return sortTks(q, p)
}
var sortTks = func(p, q *line) int {

	pL := len(p.tks)
	qL := len(q.tks)
	if pL != qL { // 带对牌跟两张单牌。只需考虑对牌需不需要拆开
		if pL == 0 {
			return 1
		} else if qL == 0 {
			return -1
		}
		if p.tks[0].min < q.tks[0].min {
			return -1
		}
		if p.tks[0].min > q.tks[0].min {
			return 1
		}
		if pL > qL {
			return 1
		}
		return -1
	}
	for i := 0; i < pL; i++ {
		if p.tks[i].min < q.tks[i].min {
			return -1
		}
		if p.tks[i].min > q.tks[i].min {
			return 1
		}
	}
	return 0
}

var rSortType = func(p, q *line) int {
	return sortType(q, p)
}
var sortType = func(p, q *line) int {
	if tp, tq := p.Type(), q.Type(); tp == tq {
		return 0
	} else if tp > tq {
		return 1
	}
	return -1
}

var rSortTake = func(p, q *line) int {
	return sortTake(q, p)
}
var sortTake = func(p, q *line) int {
	if p.tks != nil {
		if q.tks == nil {
			return 1
		}
	} else if q.tks != nil {
		return -1
	}
	return 0
}
