package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strconv"
	"sync"
	"time"

	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/i18n"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/module"
	"mongo.games.com/goserver/core/task"
	"mongo.games.com/goserver/core/timer"

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	"mongo.games.com/game/protocol/tournament"
	webapi_proto "mongo.games.com/game/protocol/webapi"
	"mongo.games.com/game/srvdata"
	"mongo.games.com/game/webapi"
)

const (
	MatchTypeNormal   = 1 // 锦标赛
	MatchTypeChampion = 2 // 冠军赛
	MatchTypeVIP      = 3 // vip比赛
)

const (
	MatchSwitchStart = 1 // 开启
	MatchSwitchClose = 2 // 关闭
)

const (
	MatchUseRobot     = 0 // 使用机器人
	MatchNotUserRobot = 1 // 关闭机器人
)

// tournament

var TournamentMgr = &Tournament{
	GameMatchDateList:   make(map[string]map[int32]*webapi_proto.GameMatchDate), //比赛配置
	matches:             make(map[int32]map[int32]*TmMatch),
	signUpPlayers:       make(map[string]map[int32]*SignInfo),
	players:             make(map[int32]map[int32]*PlayerMatchContext),
	copyPlayers:         make(map[int32]map[int32][]*PlayerMatchContext),
	rankPlayers:         make(map[int32]map[int32][]*PlayerMatchContext),
	roundOverPlayerNum:  make(map[int32]map[int32]int32),
	finalPerRank:        make(map[int32][]*PerRankInfo),
	playerSignUpTime:    make(map[int32]int64),
	playerWaitStart:     make(map[int32]int64),
	singleSignupPlayers: make(map[int32]*SignupInfo),
	singleFinalGrades:   make(map[int32]map[int32]int32),
}

type Tournament struct {
	GameMatchDateList  map[string]map[int32]*webapi_proto.GameMatchDate // 比赛配置，platform:比赛场配置id:比赛配置
	matches            map[int32]map[int32]*TmMatch                     // 开始比赛的数据，比赛配置Id:比赛顺序序号:一场开始的比赛数据
	signUpPlayers      map[string]map[int32]*SignInfo                   // 报名的玩家 platform:比赛配置id:报名人
	players            map[int32]map[int32]*PlayerMatchContext          // 比赛中玩家 比赛顺序序号:snid:玩家信息
	copyPlayers        map[int32]map[int32][]*PlayerMatchContext        // 比赛玩家数据备份 比赛顺序序号:round:玩家信息
	rankPlayers        map[int32]map[int32][]*PlayerMatchContext        //比赛玩家为了每轮积分排名用 比赛顺序序号,round
	roundOverPlayerNum map[int32]map[int32]int32                        // 完成比赛人数 比赛序号:轮次:人数
	finalPerRank       map[int32][]*PerRankInfo                         //本场比赛最后排名,每淘汰一位记录一位，最后记录决赛玩家
	playerWaitStart    map[int32]int64                                  // 等待时间 玩家Id:等待时长秒
	singleFinalGrades  map[int32]map[int32]int32                        //使用机器人模式最后一轮分数

	// UseRobot
	playerSignUpTime    map[int32]int64       //玩家报名时间 玩家Id:报名时间戳
	singleSignupPlayers map[int32]*SignupInfo //报名的玩家，玩家Id:报名信息
	// UseRobot
}

type PerRankInfo struct {
	Name   string
	SnId   int32
	RankId int32
	Grade  int32
}

type SignInfo struct {
	signup   map[int32]*TmPlayer //玩家Id
	Platform string
	MaxCnt   int // 最大报名人数
}

type SignupInfo struct {
	Platform string
	Tmid     int32
	Snid     int32
}

// 检查下数据是否合法（主要检查报名人数和晋级方式）  n  n|...|4|1
func (this *Tournament) checkData(cfg *webapi_proto.GameMatchDate) bool {
	if cfg == nil {
		return false
	}
	l := len(cfg.MatchPromotion)
	if l < 2 {
		return false
	}
	if cfg.MatchPromotion[0] != cfg.MatchNumebr { //第一位必须和报名人数相同
		return false
	}
	if cfg.MatchPromotion[l-1] != 1 { //最后一位必须是1
		return false
	}
	if cfg.MatchPromotion[l-2] != 4 { //倒数第二位必须是4
		return false
	}
	for i, num := range cfg.MatchPromotion {
		if i < l-1 { //除了最后一位
			if num%4 != 0 { //必须是4的整倍数
				return false
			}
			if num < cfg.MatchPromotion[i+1] { //必须递减
				return false
			}
		}
	}

	return true
}

// UpdateData 更新配置
// init: 通知客户端配置变更
func (this *Tournament) UpdateData(init bool, cfgs *webapi_proto.GameMatchDateList) {
	//logger.Logger.Trace("(this *Tournament) UpdateData:", cfgs)
	if cfgs.Platform == "0" {
		return
	}
	gmds := make(map[int32]*webapi_proto.GameMatchDate)
	for _, v := range cfgs.List {
		if this.checkData(v) {
			gmds[v.Id] = v
		} else {
			logger.Logger.Error("GameMatchDate error: ", v)
		}
	}
	oldgmds := this.GameMatchDateList[cfgs.Platform]
	//旧配置关闭踢人 --旧配置关闭不影响已经开始的只取消报名未开始的
	for _, v := range oldgmds {
		gmd := gmds[v.Id]
		if (gmd != nil && v.MatchSwitch == MatchSwitchStart && gmd.MatchSwitch != v.MatchSwitch) || //配置关闭
			(gmd != nil && !this.IsTimeRange(gmd)) || //或者不在时间段内
			(gmd != nil && v.UseRobot != gmd.UseRobot) || //或者匹配模式有修改
			gmd == nil { //或者删除
			signInfo := this.signUpPlayers[cfgs.Platform][v.Id]
			if signInfo != nil && len(signInfo.signup) > 0 {
				// 取消报名
				this.CancelSignUpAll(cfgs.Platform, v.Id)
			}
		}
	}
	//新配置增加更新
	for _, v := range gmds {
		oldgmd := oldgmds[v.Id]
		if oldgmd == nil || v.MatchSwitch == MatchSwitchStart {
			if this.signUpPlayers[cfgs.Platform] == nil {
				this.signUpPlayers[cfgs.Platform] = make(map[int32]*SignInfo)
			}
			this.signUpPlayers[cfgs.Platform][v.Id] = &SignInfo{
				signup:   make(map[int32]*TmPlayer),
				Platform: cfgs.Platform,
				MaxCnt:   int(v.MatchNumebr),
			}
		}
		if v.MatchSwitch == MatchSwitchClose || !this.IsTimeRange(v) {
			this.CancelSignUpAll(cfgs.Platform, v.Id)
			if v.MatchSwitch == MatchSwitchClose {
				delete(this.signUpPlayers[cfgs.Platform], v.Id)
			}
		}
	}
	this.GameMatchDateList[cfgs.Platform] = gmds

	if !init {
		//todo 优化
		for _, v := range PlayerMgrSington.playerOfPlatform[cfgs.Platform] {
			//通知平台玩家数据更新
			pack := TournamentMgr.GetSCTMInfosPack(cfgs.Platform, v.AppChannel)
			proto.SetDefaults(pack)
			logger.Logger.Trace("SCTMInfos++++++++++++:", pack)
			v.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCTMInfos), pack)
		}
	}
}

// GetMatchInfo 比赛配置
func (this *Tournament) GetMatchInfo(platform string, tmId int32) *webapi_proto.GameMatchDate {
	if list, ok := this.GameMatchDateList[platform]; ok {
		if gmd, ok1 := list[tmId]; ok1 {
			return gmd
		}
	}
	return nil
}

// MatchSwitch 比赛开关
func (this *Tournament) MatchSwitch(platform string, tmId int32) bool {
	info := this.GetMatchInfo(platform, tmId)
	return info != nil && info.MatchSwitch == MatchSwitchStart
}

// IsUseRobot 是否用机器人
func (this *Tournament) IsUseRobot(platform string, tmId int32) bool {
	info := this.GetMatchInfo(platform, tmId)
	return info != nil && info.UseRobot == MatchUseRobot
}

// 周几
func getWeekNum(t time.Time) int {
	strWeek := []string{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
	week := t.Weekday()
	for i, s := range strWeek {
		if week.String() == s {
			return i
		}
	}
	return 0
}

// IsTimeRange 判断是否在比赛时间段内
// 在时间段内才能报名
func (this *Tournament) IsTimeRange(gmd *webapi_proto.GameMatchDate) bool {
	if gmd != nil {
		if gmd.MatchType == 2 { //冠军赛
			tNow := time.Now()
			switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段
			case 0:
				return true
			case 1:
				nowWeek := getWeekNum(tNow)
				hms := int32(tNow.Hour()*10000 + tNow.Minute()*100 + tNow.Second())
				if gmd.MatchTimeWeek != nil && len(gmd.MatchTimeWeek) > 0 {
					for _, week := range gmd.MatchTimeWeek {
						if nowWeek == int(week) {
							if hms >= gmd.MatchTimeStartHMS && hms <= gmd.MatchTimeEndHMS {
								return true
							}
						}
					}
				}
			case 2:
				if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 {
					startStamp := gmd.MatchTimeStamp[0]
					endStamp := gmd.MatchTimeStamp[1]
					if tNow.Unix() >= startStamp && tNow.Unix() <= endStamp {
						return true
					}
				}
			}
		} else { //锦标赛没有时间限制
			return true
		}
	}
	return false
}

// IsOutTime 是否过期
func (this *Tournament) IsOutTime(gmd *webapi_proto.GameMatchDate) bool {
	if gmd == nil || gmd.MatchSwitch != MatchSwitchStart {
		return true
	}
	if gmd.MatchType == MatchTypeChampion { //冠军赛的一次性时间段才有过期可能
		switch gmd.MatchTimeType { // 0无时效 1重复时间段 2一次性时间段
		case 2:
			tNow := time.Now()
			if gmd.MatchTimeStamp != nil && len(gmd.MatchTimeStamp) > 1 {
				endStamp := gmd.MatchTimeStamp[1]
				if tNow.Unix() >= endStamp { //当前时间大于截止时间
					return true
				}
			}
		}
	}
	return false
}

// GetAllMatchInfo 比赛配置数据
func (this *Tournament) GetAllMatchInfo(platform string) map[int32]*webapi_proto.GameMatchDate {
	if list, ok := this.GameMatchDateList[platform]; ok {
		return list
	}
	return nil
}

// IsMatching 判断是否在比赛中
func (this *Tournament) IsMatching(snid int32) bool {
	if this.players != nil {
		for _, v := range this.players {
			if v != nil {
				for k := range v {
					if k == snid {
						return true
					}
				}
			}
		}
	}
	return false
}

// IsMatchWaiting  判断是否在匹配中
func (this *Tournament) IsMatchWaiting(platform string, snid int32) (bool, int32) {
	// 未使用机器人
	if this.signUpPlayers != nil && this.signUpPlayers[platform] != nil {
		for tmid, info := range this.signUpPlayers[platform] {
			if info.signup != nil {
				for sid, _ := range info.signup {
					if sid == snid {
						return true, tmid
					}
				}
			}
		}
	}
	// 使用机器人了
	if v, ok := this.singleSignupPlayers[snid]; ok {
		return true, v.Tmid
	}
	return false, 0
}

// Quit 如果正在等待比赛,退赛
func (this *Tournament) Quit(platform string, snid int32) {
	isWaiting, tmid := this.IsMatchWaiting(platform, snid)
	if isWaiting {
		this.CancelSignUp(platform, tmid, snid)
	}
}

// signUpCost 报名
// tmId 比赛配置id
// cost true报名、false取消报名
// 报名费用 0成功 3道具不足 5金币不足 6钻石不足 7免费次数不足
func (this *Tournament) signUpCost(tmId int32, p *Player, cost bool) (bool, int32) {
	logger.Logger.Trace("报名费用 signUpCost: ", tmId, " snid: ", p.SnId, " cost: ", cost)
	if p == nil {
		return false, int32(tournament.SignRaceCode_OPRC_NoItem)
	}
	gmd := this.GetMatchInfo(p.Platform, tmId)
	if gmd != nil && !p.IsRob { //真人费用检测
		if gmd.MatchType == MatchTypeVIP { //VIP比赛场
			freeTimes := p.GetVIPPrivilege1() // VIP比赛场免费次数
			logger.Logger.Trace("报名费用免费次数: freeTimes: ", freeTimes, " p.VipMatchTimes: ", p.VipMatchTimes)
			if freeTimes > 0 {
				if cost {
					if p.VipMatchTimes < freeTimes { //参与次数<免费次数
						p.VipMatchTimes++
					} else {
						logger.Logger.Trace("免费次数不足1")
						return false, int32(tournament.SignRaceCode_OPRC_Free)
					}
				} else {
					if p.VipMatchTimes > 0 {
						p.VipMatchTimes--
					} else {
						p.VipMatchTimes = 0
					}
				}
			} else {
				logger.Logger.Trace("免费次数不足2")
				return false, int32(tournament.SignRaceCode_OPRC_Free)
			}
		}
		if gmd.SignupCostCoin > 0 {
			logger.Logger.Trace("比赛场报名消耗金币", cost, gmd.SignupCostCoin)
			if cost {
				if p.Coin < gmd.SignupCostCoin { //金币不足
					logger.Logger.Trace("金币不足")
					return false, int32(tournament.SignRaceCode_OPRC_Coin)
				} else {
					p.AddCoin(-gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗")
				}
			} else {
				p.AddCoin(gmd.SignupCostCoin, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还")
			}
		}
		if gmd.SignupCostDiamond > 0 {
			logger.Logger.Trace("比赛场报名消耗钻石", cost, gmd.SignupCostDiamond)
			if cost {
				if p.Diamond < gmd.SignupCostDiamond { //钻石不足
					logger.Logger.Trace("钻石不足")
					return false, int32(tournament.SignRaceCode_OPRC_Diamond)
				} else {
					p.AddDiamond(-gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名消耗")
				}
			} else {
				p.AddDiamond(gmd.SignupCostDiamond, 0, common.GainWay_MatchSignup, "system", gmd.MatchName+"-报名退还")
			}
		}
		if gmd.SignupCostItem != nil && gmd.SignupCostItem.ItemNum > 0 {
			//背包数据处理
			logger.Logger.Trace("比赛场报名消耗道具", cost, gmd.SignupCostItem.ItemNum)
			item := BagMgrSingleton.GetItem(p.SnId, gmd.SignupCostItem.ItemId)
			if item != nil {
				if cost {
					if item.ItemNum < gmd.SignupCostItem.ItemNum {
						logger.Logger.Trace("道具不足")
						return false, int32(tournament.SignRaceCode_OPRC_NoItem)
					} else {
						BagMgrSingleton.SalePlayerItem(p, item, gmd.SignupCostItem.ItemNum)
						//item.ItemNum -= gmd.SignupCostItem.ItemNum
						//p.dirty = true
						BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemConsume, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名消耗")
						//BagMgrSingleton.SyncBagData(p, item.ItemId)
					}
				} else {
					BagMgrSingleton.SalePlayerItem(p, item, -gmd.SignupCostItem.ItemNum)
					//item.ItemNum += gmd.SignupCostItem.ItemNum
					//p.dirty = true
					BagMgrSingleton.RecordItemLog(p.Platform, p.SnId, ItemObtain, item.ItemId, item.Name, gmd.SignupCostItem.ItemNum, gmd.MatchName+"-报名退还")
					//BagMgrSingleton.SyncBagData(p, item.ItemId)
				}
			} else {
				logger.Logger.Trace("道具不足")
				return false, int32(tournament.SignRaceCode_OPRC_NoItem)
			}
		}
	}
	return true, 0
}

// SignUp 报名
// 0成功 1重复报名 2比赛没有开启 3道具不足 4不在报名时间段 5金币不足 6钻石不足 7免费次数不足
func (this *Tournament) SignUp(tmId int32, p *Player) (bool, int32) {
	logger.Logger.Trace("(this *Tournament) SignUp:", tmId, p.SnId)
	// 开启
	info := this.GetMatchInfo(p.Platform, tmId)
	if info == nil || info.GetMatchSwitch() == MatchSwitchClose {
		return false, int32(tournament.SignRaceCode_OPRC_Close)
	}
	// 报名时间
	if !TournamentMgr.IsTimeRange(info) {
		return false, int32(tournament.SignRaceCode_OPRC_Time)
	}
	// 已报名
	isWaiting, _ := TournamentMgr.IsMatchWaiting(p.Platform, p.SnId)
	if TournamentMgr.IsMatching(p.SnId) || isWaiting {
		return false, int32(tournament.SignRaceCode_OPRC_Repeat)
	}
	// 扣报名费
	ok, code := this.signUpCost(tmId, p, true)
	if !ok {
		return false, code
	}

	var signInfo *SignInfo
	gmd := this.GetMatchInfo(p.Platform, tmId)
	if gmd == nil {
		logger.Logger.Errorf("GetMatchInfo is nil. id=%v", tmId)
		return false, int32(tournament.SignRaceCode_OPRC_Repeat)
	}

	if this.IsUseRobot(p.Platform, tmId) {
		signupInfo := &SignupInfo{
			Platform: p.Platform,
			Tmid:     tmId,
			Snid:     p.SnId,
		}
		this.singleSignupPlayers[p.SnId] = signupInfo
		this.playerSignUpTime[p.SnId] = time.Now().Unix()
	} else {
		signInfo = &SignInfo{
			signup:   make(map[int32]*TmPlayer),
			Platform: p.Platform,
			MaxCnt:   int(gmd.MatchNumebr),
		}
		var platform = p.Platform
		gps := PlatformMgrSingleton.GetGameFree(p.Platform, gmd.GameFreeId)
		if gps != nil && gps.GroupId != 0 {
			for plt := range this.signUpPlayers {
				gps2 := PlatformMgrSingleton.GetGameFree(plt, gmd.GameFreeId)
				if gps.GroupId == gps2.GroupId {
					platform = plt
					break
				}
			}
		}
		if _, ok := this.signUpPlayers[platform][tmId]; !ok {
			this.signUpPlayers[platform][tmId] = signInfo
		} else {
			signInfo = this.signUpPlayers[platform][tmId]
		}

		if _, ok := signInfo.signup[p.SnId]; !ok {
			n := len(signInfo.signup) + 1
			signInfo.signup[p.SnId] = &TmPlayer{SnId: p.SnId, seq: n}
			logger.Logger.Trace("没使用机器人，真人 报名.............", n, " p.SnId: ", p.SnId)
		} else {
			return false, int32(tournament.SignRaceCode_OPRC_Repeat)
		}
	}
	//（报名人数/20，报名人数/10）
	minTime := gmd.MatchNumebr / 20
	maxTime := gmd.MatchNumebr / 10
	if gmd.MatchNumebr > 20 {
		this.playerWaitStart[p.SnId] = int64(minTime + rand.Int31n(maxTime-minTime))
	} else {
		this.playerWaitStart[p.SnId] = 1
	}
	return true, 0
}

// CancelSignUp 取消玩家报名
func (this *Tournament) CancelSignUp(platform string, tmid, snid int32) {
	delete(this.playerWaitStart, snid)
	if this.signUpPlayers[platform] != nil && this.signUpPlayers[platform][tmid] != nil {
		signInfo := this.signUpPlayers[platform][tmid]
		if _, ok := signInfo.signup[snid]; ok {
			p := PlayerMgrSington.GetPlayerBySnId(snid)
			if p != nil && p.scene == nil {
				//退费
				this.signUpCost(tmid, p, false)
				delete(this.signUpPlayers[platform][tmid].signup, snid)
				//通知取消报名
				pack := &tournament.SCSignRace{
					OpCode:  1,
					RetCode: 0,
				}
				proto.SetDefaults(pack)
				logger.Logger.Trace("signUpPlayers: ", pack)
				p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack)
				return
			}
		}
	}
	if signupinfo, ok := this.singleSignupPlayers[snid]; ok {
		p := PlayerMgrSington.GetPlayerBySnId(snid)
		if p != nil && p.scene == nil {
			//退费
			this.signUpCost(signupinfo.Tmid, p, false)
			delete(this.singleSignupPlayers, snid)
			delete(this.playerSignUpTime, snid)
			//通知取消报名
			pack := &tournament.SCSignRace{
				OpCode:  1,
				RetCode: 0,
			}
			proto.SetDefaults(pack)
			logger.Logger.Trace("singleSignupPlayers: ", pack)
			p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCSignRace), pack)
			return
		}
	}
}

// CancelSignUpAll 取消所有报名
func (this *Tournament) CancelSignUpAll(platform string, tmId int32) {
	if this.signUpPlayers[platform] != nil && this.signUpPlayers[platform][tmId] != nil {
		for _, tmp := range this.signUpPlayers[platform][tmId].signup {
			this.CancelSignUp(platform, tmId, tmp.SnId)
		}
	}
	if this.singleSignupPlayers != nil {
		for _, signupInfo := range this.singleSignupPlayers {
			if signupInfo.Tmid == tmId {
				this.CancelSignUp(signupInfo.Platform, tmId, signupInfo.Snid)
			}
		}
	}
}

// CanStart 是否开赛
// 没有开机器人走这个判断
func (this *Tournament) CanStart(platform string, tmId int32) bool {
	if this.signUpPlayers != nil && this.signUpPlayers[platform] != nil {
		if signInfo, ok := this.signUpPlayers[platform][tmId]; ok {
			matchInfo := this.GetMatchInfo(signInfo.Platform, tmId)
			if matchInfo != nil {
				n := 0
				for _, v := range signInfo.signup {
					p := PlayerMgrSington.GetPlayerBySnId(v.SnId)
					if p == nil || p.scene != nil { //人不在或者在游戏内
						this.CancelSignUp(platform, tmId, v.SnId)
						break
					}
					n++
				}
				logger.Logger.Trace("TournamentMgr.CanStart: n: ", n, " matchInfo: ", matchInfo)
				needNum := matchInfo.MatchNumebr / 4 * 4 //对4向下取整防止后台配置数据不是4的倍数
				//所有人都准备好了
				if needNum == int32(n) {
					//人满 开始比赛
					return true
				}
			}
		}
	}
	return false
}

// Start 开赛
func (this *Tournament) Start(platform string, tmId int32) {
	if this.signUpPlayers[platform] == nil {
		this.signUpPlayers[platform] = make(map[int32]*SignInfo)
	}
	matchInfo := this.GetMatchInfo(platform, tmId)
	signInfo := this.signUpPlayers[platform][tmId]
	sortId := int32(time.Now().Nanosecond())
	tm := &TmMatch{
		TMId:        tmId,
		SortId:      sortId,
		gmd:         matchInfo,
		Platform:    signInfo.Platform,
		tmpUseRobot: matchInfo.UseRobot,
	}
	tm.gml = srvdata.MatchLevelMgr.Get(matchInfo.GameFreeId, matchInfo.MatchLevel)
	tm.dbGameFree = srvdata.PBDB_GameFreeMgr.GetData(matchInfo.GameFreeId)
	tm.CopyMap(signInfo.signup)
	if this.matches[tmId] == nil {
		this.matches[tmId] = make(map[int32]*TmMatch)
	}
	logger.Logger.Tracef("开赛：%+v, 难度：%v", tm, tm.gml)
	// 开始比赛，清除报名数据
	this.matches[tmId][sortId] = tm
	tm.Start()
	this.signUpPlayers[platform][tmId].signup = make(map[int32]*TmPlayer)
}

// NextRoundStartSingle 下一轮是否开始
func (this *Tournament) NextRoundStartSingle(sortId int32, mtp *PlayerMatchContext, matchRobotGrades map[int32]int32) {
	logger.Logger.Tracef("================NextRoundStart_Single==========当前第 %v 轮============", mtp.round)
	if _, ok := this.copyPlayers[sortId]; ok {
		if _, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 {
			gmd := mtp.tm.gmd
			//需要晋级的人数
			promotionNum := gmd.MatchPromotion[mtp.round] // 晋级人数
			if promotionNum != 1 {                        // 非决赛
				if mtp.tm.robotGrades == nil || mtp.tm.robotGrades[int(mtp.round)] == nil {
					mtp.tm.CreateRobotGrades(int(mtp.round))
				}
				if mtp.tm.robotGrades != nil && mtp.tm.robotGrades[int(mtp.round)] != nil {
					robotGrades := mtp.tm.robotGrades[int(mtp.round)]
					// 修改陪真人玩的机器人积分
					if matchRobotGrades != nil {
						logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades)
						for _, gradeInfo := range robotGrades {
							if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok {
								gradeInfo.grade = grade
							}
						}
					}
					//非决赛淘汰后开始配桌
					logger.Logger.Trace("需要晋级的人数 promotionNum: ", promotionNum)
					logger.Logger.Trace("非决赛开始淘汰晋级 grade: ", mtp.grade)
					// 记录真人积分
					findMe := false
					for _, gradeinfo := range robotGrades {
						if gradeinfo.copySnid == 0 {
							gradeinfo.grade = mtp.grade
							findMe = true
							break
						}
					}
					if !findMe {
						gradeInfo := &TmGradeInfo{
							grade:    mtp.grade,
							copySnid: 0,
						}
						robotGrades = append(robotGrades, gradeInfo) // 添加了真人数据
					}
					sort.Slice(robotGrades, func(i, j int) bool {
						return robotGrades[i].grade > robotGrades[j].grade
					})
					for _, info := range robotGrades {
						logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId)
					}
					// 获取排名
					index := int32(0) //晋级淘汰
					for i, gradeinfo := range robotGrades {
						if mtp.grade >= gradeinfo.grade {
							index = int32(i) + 1
							break
						}
					}
					robotGrades = append(robotGrades[:promotionNum])
					mtp.tm.robotGrades[int(mtp.round)] = robotGrades

					if index == 0 { // 是最后一名
						index = gmd.MatchPromotion[mtp.round-1]
					}
					if index <= promotionNum {
						mtp.rank = index
						this.sendPromotionInfo(mtp, sortId, JinJi, false, false) //晋级

						mct := []*PlayerMatchContext{mtp}
						finals := false
						if mtp.round == int32(len(gmd.MatchPromotion)-2) {
							finals = true
						}
						timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
							MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1)
							return true
						}), nil, time.Second*7, 1)
					} else {
						mtp.rank = index
						pri := &PerRankInfo{
							Name:   mtp.p.Name,
							SnId:   mtp.p.SnId,
							RankId: mtp.rank,
							Grade:  mtp.grade,
						}
						this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri)
						this.sendPromotionInfo(mtp, sortId, TaoTai, true, false) //淘汰
					}
				}
			} else {
				// 比赛结束
				// 修改陪真人玩的机器人积分
				robotGrades := mtp.tm.robotGrades[int(mtp.round-1)]
				if matchRobotGrades != nil {
					logger.Logger.Trace("matchRobotGrades: ", matchRobotGrades)
					for snid, grade := range matchRobotGrades {
						logger.Logger.Trace("Snid: ", snid, " grade: ", grade)
					}
					for _, gradeInfo := range robotGrades {
						if grade, ok := matchRobotGrades[gradeInfo.copySnid]; ok {
							gradeInfo.grade = grade
						}
					}
				}
				// 记录真人积分
				for _, gradeinfo := range robotGrades {
					if gradeinfo.copySnid == 0 {
						gradeinfo.grade = mtp.grade
						break
					}
				}
				sort.Slice(robotGrades, func(i, j int) bool {
					return robotGrades[i].grade > robotGrades[j].grade
				})
				logger.Logger.Trace("robotGrades: ")
				for _, info := range robotGrades {
					logger.Logger.Trace("Snid: ", info.copySnid, " grade: ", info.grade, " copyLv: ", info.copyLv, " copyRoleId: ", info.copyRoleId)
				}
				// 获取真人名次
				index := int32(0) //晋级淘汰
				for i, gradeinfo := range robotGrades {
					if mtp.grade >= gradeinfo.grade {
						index = int32(i) + 1
						break
					}
				}
				if index == 0 {
					index = gmd.MatchPromotion[mtp.round-1] //最后一名
				}
				mtp.rank = index
				if this.finalPerRank[sortId] == nil {
					this.finalPerRank[sortId] = []*PerRankInfo{}
				}
				pri := &PerRankInfo{
					Name:   mtp.p.Name,
					SnId:   mtp.p.SnId,
					RankId: mtp.rank,
					Grade:  mtp.grade,
				}
				this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜
				outCode := TaoTai
				if mtp.rank == 1 {
					outCode = JinJi
				}
				this.sendPromotionInfo(mtp, sortId, outCode, true, true) //晋级
				logger.Logger.Trace("比赛结束!!! ")
				// 比赛结束
				this.StopMatch(mtp.tm.TMId, sortId)
			}
		}
	}
}

// 下一轮是否开始
func (this *Tournament) NextRoundStart(sortId int32, mtp *PlayerMatchContext) {
	logger.Logger.Tracef("================NextRoundStart==========当前第 %v 轮============", mtp.round)
	if _, ok := this.copyPlayers[sortId]; ok {
		if mcs, ok1 := this.copyPlayers[sortId][mtp.round]; ok1 {
			gmd := mtp.tm.gmd
			//需要晋级的人数
			promotionNum1 := gmd.MatchPromotion[mtp.round-1]
			promotionNum := gmd.MatchPromotion[mtp.round]
			//通知晋级信息：0.晋级等待匹配 1.失败退出 2.等待判断是否晋级
			pack := &tournament.SCPromotionInfo{}
			if promotionNum != 1 {
				n := int32(len(mcs))
				//非决赛淘汰后开始配桌
				logger.Logger.Trace("非决赛开始淘汰晋级")
				outNum := promotionNum1 - promotionNum
				//已经晋级的人数减去一桌之后  剩余人数还能够满足本轮淘汰
				logger.Logger.Trace("n: ", n, " outNum: ", outNum)
				if n-4 >= outNum {
					//提前晋级的开始凑桌
					MatchContextSlice(mcs).Sort(false)
					//挑选出晋级的玩家
					meIn := false //自己晋级
					for k, v := range mcs {
						if mtp.p.SnId == v.p.SnId {
							meIn = true
						}
						logger.Logger.Trace("排序之后=========== ", k, v.p.SnId, v.round, v.seq, v.grade)
					}
					mct := []*PlayerMatchContext{}
					finals := false
					for i := 0; i < len(mcs)-int(outNum); i++ {
						var mc PlayerMatchContext
						mc = *mcs[i]
						this.sendPromotionInfo(mcs[i], sortId, JinJi, false, false) //晋级
						mc.rank = mcs[i].rank
						mct = append(mct, &mc)
						logger.Logger.Trace("======凑桌==========mc=================", mc)
						if !finals && mc.round == int32(len(gmd.MatchPromotion)-2) {
							finals = true
						}
					}
					mcs = mcs[len(mct):]
					this.copyPlayers[sortId][mtp.round] = this.copyPlayers[sortId][mtp.round][len(mct):]
					willOut := false
					if promotionNum1 == this.roundOverPlayerNum[sortId][mtp.round-1] {
						//最后一个人打完了，确定要淘汰的人
						willOut = true
					} else {
						if !meIn { //自己暂时没晋级
							this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定
						}
					}
					if this.finalPerRank[sortId] == nil {
						this.finalPerRank[sortId] = []*PerRankInfo{}
					}
					isOver := false
					for k, v := range this.copyPlayers[sortId][mtp.round] {
						logger.Logger.Trace("凑桌之后剩余===2======== ", k, v.p.SnId, v.round, v.seq, v.grade)
						if willOut {
							this.sendPromotionInfo(v, sortId, TaoTai, true, false) //淘汰
							//把淘汰的玩家记录在排行榜
							pri := &PerRankInfo{
								Name:   v.p.Name,
								SnId:   v.p.SnId,
								RankId: pack.RankId,
								Grade:  v.grade,
							}
							this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri)
							//真人被淘汰，如果剩下的都是机器人，比赛解散
							if !v.p.IsRob {
								if this.players[sortId] != nil {
									hasReal := false
									for snid, context := range this.players[sortId] {
										if !context.p.IsRob && !this.isOut(sortId, snid) { // 有真人没有淘汰
											hasReal = true
											break
										}
									}
									//没有真人比赛解散
									if !hasReal {
										isOver = true
										logger.Logger.Trace("没有真人比赛解散")
										this.StopMatch(mtp.tm.TMId, sortId)
									}
								}
							}
						}
					}
					if !isOver {
						timer.StartTimer(timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool {
							MatchSceneMgrSingleton.NewRoundStart(mtp.tm, mct, finals, mtp.round+1)
							return true
						}), nil, time.Second*7, 1)
					}
				} else {
					this.sendPromotionInfo(mtp, sortId, DaiDing, false, false) //待定
				}
			} else {
				if len(mcs) == 4 {
					MatchContextSlice(mcs).Sort(true)
					if this.finalPerRank[sortId] == nil {
						this.finalPerRank[sortId] = []*PerRankInfo{}
					}
					for _, mc := range mcs {
						pri := &PerRankInfo{
							Name:   mc.p.Name,
							SnId:   mc.p.SnId,
							RankId: mc.rank,
							Grade:  mc.grade,
						}
						this.finalPerRank[sortId] = append(this.finalPerRank[sortId], pri) //把决赛的玩家记录在排行榜
						outCode := TaoTai
						if mc.rank == 1 {
							outCode = JinJi
						}
						this.sendPromotionInfo(mc, sortId, outCode, true, true) //晋级
					}
					logger.Logger.Trace("比赛结束!!! ")
					// 比赛结束
					this.StopMatch(mtp.tm.TMId, sortId)
				}
			}
		}
	}
}

const (
	JinJi   = 0 // 晋级
	TaoTai  = 1 // 淘汰
	DaiDing = 2 // 待定
)

// 发送晋级信息
// outCode 0晋级 1淘汰 2待定
// isOver 玩家比赛结束
// isFinals 比赛结束
func (this *Tournament) sendPromotionInfo(mc *PlayerMatchContext, sortId int32, outCode int, isOver, isFinals bool) {
	if mc == nil {
		return
	}
	rankId := mc.rank
	if mc.tm.tmpUseRobot == MatchNotUserRobot { // 不使用机器人
		rankId = this.getRank(sortId, mc.round, mc.p.SnId, isFinals)
		mc.rank = rankId
	}
	//通知晋级信息：0.晋级等待匹配 1.失败退出 2.等待判断是否晋级
	pack := &tournament.SCPromotionInfo{}
	pack.RetCode = int32(outCode)
	pack.Round = mc.round
	pack.RankId = rankId
	pack.RoundCoin = 100 //暂时用不到先写死
	pack.Record = mc.record
	if mc.tm != nil {
		pack.MatchId = mc.tm.TMId
	}
	if mc.tm != nil && mc.tm.gmd != nil {
		pack.MatchPromotion = mc.tm.gmd.GetMatchPromotion()
		pack.MatchName = mc.tm.gmd.GetMatchName()
		if !mc.p.IsRob && isOver { //真人发奖
			if mc.tm.gmd.Award != nil {
				for _, award := range mc.tm.gmd.Award {
					if rankId >= award.UpLimit && rankId <= award.DownLimit { //上下限是反的，我也是醉了
						rankAward := &tournament.RankAward{
							Coin:    proto.Int64(award.Coin),
							Diamond: proto.Int64(award.Diamond),
						}
						if award.ItemId != nil {
							for _, info := range award.ItemId {
								item := &tournament.ItemInfo{
									ItemId:  proto.Int32(info.ItemId),
									ItemNum: proto.Int32(int32(info.ItemNum)),
									Name:    proto.String(info.Name),
								}
								rankAward.ItemInfo = append(rankAward.ItemInfo, item)
							}
						}
						// test
						//rankAward.ItemInfo = append(rankAward.ItemInfo, &tournament.ItemInfo{
						//	ItemId:  30003,
						//	ItemNum: 1,
						//	Name:    "1元话费兑换码",
						//})
						// test
						pack.RankAward = rankAward
					}
				}
			}
		}
	}

	sendFunc := func() {
		outStr := "晋级"
		switch outCode {
		case 1:
			outStr = "淘汰"
		case 2:
			outStr = "待定"
		}
		proto.SetDefaults(pack)
		logger.Logger.Trace("sendPromotionInfo: ", outStr, " snid: ", mc.p.SnId, " pack: ", pack)
		ok := mc.p.SendToClient(int(tournament.TOURNAMENTID_PACKET_TM_SCPromotionInfo), pack)
		if ok && !mc.p.IsRob && isOver {
			if pack.RankAward != nil {
				if pack.RankAward.Coin != 0 { //金币
					mc.p.AddCoin(pack.RankAward.Coin, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励")
				}
				if pack.RankAward.Diamond != 0 { //钻石
					mc.p.AddDiamond(pack.RankAward.Diamond, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励")
				}
				if pack.RankAward.ItemInfo != nil {
					for _, info := range pack.RankAward.ItemInfo {
						if info.ItemNum <= 0 {
							continue
						}
						item := &Item{
							ItemId:  info.ItemId,
							ItemNum: int64(info.ItemNum),
						}
						BagMgrSingleton.AddJybBagInfo(mc.p, []*Item{item}, 0, common.GainWay_MatchSystemSupply, "system", mc.tm.gmd.MatchName+"排名奖励")
						data := srvdata.PBDB_GameItemMgr.GetData(info.ItemId)
						if data != nil {
							// 背包变更记录
							BagMgrSingleton.RecordItemLog(mc.p.Platform, mc.p.SnId, ItemObtain, item.ItemId, data.Name, item.ItemNum, mc.tm.gmd.MatchName+"排名奖励")
						}
					}
				}
			}
		}
		if isOver && mc.tm != nil { // 自己比赛结束
			if !mc.p.IsRob {
				this.CheckAddMatchSeasonLv(mc)
			}
			delete(this.players[sortId], mc.p.SnId)
		}
	}

	// 获取兑换码奖品
	var has bool
	wg := new(sync.WaitGroup)
	if pack.GetRankAward() != nil {
		for _, v := range pack.GetRankAward().GetItemInfo() {
			data := srvdata.PBDB_GameItemMgr.GetData(v.ItemId)
			if data != nil && data.GetType() == common.ItemTypePhoneCode {
				has = true
				item := v // 并发需要复制
				wg.Add(1)
				// 兑换码奖品
				var err error
				var newMsg *model.Message
				res := &webapi_proto.SAGetMatchAwardCode{}
				task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
					// 获取兑换码
					pack := &webapi_proto.ASGetMatchAwardCode{
						Platform: mc.p.Platform,
						Snid:     mc.p.SnId,
						ItemID:   data.GetId(),
						Money:    data.GetNum(),
						Tel:      mc.p.Tel,
					}
					logger.Logger.Trace("GetMatchAwardCode ", pack)
					var buff []byte
					buff, err = webapi.APIGetMatchAwardCode(common.GetAppId(), pack)
					if err != nil {
						return err
					}
					if err = proto.Unmarshal(buff, res); err != nil {
						return err
					}

					if res.GetCode() == "" || res.GetTag() != webapi_proto.TagCode_SUCCESS {
						return nil
					}

					var params []string
					for range []string{"zh", "vi", "en", "kh"} {
						params = append(params, res.GetMoney(), res.GetCode()) // 金额，兑换码
					}
					// 发送邮件
					title := i18n.Tr("languages", "MatchAwardTitle")
					content := i18n.Tr("languages", "MatchAward", params)
					newMsg = model.NewMessage("", 0, "", mc.p.SnId, model.MSGTYPE_RANK_REWARD,
						title, content, 0, 0, model.MSGSTATE_UNREAD, time.Now().Unix(), 0, "", nil, mc.p.Platform, model.HallTienlen)
					err := model.InsertMessage(mc.p.Platform, newMsg)
					if err != nil {
						logger.Logger.Errorf("发送邮件失败 snid:%v itemID:%v err:%v", mc.p.SnId, data.Id, err)
						return err
					}
					return nil
				}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
					defer wg.Done()
					if err != nil || res.GetCode() == "" || res.GetTag() != webapi_proto.TagCode_SUCCESS {
						logger.Logger.Errorf("获取兑换码失败 snid:%v itemID:%v res:%v err:%v", mc.p.SnId, data.Id, res, err)
						return
					}
					item.Code = res.GetCode()
					item.Msg = res.GetMoney()
					p := PlayerMgrSington.GetPlayerBySnId(mc.p.SnId)
					if p != nil {
						p.AddMessage(newMsg)
					}
				}), fmt.Sprintf("RankAward%d", mc.p.SnId)).Start()
			}
		}
	}

	if !has {
		sendFunc()
		return
	}
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		wg.Wait()
		return nil
	}), task.CompleteNotifyWrapper(func(i interface{}, t task.Task) {
		sendFunc()
	})).Start()
}

// 更新段位
func (this *Tournament) CheckAddMatchSeasonLv(mc *PlayerMatchContext) {
	if mc == nil {
		return
	}
	platform := mc.p.Platform
	if platform == DefaultPlatform {
		return
	}
	rank := mc.rank
	maxPlayerNum := mc.tm.gmd.MatchNumebr
	upLine := maxPlayerNum * 33 / 100
	downLine := maxPlayerNum * 67 / 100
	snid := mc.p.SnId
	ms := MatchSeasonMgrSington.GetMatchSeason(mc.p.SnId)
	msid := MatchSeasonMgrSington.GetMatchSeasonId(platform)
	if msid == nil {
		MatchSeasonMgrSington.UpdateMatchSeasonId(platform)
	}
	if ms == nil {
		task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
			ret, err := model.QueryMatchSeasonBySnid(platform, snid)
			if err != nil {
				return nil
			}
			return ret
		}), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) {
			var ret *model.MatchSeason
			dirty := false
			seasonId := int32(1)
			if msid != nil {
				seasonId = msid.SeasonId
			}
			if data == nil || data.(*model.MatchSeason) == nil {
				player := PlayerMgrSington.GetPlayerBySnId(snid)
				if player != nil {
					ret = model.NewMatchSeason(player.Platform, snid, player.Name, seasonId, 1)
					dirty = true
				} else {
					logger.Logger.Error("CSTMSeasonInfoHandler error: player == nil or  msi == nil", snid)
				}
			} else {
				ret = data.(*model.MatchSeason)
				if ret.SeasonId < seasonId { //不同赛季段位继承
					num := seasonId - ret.SeasonId
					finalLv := ret.Lv
					for i := 0; i < int(num); i++ { //继承几次
						if i == int(num)-1 { //上个赛季
							ret.LastLv = finalLv
						}
						finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv)
					}
					ret.Lv = finalLv
					ret.SeasonId = seasonId
					ret.IsAward = false
					dirty = true
				}
			}
			ms = MatchSeasonMgrSington.exchangeModel2Cache(ret)
			ms.dirty = dirty
			MatchSeasonMgrSington.SetMatchSeason(ms)
			if rank <= upLine { //加分
				MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty)
			} else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分
				MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty)
			} else {
				MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty)
			}
		})).StartByFixExecutor("SnId:" + strconv.Itoa(int(snid)))
	} else {
		dirty := false
		if ms.SeasonId < msid.SeasonId { //不同赛季段位继承
			num := msid.SeasonId - ms.SeasonId
			finalLv := ms.Lv
			for i := 0; i < int(num); i++ { //继承几次
				if i == int(num)-1 { //上个赛季
					ms.LastLv = finalLv
				}
				finalLv = MatchSeasonMgrSington.MatchSeasonInherit(finalLv)
			}
			ms.Lv = finalLv
			ms.SeasonId = msid.SeasonId
			ms.IsAward = false
			ms.UpdateTs = time.Now().Unix()
			ms.dirty = true
			dirty = true
			MatchSeasonMgrSington.SetMatchSeason(ms)
		}
		if rank <= upLine { //加分
			MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 1, dirty)
		} else if rank >= downLine && ms.Lv > 75 { //白银以上才触发减分
			MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, -1, dirty)
		} else {
			MatchSeasonMgrSington.UpdateMatchSeasonLv(mc.p, 0, dirty)
		}
	}
}

// 比赛结束
func (this *Tournament) StopMatch(tmId, sortId int32) {
	//房间清理
	if this.matches[tmId] != nil && this.matches[tmId][sortId] != nil {
		this.matches[tmId][sortId].Stop()
		delete(this.matches[tmId], sortId)
	}
	//数据清理
	delete(this.players, sortId)
	delete(this.copyPlayers, sortId)
	delete(this.rankPlayers, sortId)
	delete(this.roundOverPlayerNum, sortId)
	delete(this.finalPerRank, sortId)
}

func (this *Tournament) getRank(sortId, round, snid int32, isFinals bool) int32 {
	if _, ok := this.rankPlayers[sortId]; ok {
		if rps, ok1 := this.rankPlayers[sortId][round]; ok1 {
			MatchContextSlice(rps).Sort(isFinals)
			for _, rp := range rps {
				if rp.p.SnId == snid {
					return rp.rank
				}
			}
		}
	}
	return 0
}

func (this *Tournament) isOut(sortId, snid int32) bool {
	out := false
	if this.finalPerRank[sortId] != nil {
		for _, info := range this.finalPerRank[sortId] {
			if info.SnId == snid {
				out = true
			}
		}
	}
	return out
}

func (this *Tournament) GetTm(sortId int32) *TmMatch {
	if this.matches != nil {
		for _, sortTms := range this.matches {
			for id, match := range sortTms {
				if id == sortId {
					return match
				}
			}
		}
	}
	return nil
}

// 玩家比赛结束 更新积分
func (this *Tournament) UpdateMatchInfo(p *Player, sortId, grade, isWin int32, matchRobotGrades map[int32]int32) {
	logger.Logger.Trace("=========== UpdateMatchInfo ==============")
	logger.Logger.Tracef("UpdateMatchInfo: sortId:%v, grade:%v, isWin: %v, matchRobotGrades:%v", sortId, grade, isWin, matchRobotGrades)
	if _, ok := this.players[sortId]; !ok {
		this.players[sortId] = make(map[int32]*PlayerMatchContext)
	}
	if mtp, ok := this.players[sortId][p.SnId]; ok {
		mtp.grade = grade
		if this.roundOverPlayerNum[sortId] == nil {
			this.roundOverPlayerNum[sortId] = make(map[int32]int32)
		}
		this.roundOverPlayerNum[sortId][mtp.round]++
		if mtp.record == nil {
			mtp.record = make(map[int32]int32)
		}
		mtp.record[isWin]++
		//轮数增加
		mtp.round++
		mtp.gaming = false
		if this.copyPlayers[sortId] == nil {
			this.copyPlayers[sortId] = make(map[int32][]*PlayerMatchContext)
		}
		var mc PlayerMatchContext
		mc = *mtp
		this.copyPlayers[sortId][mtp.round] = append(this.copyPlayers[sortId][mtp.round], &mc)
		if this.rankPlayers[sortId] == nil {
			this.rankPlayers[sortId] = make(map[int32][]*PlayerMatchContext)
		}
		this.rankPlayers[sortId][mtp.round] = append(this.rankPlayers[sortId][mtp.round], &mc)
		logger.Logger.Tracef("========snid(%v)   grade(%v) ============", p.SnId, grade)
		if mtp.tm.tmpUseRobot == MatchUseRobot { // 使用机器人
			this.NextRoundStartSingle(sortId, mtp, matchRobotGrades)
		} else {
			this.NextRoundStart(sortId, mtp)
		}
	}
}

func (this *Tournament) CreatePlayerMatchContext(p *Player, m *TmMatch, seq int) *PlayerMatchContext {
	ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId)
	var lv int32
	if ms != nil {
		lv = ms.Lv
	}
	roleId := int32(2000001)
	if p.Roles != nil {
		roleId = p.Roles.ModId
	}

	mc := NewMatchContext(p, m, 1000, p.SnId, lv, roleId, seq)
	if mc != nil {
		if this.players[m.SortId] == nil {
			this.players[m.SortId] = make(map[int32]*PlayerMatchContext)
		}
		this.players[m.SortId][p.SnId] = mc
		p.matchCtx = mc
		return mc
	}
	return nil
}

// GetSCTMInfosPack 发送比赛配置数据
func (this *Tournament) GetSCTMInfosPack(platform, channelName string) *tournament.SCTMInfos {
	pack := &tournament.SCTMInfos{}
	matchInfo := this.GetAllMatchInfo(platform)
	if matchInfo != nil {
		for id, info := range matchInfo {
			if info.MatchSwitch == 1 && !this.IsOutTime(info) && common.InMatchChannel(info.OnChannelName, channelName) {
				tMInfo := &tournament.TMInfo{
					Id:                proto.Int32(id),
					GameFreeId:        info.GameFreeId,
					MatchType:         info.MatchType,
					MatchName:         info.MatchName,
					MatchNumebr:       info.MatchNumebr,
					MatchSwitch:       info.MatchSwitch,
					SignupCostCoin:    info.SignupCostCoin,
					SignupCostDiamond: info.SignupCostDiamond,
					MatchTimeType:     info.MatchTimeType,
					MatchTimeStartHMS: info.MatchTimeStartHMS,
					MatchTimeEndHMS:   info.MatchTimeEndHMS,
					TitleURL:          info.TitleURL,
					AwardShow:         info.AwardShow,
					Rule:              info.Rule,
					SortId:            info.SortId,
					OnChannelName:     info.OnChannelName,
				}
				if info.MatchTimeWeek != nil && len(info.MatchTimeWeek) > 0 {
					for _, week := range info.MatchTimeWeek {
						tMInfo.MatchTimeWeek = append(tMInfo.MatchTimeWeek, week)
					}
				}
				if info.MatchTimeStamp != nil && len(info.MatchTimeStamp) > 0 {
					for _, stamp := range info.MatchTimeStamp {
						tMInfo.MatchTimeStamp = append(tMInfo.MatchTimeStamp, stamp)
					}
				}
				if info.MatchPromotion != nil {
					for _, mp := range info.MatchPromotion {
						tMInfo.MatchPromotion = append(tMInfo.MatchPromotion, mp)
					}
				}
				if info.Award != nil {
					for _, award := range info.Award {
						miAward := &tournament.MatchInfoAward{
							Coin:      award.Coin,
							Diamond:   award.Diamond,
							UpLimit:   award.UpLimit,
							DownLimit: award.DownLimit,
						}
						if award.ItemId != nil {
							for _, itemInfo := range award.ItemId {
								a := &tournament.ItemInfo{
									ItemId:  itemInfo.ItemId,
									ItemNum: int32(itemInfo.ItemNum),
									Name:    itemInfo.Name,
								}
								miAward.ItemInfo = append(miAward.ItemInfo, a)
							}
						}
						tMInfo.Award = append(tMInfo.Award, miAward)
					}
				}
				if info.SignupCostItem != nil && info.SignupCostItem.ItemNum > 0 {
					signupCost := &tournament.ItemInfo{
						ItemId:  info.SignupCostItem.ItemId,
						ItemNum: int32(info.SignupCostItem.ItemNum),
						Name:    info.SignupCostItem.Name,
					}
					tMInfo.SignupCostItem = signupCost
				}
				pack.TMInfo = append(pack.TMInfo, tMInfo)
			}
		}
	}
	return pack
}

func (this *Tournament) ModuleName() string {
	return "Tournament"
}

// 初始化
func (this *Tournament) Init() {
	logger.Logger.Trace("(this *Tournament) Init()")
}

func (this *Tournament) Update() {
	// 使用机器人的情况
	for snid, info := range this.singleSignupPlayers {
		if this.playerSignUpTime[snid] != 0 {
			matchInfo := this.GetMatchInfo(info.Platform, info.Tmid)
			if matchInfo.MatchSwitch == MatchSwitchStart && !this.IsOutTime(matchInfo) && this.IsUseRobot(info.Platform, info.Tmid) {
				needTime := this.playerWaitStart[snid]
				if time.Now().Unix()-this.playerSignUpTime[snid] > needTime {
					delete(this.singleSignupPlayers, snid)
					delete(this.playerSignUpTime, snid)
					signInfo := &SignInfo{
						signup:   make(map[int32]*TmPlayer),
						Platform: info.Platform,
						MaxCnt:   int(matchInfo.MatchNumebr),
					}
					seq := rand.Intn(int(matchInfo.MatchNumebr)) + 1
					signInfo.signup[snid] = &TmPlayer{SnId: snid, seq: seq}
					this.signUpPlayers[info.Platform][info.Tmid] = signInfo
					//倒计时结束 开始比赛
					this.Start(info.Platform, info.Tmid)
				}
			} else {
				this.CancelSignUpAll(info.Platform, info.Tmid)
			}
		}
	}
}

func (this *Tournament) Shutdown() {
}
func init() {
	module.RegisteModule(TournamentMgr, time.Second, 0)
}
