package thirteen

import (
	"strconv"
	"time"

	"mongo.games.com/goserver/core"
	"mongo.games.com/goserver/core/logger"

	"mongo.games.com/game/common"
	rule "mongo.games.com/game/gamerule/thirteen"
	"mongo.games.com/game/gamesrv/base"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	"mongo.games.com/game/protocol/thirteen"
)

var PolicyThirteenSingleton = &PolicyThirteen{}

type PolicyThirteen struct {
	base.BaseScenePolicy
	states [rule.ThirteenWaterSceneStateMax]base.SceneState
}

func (this *PolicyThirteen) CreateSceneExData(s *base.Scene) interface{} {
	sceneEx := NewThirteenWaterSceneData(s)
	if sceneEx != nil {
		if sceneEx.init() {
			s.ExtraData = sceneEx
		}
	}
	return sceneEx
}

func (this *PolicyThirteen) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} {
	playerEx := &PlayerEx{Player: p}
	if playerEx != nil {
		p.ExtraData = playerEx
	}
	return playerEx
}

func (this *PolicyThirteen) OnStart(s *base.Scene) {
	logger.Logger.Trace("(this *PolicyThirteen) OnStart, sceneId=", s.GetSceneId())

	sceneEx := NewThirteenWaterSceneData(s)
	if sceneEx != nil {
		if sceneEx.init() {
			s.ExtraData = sceneEx
			s.ChangeSceneState(rule.ThirteenWaterSceneStateWait)
		}
	}
}

func (this *PolicyThirteen) OnStop(s *base.Scene) {
	logger.Logger.Trace("(this *PolicyThirteen) OnStop , sceneId=", s.GetSceneId())
}

func (this *PolicyThirteen) OnTick(s *base.Scene) {
	if s == nil {
		return
	}
	if s.SceneState != nil {
		s.SceneState.OnTick(s)
	}
}

func (this *PolicyThirteen) OnPlayerEnter(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerEnter, sceneId=", s.GetSceneId(), " player=", p.SnId)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		pos := -1
		for i := 0; i < sceneEx.GetPlayerNum(); i++ {
			if sceneEx.seats[i] == nil {
				pos = i
				break
			}
		}
		if pos != -1 {
			playerEx := &PlayerEx{Player: p}
			sceneEx.seats[pos] = playerEx
			sceneEx.players[p.SnId] = playerEx
			p.Pos = pos
			p.ExtraData = playerEx
			playerEx.Clear()
			if sceneEx.Gaming {
				p.MarkFlag(base.PlayerState_WaitNext)
				p.UnmarkFlag(base.PlayerState_Ready)
			}
			//send msg
			if len(s.Players) > 1 {
				//向其他人广播玩家进入信息
				pack := &thirteen.SCThirteenPlayerEnter{
					Data: &thirteen.ThirteenPlayerData{
						SnId:      proto.Int32(p.SnId),
						Name:      proto.String(p.Name),
						Head:      proto.Int32(p.Head),
						Sex:       proto.Int32(p.Sex),
						Coin:      proto.Int64(p.Coin),
						Pos:       proto.Int(pos),
						Flag:      proto.Int(p.GetFlag()),
						Longitude: proto.Int32(p.Longitude),
						Latitude:  proto.Int32(p.Latitude),
						City:      proto.String(p.City),
						//Params:    proto.String(p.Params),
						AgentCode:   proto.String(p.AgentCode),
						HeadOutLine: proto.Int32(p.HeadOutLine),
						VIP:         proto.Int32(p.VIP),
						RoleId:      p.PlayerData.GetRoleId(),
					},
				}
				proto.SetDefaults(pack)
				logger.Logger.Trace("SCThirteenWaterPlayerEnter:", pack)
				s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), pack, p.GetSid())
			}
			//给自己发送房间信息
			this.SendRoomInfo(s, p, sceneEx)
			s.FirePlayerEvent(p, base.PlayerEventEnter, nil)
		}
	}
}

func (this *PolicyThirteen) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) {
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerLeave, sceneId=", s.GetSceneId(), " player=", p.SnId)
	if s == nil || p == nil {
		return
	}
	if !this.CanChangeCoinScene(s, p) {
		return
	}
	sceneEx, ok := s.ExtraData.(*SceneEx)
	if !ok {
		return
	}
	playerEx, ok := p.ExtraData.(*PlayerEx)
	if !ok {
		return
	}
	if !playerEx.IsGameing() {
		sceneEx.OnPlayerLeave(p, reason)
		s.FirePlayerEvent(p, base.PlayerEventLeave, nil)
		return
	}

	isBilled := false
	switch sceneEx.GetSceneState().GetState() {
	case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateOptCard:
		// 发牌，选牌，离场扣分
		n := sceneEx.GetLeaveDeductCoin()
		if playerEx.Coin < n { // 不够扣，不能离开
			return
		}
		playerEx.AddCoin(-n, common.GainWay_LeaveDeduct, base.SyncFlag_ToClient, "system", s.GetSceneName())
		sceneEx.LeaveNum++

	case rule.ThirteenWaterSceneStateShowCards, rule.ThirteenWaterSceneStateHit:
		// 亮牌，打枪，提前结算；（重连和结算使用备份数据）
		isBilled = true
		if playerEx.gainCoin > 0 {
			//税前赢的钱 税收 下注额
			playerEx.AddServiceFee(playerEx.taxCoin)
			playerEx.AddCoin(int64(playerEx.gainCoin), common.GainWay_CoinSceneWin, base.SyncFlag_ToClient, "system", s.GetSceneName())
			playerEx.CurIsWin = 1
		} else if playerEx.gainCoin < 0 {
			playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneLost, base.SyncFlag_ToClient, "system", s.GetSceneName())
			playerEx.CurIsWin = -1
		}
		// 离场补偿分
		if playerEx.score[6] > 0 {
			playerEx.AddCoin(playerEx.score[6]*int64(sceneEx.GetBaseScore()), common.GainWay_LeaveCombat, base.SyncFlag_ToClient, "system", s.GetSceneName())
		}

		if playerEx.IsGameing() && !playerEx.IsRob {
			totalin, totalout := int64(0), int64(0)
			if playerEx.CurIsWin > 0 {
				totalout = playerEx.gainCoin + playerEx.taxCoin
			} else {
				totalin -= playerEx.gainCoin
			}
			validFlow := totalin + totalout
			validBet := common.AbsI64(totalin - totalout)
			sceneEx.SaveGamePlayerListLog(playerEx.SnId, base.GetSaveGamePlayerListLogParam(playerEx.Platform, playerEx.Channel, playerEx.BeUnderAgentCode,
				playerEx.PackageID, sceneEx.logid, playerEx.InviterId, totalin, totalout, playerEx.taxCoin,
				0, 0, playerEx.gainCoin, validBet, validFlow, sceneEx.IsPlayerFirst(sceneEx.GetPlayer(playerEx.SnId)), false))

			sceneEx.Statistics(&base.StaticParam{
				SnId:           playerEx.SnId,
				Gain:           playerEx.gainCoin,
				GainTax:        playerEx.taxCoin,
				IsAddTimes:     true,
				HasRobotGaming: sceneEx.robotNum > 0,
			})
		}
	}

	// 游戏已开始但未结算，玩家离开，备份玩家数据
	if sceneEx.Gaming && playerEx.IsGameing() {
		sceneEx.BackupPlayer(playerEx, isBilled)
	}

	// 清理玩家数据
	sceneEx.OnPlayerLeave(p, reason)
	s.FirePlayerEvent(p, base.PlayerEventLeave, nil)
}

func (this *PolicyThirteen) OnPlayerDropLine(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId)
	s.FirePlayerEvent(p, base.PlayerEventDropLine, nil)
}

func (this *PolicyThirteen) OnPlayerRehold(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if _, ok := p.ExtraData.(*PlayerEx); ok {
			//发送房间信息给自己
			if p.IsGameing() {
				p.MarkFlag(base.PlayerState_Ready)
			}
			this.SendRoomInfo(s, p, sceneEx)
			s.FirePlayerEvent(p, base.PlayerEventRehold, nil)
		}
	}
}

func (this *PolicyThirteen) OnPlayerReturn(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerRehold, sceneId=", s.GetSceneId(), " player=", p.SnId)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if _, ok := p.ExtraData.(*PlayerEx); ok {
			//发送房间信息给自己
			if p.IsGameing() {
				p.MarkFlag(base.PlayerState_Ready)
			}
			this.SendRoomInfo(s, p, sceneEx)
			s.FirePlayerEvent(p, base.PlayerEventReturn, nil)
		}
	}
}

func (this *PolicyThirteen) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if s == nil || p == nil {
		return false
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerOp, sceneId=", s.GetSceneId(), " player=", p.SnId, " opcode=", opcode, " params=", params)
	if s.SceneState != nil {
		p.LastOPTimer = time.Now()
		p.Trusteeship = 0
		return s.SceneState.OnPlayerOp(s, p, opcode, params)
	}
	return true
}

func (this *PolicyThirteen) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " eventcode=", evtcode, " params=", params)
	if s.SceneState != nil {
		s.SceneState.OnPlayerEvent(s, p, evtcode, params)
	}
}

func (this *PolicyThirteen) OnAudienceEnter(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnAudienceEnter, sceneId=", s.GetSceneId(), " player=", p.SnId)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		//给自己发送房间信息
		this.SendRoomInfo(s, p, sceneEx)
		s.FirePlayerEvent(p, base.AudienceEventEnter, nil)
	}
}

func (this *PolicyThirteen) OnAudienceLeave(s *base.Scene, p *base.Player, reason int) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnAudienceLeave, sceneId=", s.GetSceneId(), " player=", p.SnId)
	s.FirePlayerEvent(p, base.AudienceEventLeave, nil)
}

func (this *PolicyThirteen) OnAudienceDropLine(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnAudienceDropLine, sceneId=", s.GetSceneId(), " player=", p.SnId)
	s.AudienceLeave(p, common.PlayerLeaveReason_DropLine)
	s.FirePlayerEvent(p, base.AudienceEventDropLine, nil)
}

func (this *PolicyThirteen) OnAudienceSit(s *base.Scene, p *base.Player) {
	//十三水观众坐下
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *PolicyThirteen) OnAudienceSit, sceneId=", s.GetSceneId(), " player=", p.GetName())
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {

		pos := p.Pos
		if pos != -1 && sceneEx.seats[pos] == nil {
			playerEx := &PlayerEx{Player: p}
			sceneEx.seats[pos] = playerEx
			sceneEx.players[p.SnId] = playerEx
			p.Pos = pos
			p.ExtraData = playerEx
			playerEx.Clear()
			if sceneEx.Gaming {
				p.MarkFlag(base.PlayerState_WaitNext)
				p.UnmarkFlag(base.PlayerState_Ready)
				p.SyncFlag()
			}
			//send msg
			if len(s.Players) > 1 {
				//向其他人广播玩家进入信息
				pack := &thirteen.SCThirteenPlayerEnter{
					Data: &thirteen.ThirteenPlayerData{
						SnId:        proto.Int32(p.SnId),
						Name:        proto.String(p.Name),
						Head:        proto.Int32(p.Head),
						Sex:         proto.Int32(p.Sex),
						Coin:        proto.Int64(p.Coin),
						Pos:         proto.Int(pos),
						Flag:        proto.Int(p.GetFlag()),
						Longitude:   proto.Int32(p.Longitude),
						Latitude:    proto.Int32(p.Latitude),
						City:        proto.String(p.City),
						HeadOutLine: proto.Int32(p.HeadOutLine),
						VIP:         proto.Int32(p.VIP),
						RoleId:      p.PlayerData.GetRoleId(),
					},
				}
				proto.SetDefaults(pack)
				s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerEnter), pack, p.GetSid())
			}
			//给自己发送房间信息
			this.SendRoomInfo(s, p, sceneEx)
			//sceneEx.CheckAndSendDisbandInfo(p)
		}
		s.FirePlayerEvent(p, base.PlayerEventEnter, nil)
	}
}

func (this *PolicyThirteen) IsCompleted(s *base.Scene) bool {
	if s == nil {
		return false
	}
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		return !sceneEx.Gaming
	}
	return false
}

func (this *PolicyThirteen) IsCanForceStart(s *base.Scene) bool {
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		return len(s.Players) >= 2 && !sceneEx.Gaming
	}
	return false
}

func (this *PolicyThirteen) ForceStart(s *base.Scene) {
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if sceneEx.SceneState.GetState() == rule.ThirteenWaterSceneStateWait {
			s.ChangeSceneState(rule.ThirteenWaterSceneStateStart)
		}
	}
}

func (this *PolicyThirteen) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	if s == nil || p == nil {
		return false
	}
	if s.SceneState != nil {
		return s.SceneState.CanChangeCoinScene(s, p)
	}
	return false
}

func (this *PolicyThirteen) SendRoomInfo(s *base.Scene, p *base.Player, sceneEx *SceneEx) {
	pack := sceneEx.ThirteenWaterCreateRoomInfoPacket(s, p)
	p.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomInfo), pack)
}

func ThirteenWaterBroadcastRoomState(s *base.Scene, params ...int32) {
	pack := &thirteen.SCThirteenRoomState{
		State:  proto.Int(s.SceneState.GetState()),
		Params: params,
	}
	s.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenRoomState), pack, 0)
}

//=====================================
// BaseState 状态基类
//=====================================

type BaseState struct {
}

func (this *BaseState) GetTimeout(s *base.Scene) int {
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		return int(time.Now().Sub(sceneEx.StateStartTime) / time.Second)
	}
	return 0
}

func (this *BaseState) CanChangeTo(s base.SceneState) bool {
	return true
}

func (this *BaseState) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	sceneEx, ok := s.ExtraData.(*SceneEx)
	if !ok {
		return false
	}
	playerEx, ok := p.ExtraData.(*PlayerEx)
	if !ok {
		return false
	}

	if !playerEx.IsGameing() {
		return true
	}

	switch sceneEx.GetSceneState().GetState() {
	case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateOptCard:
		if playerEx.Coin < sceneEx.GetLeaveDeductCoin() { // 不够扣，不能离开
			return false
		}
	}
	return true
}

func (this *BaseState) OnEnter(s *base.Scene) {
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		sceneEx.StateStartTime = time.Now()
	}
}

func (this *BaseState) OnLeave(s *base.Scene) {}

func (this *BaseState) OnTick(s *base.Scene) {}

func (this *BaseState) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	playerEx, ok := p.ExtraData.(*PlayerEx)
	if !ok {
		return false
	}

	switch opcode {
	case rule.ThirteenWaterPlayerOpStandup:
		if len(params) > 0 {
			if params[0] == 0 {
				playerEx.isStand = true
			} else {
				playerEx.isStand = false
			}
			pack := &thirteen.SCThirteenPlayerOp{
				OpRetCode: thirteen.OpResultCode_OPRC_Sucess,
				OpCode:    int32(opcode),
				OpParam:   params,
				Pos:       int32(playerEx.GetPos()),
			}
			playerEx.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack)
			logger.Logger.Trace("TWMmoPacketID_PACKET_SCThirteenPlayerOp:", pack)
		}
		return true
	case rule.ThirteenWaterPlayerOpTest:
		if !common.Config.IsDevMode {
			return false
		}
		sceneEx, ok := s.ExtraData.(*SceneEx)
		if !ok {
			return false
		}
		sceneEx.testPokers = make([]int64, len(params))
		copy(sceneEx.testPokers, params)
		return true
	}

	return false
}

func (this *BaseState) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
}

//=====================================
// StateWait 匹配中
//=====================================

type StateWait struct {
	BaseState
}

func (this *StateWait) GetState() int {
	return rule.ThirteenWaterSceneStateWait
}

func (this *StateWait) CanChangeTo(s base.SceneState) bool {
	if s.GetState() == rule.ThirteenWaterSceneStateStart {
		return true
	}
	return false
}

func (this *StateWait) GetTimeout(s *base.Scene) int {
	return 0
}

func (this *StateWait) OnEnter(s *base.Scene) {
	this.BaseState.OnEnter(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if s.Gaming {
			s.NotifySceneRoundPause()
		}
		s.Gaming = false
		ThirteenWaterBroadcastRoomState(s)
		if sceneEx.CanStart() {
			s.ChangeSceneState(rule.ThirteenWaterSceneStateStart)
		}
	}
}

// 玩家事件
func (this *StateWait) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *StateWait) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode)
	this.BaseState.OnPlayerEvent(s, p, evtcode, params)
	if _, ok := s.ExtraData.(*SceneEx); ok {
		switch evtcode {
		case base.PlayerEventLeave:
		case base.PlayerEventEnter:
			if !p.IsReady() {
				p.MarkFlag(base.PlayerState_Ready)
				p.SyncFlag()
			}
		}
	}
}

func (this *StateWait) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if s.CheckNeedDestroy() {
			sceneEx.SceneDestroy(true)
			return
		}
		if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterSceneWaitTimeout {
			//切换到准备开局状态
			if sceneEx.CanStart() {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateStart)
			} else if s.GetRobotNum() == 1 {
				tNow := time.Now()
				for _, p := range s.Players {
					if p.IsRob && tNow.Sub(p.GetLastOPTimer()) > time.Second*time.Duration(30+s.Rand.Int63n(60)) {
						//p.LastOPTimer = tNow.Add(time.Minute * 30)
						s.PlayerLeave(p, common.PlayerLeaveReason_Normal, true)
					}
				}
			}
		}
	}
}

//=====================================
// StateStart 开始倒计时
//=====================================

type StateStart struct {
	BaseState
}

func (this *StateStart) GetState() int {
	return rule.ThirteenWaterSceneStateStart
}

func (this *StateStart) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateSendCards, rule.ThirteenWaterSceneStateBilled, rule.ThirteenWaterSceneStateWait:
		return true
	}
	return false
}

func (this *StateStart) OnEnter(s *base.Scene) {
	this.BaseState.OnEnter(s)
	if _, ok := s.ExtraData.(*SceneEx); ok {
		ThirteenWaterBroadcastRoomState(s)
		s.Gaming = false
	}
}

func (this *StateStart) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterStartTimeout {
			if sceneEx.Creator != 0 && sceneEx.GetRealPlayerNum() == 0 {
				sceneEx.Destroy(true)
				return
			}
			//切换到等待操作状态
			if sceneEx.CanStart() {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateSendCards)
			} else {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateWait)
			}
		}
	}
}

func (this *StateStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *StateStart) OnPlayerEvent, sceneId=", s.GetSceneId(), " player=", p.SnId, " evtcode=", evtcode)
	this.BaseState.OnPlayerEvent(s, p, evtcode, params)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		switch evtcode {
		case base.PlayerEventLeave:
			if !sceneEx.CanStart() {
				logger.Logger.Tracef("(this *StateStart) OnPlayerEvent s.ChangeSceneState(ThirteenWaterSceneStateWait) %v", s.GetSceneId())
				s.ChangeSceneState(rule.ThirteenWaterSceneStateWait)
			}
		case base.PlayerEventEnter:
			if !p.IsReady() {
				p.MarkFlag(base.PlayerState_Ready)
				p.SyncFlag()
			}
		}
	}
}

//=====================================
// StateSendCard 发牌状态
//=====================================

type StateSendCard struct {
	BaseState
}

func (this *StateSendCard) GetState() int {
	return rule.ThirteenWaterSceneStateSendCards
}

func (this *StateSendCard) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateOptCard, rule.ThirteenWaterSceneStateWait:
		return true
	}
	return false
}

func (this *StateSendCard) OnEnter(s *base.Scene) {
	logger.Logger.Tracef("(this *StateSendCard) OnEnter, sceneid=%v", s.GetSceneId())
	this.BaseState.OnEnter(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		s.Gaming = true
		sceneEx.GameNowTime = time.Now()
		sceneEx.logid, _ = model.AutoIncGameLogId()
		//通知world开始第几局
		sceneEx.NumOfGames++
		s.NotifySceneRoundStart(sceneEx.NumOfGames)
		//开始记录录像
		//if !s.Testing && s.rr != nil {
		//	pack := sceneEx.ThirteenWaterCreateRoomInfoPacket(s, nil)
		//	if pack != nil {
		//		s.rr.Init(int(thirteen.MmoPacketID_PACKET_SC_WINTHREE_ROOMINFO), pack)
		//	}
		//}

		sceneEx.Clear()
		sceneEx.GetInGameNum()
		ThirteenWaterBroadcastRoomState(s, int32(sceneEx.NumOfGames))
		if len(sceneEx.testPokers) > 0 {
			// 调试
			for _, v := range sceneEx.seats {
				if v == nil || !v.IsGameing() {
					continue
				}
				if len(sceneEx.testPokers) >= 13 {
					for k, vv := range sceneEx.testPokers[:13] {
						v.cards[k] = int(vv)
					}
					v.allGroup = sceneEx.logic.Suggest(v.cards)
					sceneEx.testPokers = sceneEx.testPokers[13:]
				} else {
					sceneEx.testPokers = nil
					v.cards = sceneEx.poker.Get13Crads()
				}
			}
		} else {
			//for i := 0; i < 10; i++ {
			//	isHaveQingLong := false
			//	lessCoin := 0
			//	isF := false
			//	for _, player := range sceneEx.players {
			//		if player != nil && player.IsGameing() {
			//			if isF && !player.IsRob {
			//				player.cards = sceneEx.GetRandsType()
			//				isF = false
			//			} else {
			//				player.cards = sceneEx.poker.Get13Crads()
			//			}
			//			player.allGroup = sceneEx.logic.Suggest(player.cards)
			//			//todo 遇到清龙重新发牌 十次之后 不处理
			//			for k, v := range player.allGroup {
			//				if k == 1000 || v.PokerType == 1 {
			//					isHaveQingLong = true
			//					break
			//				}
			//			}
			//			if player.Coin < 108*int64(sceneEx.GetBaseScore()) {
			//				lessCoin++
			//			}
			//		}
			//	}
			//	if isHaveQingLong && lessCoin > 0 {
			//		sceneEx.poker.Init()
			//	} else {
			//		break
			//	}
			//}
			sceneEx.SendHandCardOdds()
		}
		sceneEx.SendToPlayerCards(s)
	}
}

func (this *StateSendCard) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterSendCardsTimeout {
			if sceneEx.CheckNeedDestroy() {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateWait)
			} else {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateOptCard)
			}
		}
	}
}

//=====================================
// StateOp 选牌状态
//=====================================

type StateOp struct {
	BaseState
}

func (this *StateOp) GetState() int {
	return rule.ThirteenWaterSceneStateOptCard
}

func (this *StateOp) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateShowCards:
		return true
	}
	return false
}

// 状态进入时
func (this *StateOp) OnEnter(s *base.Scene) {
	logger.Logger.Tracef("(this *StateOp) OnEnter, sceneid=%v", s.GetSceneId())
	this.BaseState.OnEnter(s)
	ThirteenWaterBroadcastRoomState(s)
}

// 玩家操作
func (this *StateOp) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	logger.Logger.Infof("(this *StateOp) OnPlayerOp p.snid (%v) opcode (%v)", p.SnId, opcode)
	if this.BaseState.OnPlayerOp(s, p, opcode, params) {
		return true
	}

	sceneEx, ok := s.ExtraData.(*SceneEx)
	if !ok {
		return false
	}
	playerEx, ok := p.ExtraData.(*PlayerEx)
	if !ok {
		return false
	}

	returnFunc := func(code thirteen.OpResultCode) {
		pack := &thirteen.SCThirteenPlayerOp{
			OpRetCode: code,
			OpCode:    int32(opcode),
			OpParam:   params,
			Pos:       int32(playerEx.GetPos()),
		}
		if code == thirteen.OpResultCode_OPRC_Error {
			proto.SetDefaults(pack)
			logger.Logger.Trace("SCThirteenPlayerOp:", pack)
			playerEx.SendToClient(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack)
			return
		}
	}

	if !playerEx.IsGameing() {
		returnFunc(thirteen.OpResultCode_OPRC_Error)
		return true
	}

	switch opcode {
	case rule.ThirteenWaterPlayerOpMS: //确定牌
		//if playerEx.deterMine { // 确认牌型
		//	returnFunc(thirteen.OpResultCode_OPRC_Error)
		//	return false
		//}
		if len(params) == 13 {
			//校验牌
			a := rule.DelCards(playerEx.cards[:], common.Int64Toint(params))
			if len(a) != 0 {
				logger.Logger.Error("the cards is error.")
				returnFunc(thirteen.OpResultCode_OPRC_Error)
				return true
			}
			//牌赋值
			copy(playerEx.cardsO.Head[:], common.Int64Toint(params[:3]))
			copy(playerEx.cardsO.Mid[:], common.Int64Toint(params[3:8]))
			copy(playerEx.cardsO.End[:], common.Int64Toint(params[8:]))
			playerEx.cardsO.PokerType = 0
			sceneEx.SendSelectCards(playerEx, 0, int64(opcode))
		} else {
			sceneEx.SendSelectCards(playerEx, int(params[0]), int64(opcode))
		}
		playerEx.Trusteeship = 0
		playerEx.UnmarkFlag(base.PlayerState_Auto)
		playerEx.deterMine = true
		//如果所有玩家都确认牌之后 可以直接开始下一阶段
		a := true
		for _, v := range sceneEx.players {
			if v != nil && v.IsGameing() {
				if !v.deterMine {
					a = false
				}
			}
		}
		if a {
			//提前进入亮牌阶段
			s.ChangeSceneState(rule.ThirteenWaterSceneStateShowCards)
		}
	case rule.ThirteenWaterPlayerOpReset:
		// 取消确认
		playerEx.deterMine = false
		pack := &thirteen.SCThirteenPlayerOp{
			OpRetCode: thirteen.OpResultCode_OPRC_Sucess,
			OpCode:    int32(opcode),
			OpParam:   params,
			Pos:       int32(playerEx.GetPos()),
		}
		playerEx.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenPlayerOp), pack, 0)

	default:
		return false
	}
	return false
}

func (this *StateOp) OnLeave(s *base.Scene) {
	logger.Logger.Tracef("(this *StateOp) OnLeave, sceneid=%v", s.GetSceneId())
	this.BaseState.OnLeave(s)
	sceneEx, ok := s.ExtraData.(*SceneEx)
	if !ok {
		return
	}

	for _, player := range sceneEx.players {
		if player != nil && player.IsGameing() {
			if player.cardsO != nil && player.cardsO.PokerType != -1 {
				if player.cardsO.PokerType < 1000000 {
					player.isDP = sceneEx.logic.IsDP(player.cardsO.Head, player.cardsO.Mid, player.cardsO.End)
				}
				continue
			}
			//没有确定牌的玩家帮他确定牌
			sceneEx.SelectCards(player, -2)
			player.deterMine = true
			player.Trusteeship++
			// 标记托管状态
			player.MarkFlag(base.PlayerState_Auto)
		}
	}

	// test
	//type CC struct {
	//	Cards [13]int
	//	Card0 *rule.Group
	//	Coin  int64
	//}
	//if len(sceneEx.players) == 3 {
	//	sceneEx.Params[3] = 200000
	//	ls := []CC{
	//		{
	//			Cards: [13]int{3, 9, 30, 40, 39, 14, 18, 47, 27, 4, 19, 6, 22},
	//			Card0: &rule.Group{
	//				Head: [3]int{39, 18, 47},
	//				Mid:  [5]int{30, 4, 19, 6, 3},
	//				End:  [5]int{40, 27, 14, 22, 9},
	//				//PokerType: 100804,
	//			},
	//			Coin: 550900,
	//		},
	//		{
	//			Cards: [13]int{43, 53, 24, 34, 28, 36, 12, 20, 52, 5, 8, 25, 2},
	//			Card0: &rule.Group{
	//				Head: [3]int{36, 20, 5},
	//				Mid:  [5]int{28, 2, 34, 8, 43},
	//				End:  [5]int{25, 12, 53, 24, 52},
	//				//PokerType: 100803,
	//			},
	//			Coin: 560500,
	//		},
	//	}
	//	var i int
	//	for _, v := range sceneEx.players {
	//		if v.SnId != 33424700 {
	//			info := ls[i]
	//			v.cards = info.Cards
	//			v.cardsO = info.Card0
	//			v.Coin = info.Coin
	//			i++
	//		} else {
	//			// 真人
	//			v.cards = [13]int{10, 35, 16, 29, 45, 0, 33, 37, 1, 50, 46, 31, 44}
	//			v.cardsO = &rule.Group{
	//				Head: [3]int{10, 46, 44},
	//				Mid:  [5]int{50, 16, 45, 0, 1},
	//				End:  [5]int{37, 35, 33, 31, 29},
	//				//PokerType: 101005,
	//			}
	//			v.Coin = 8968339671
	//		}
	//	}
	//}

	//type CC struct {
	//	Cards [13]int
	//	Card0 *rule.Group
	//	Coin  int64
	//}
	//if len(sceneEx.players) == 3 {
	//	sceneEx.Params[3] = 200000
	//	ls := []CC{
	//		{
	//			Cards: [13]int{10, 21, 35, 34, 19, 31, 47, 15, 44, 2, 23, 18, 38},
	//			Card0: &rule.Group{
	//				Head: [3]int{35, 19, 38},
	//				Mid:  [5]int{44, 31, 18, 15, 2},
	//				End:  [5]int{47, 34, 21, 23, 10},
	//			},
	//			Coin: 179758402,
	//		},
	//		{
	//			Cards: [13]int{20, 3, 39, 14, 52, 25, 7, 53, 22, 16, 43, 30, 17},
	//			Card0: &rule.Group{
	//				Head: [3]int{3, 39, 7},
	//				Mid:  [5]int{25, 22, 20, 16, 14},
	//				End:  [5]int{43, 30, 17, 53, 52},
	//			},
	//			Coin: 993000,
	//		},
	//	}
	//	var i int
	//	for _, v := range sceneEx.players {
	//		if v.SnId != 33424700 {
	//			info := ls[i]
	//			v.cards = info.Cards
	//			v.cardsO = info.Card0
	//			v.Coin = info.Coin
	//			i++
	//		} else {
	//			// 真人
	//			v.cards = [13]int{12, 13, 9, 36, 49, 24, 32, 48, 4, 33, 6, 45, 50}
	//			v.cardsO = &rule.Group{
	//				Head: [3]int{13, 4, 33},
	//				Mid:  [5]int{48, 9, 49, 36, 12},
	//				End:  [5]int{45, 32, 6, 50, 24},
	//			}
	//			v.Coin = 1150000
	//		}
	//	}
	//}

	//type CC struct {
	//	Cards [13]int
	//	Card0 *rule.Group
	//	Coin  int64
	//}
	//if len(sceneEx.players) == 3 {
	//	sceneEx.Params[3] = 200000
	//	ls := []CC{
	//		{
	//			Cards: [13]int{18, 12, 3, 10, 9, 8, 19, 14, 51, 25, 28, 24, 33},
	//			Card0: &rule.Group{
	//				Head: [3]int{51, 28, 33},
	//				Mid:  [5]int{12, 10, 9, 8, 3},
	//				End:  [5]int{25, 24, 19, 18, 14},
	//			},
	//			Coin: 298348695,
	//		},
	//		{
	//			Cards: [13]int{26, 43, 44, 1, 0, 16, 50, 48, 34, 29, 17, 32, 37},
	//			Card0: &rule.Group{
	//				Head: [3]int{16, 50, 48},
	//				Mid:  [5]int{44, 1, 43, 17, 0},
	//				End:  [5]int{37, 34, 32, 29, 26},
	//			},
	//			Coin: 263600,
	//		},
	//	}
	//	var i int
	//	for _, v := range sceneEx.players {
	//		if v.SnId != 33424700 {
	//			info := ls[i]
	//			v.cards = info.Cards
	//			v.cardsO = info.Card0
	//			v.Coin = info.Coin
	//			i++
	//		} else {
	//			// 真人
	//			v.cards = [13]int{20, 47, 2, 36, 38, 39, 6, 15, 46, 21, 40, 52, 7}
	//			v.cardsO = &rule.Group{
	//				Head: [3]int{39, 6, 40},
	//				Mid:  [5]int{15, 2, 47, 21, 38},
	//				End:  [5]int{46, 20, 7, 52, 36},
	//			}
	//			v.Coin = 586000
	//		}
	//	}
	//}

	// test

	// 计算总分数
	sceneEx.CountScore()
	// 预结算，玩家余额在结算状态修改
	sceneEx.CountBilled()
}

func (this *StateOp) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.GetBaiPai() {
			s.ChangeSceneState(rule.ThirteenWaterSceneStateShowCards)
		}
	}
}

//=====================================
// StateOp 看牌状态
//=====================================

type StateShow struct {
	BaseState
}

func (this *StateShow) GetState() int {
	return rule.ThirteenWaterSceneStateShowCards
}

func (this *StateShow) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateShowCards, rule.ThirteenWaterSceneStateHit, rule.ThirteenWaterSceneStateBilled: //玩家操作状态
		return true
	}
	return false
}

// 状态进入
func (this *StateShow) OnEnter(s *base.Scene) {
	this.BaseState.OnEnter(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		ThirteenWaterBroadcastRoomState(s)
		logger.Logger.Tracef("(this *StateShow) OnEnter, sceneid=%v currpos:%v", s.GetSceneId(), sceneEx.currOpPos)
		sceneEx.ShowCards()
		// 每人看牌5秒，特殊牌型不算;
		var n int
		var has bool
		for _, v := range sceneEx.players {
			if v != nil && v.IsGameing() && v.cardsO != nil {
				n++
				if v.cardsO.PokerType == 1 { // 有青龙
					has = true
				}
			}
		}
		n -= sceneEx.specialTypeNum
		sceneEx.specialTime = time.Second * time.Duration(n*5)
		// pk动画 2秒
		sceneEx.specialTime += time.Second * 2
		// 特殊牌型 4.5秒；至尊青龙5.5秒
		if sceneEx.specialTypeNum > 0 {
			if has {
				sceneEx.specialTime += time.Millisecond * 5500
			} else {
				sceneEx.specialTime += time.Millisecond * 4500
			}
		}
		logger.Logger.Tracef("show cards: %v %v", n, sceneEx.specialTime)
		if sceneEx.specialTime <= 0 {
			sceneEx.specialTime = time.Second
		}
	}
}

func (this *StateShow) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.specialTime {
			if sceneEx.entryHitState {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateHit)
			} else {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateBilled)
			}
		}
	}
}

//=====================================
// StateHit 打枪状态
//=====================================

type StateHit struct {
	BaseState
}

func (this *StateHit) GetState() int {
	return rule.ThirteenWaterSceneStateHit
}

func (this *StateHit) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateBilled:
		return true
	}
	return false
}

// 状态进入
func (this *StateHit) OnEnter(s *base.Scene) {
	this.BaseState.OnEnter(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		ThirteenWaterBroadcastRoomState(s)
		logger.Logger.Tracef("(this *StateHit) OnEnter, sceneid=%v currpos:%v", s.GetSceneId(), sceneEx.currOpPos)

		hitNum := 0 //打枪人数
		for _, playerEx := range sceneEx.players {
			if playerEx != nil && playerEx.IsGameing() {
				hitSum := len(playerEx.winThreePos)
				if hitSum > 0 {
					hitNum += hitSum
				}
			}
		}
		if sceneEx.isCanAllHitPos != -1 {
			hitNum++
		}
		// 每个打枪加1秒，全垒打再加1秒
		sceneEx.hitTime += time.Second * (time.Duration(hitNum))
		sceneEx.ShowCards()
	}
}

func (this *StateHit) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > sceneEx.hitTime {
			s.ChangeSceneState(rule.ThirteenWaterSceneStateBilled)
		}
	}
}

//=====================================
// StateBilled 结算
//=====================================

type StateBilled struct {
	BaseState
}

func (this *StateBilled) GetState() int {
	return rule.ThirteenWaterSceneStateBilled
}

func (this *StateBilled) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.ThirteenWaterSceneStateStart, rule.ThirteenWaterSceneStateWait:
		return true
	}
	return false
}

func (this *StateBilled) OnEnter(s *base.Scene) {
	logger.Logger.Tracef("(this *StateBilled) OnEnter, sceneid=%v", s.GetSceneId())
	this.BaseState.OnEnter(s)
	ThirteenWaterBroadcastRoomState(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		//通知客户端结算结果
		var billed []*thirteen.Billed
		// 没离开，剩余玩家结算
		for _, playerEx := range sceneEx.players {
			if playerEx != nil && playerEx.IsGameing() {
				if playerEx.gainCoin > 0 {
					//税前赢的钱 税收 下注额
					playerEx.AddServiceFee(playerEx.taxCoin)
					playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneWin, base.SyncFlag_ToClient, "system", s.GetSceneName())
					playerEx.CurIsWin = 1
				} else if playerEx.gainCoin < 0 {
					playerEx.AddCoin(playerEx.gainCoin, common.GainWay_CoinSceneLost, base.SyncFlag_ToClient, "system", s.GetSceneName())
					playerEx.CurIsWin = -1
				}
				// 离场补偿分
				if playerEx.score[6] > 0 {
					playerEx.AddCoin(playerEx.score[6]*int64(sceneEx.GetBaseScore()), common.GainWay_LeaveCombat, base.SyncFlag_ToClient, "system", s.GetSceneName())
				}
				sceneEx.Statistics(&base.StaticParam{
					SnId:           playerEx.SnId,
					Gain:           playerEx.gainCoin,
					GainTax:        playerEx.taxCoin,
					IsAddTimes:     true,
					HasRobotGaming: sceneEx.robotNum > 0,
				})
				billed = append(billed, &thirteen.Billed{
					Pos:        proto.Int32(int32(playerEx.Pos)),
					Coin:       proto.Int64(playerEx.Coin),
					WinCoin:    proto.Int64(playerEx.gainCoin + playerEx.score[6]*int64(sceneEx.GetBaseScore())),
					CombatCoin: playerEx.score[6] * int64(sceneEx.GetBaseScore()),
				})
			}
		}
		// 提前离开的
		for _, playerEx := range sceneEx.PlayerBackup {
			if playerEx != nil && playerEx.isBilled {
				billed = append(billed, &thirteen.Billed{
					Pos:        proto.Int32(int32(playerEx.Pos)),
					Coin:       proto.Int64(playerEx.Coin),
					WinCoin:    proto.Int64(playerEx.gainCoin + playerEx.score[6]*int64(sceneEx.GetBaseScore())),
					CombatCoin: playerEx.score[6] * int64(sceneEx.GetBaseScore()),
				})
			}
		}

		pack := &thirteen.SCThirteenBilled{
			AllBilled: billed,
		}
		logger.Logger.Trace("SCThirteenWaterBilled is pack: ", pack)
		sceneEx.Broadcast(int(thirteen.TWMmoPacketID_PACKET_SCThirteenBilled), pack, 0)

		//体验场不走统计
		if !sceneEx.Testing {
			if sceneEx.gamePlayerNum-sceneEx.robotNum > 0 {
				/////////////////////////////////////统计牌局详细记录
				thirteenWaterType := model.ThirteenWaterType{
					RoomId:     int32(sceneEx.SceneId),
					RoomRounds: int32(sceneEx.NumOfGames),
					RoomType:   int32(sceneEx.SceneType),
					BaseScore:  sceneEx.GetBaseScore(),
					NowRound:   int32(sceneEx.NumOfGames),
					ClubRate:   sceneEx.Scene.PumpCoin,
				}
				var person []model.ThirteenWaterPerson
				for _, o_player := range sceneEx.players {
					if o_player != nil && o_player.IsGameing() {
						p := model.ThirteenWaterPerson{
							UserId:      o_player.SnId,
							ChangeCoin:  o_player.gainCoin + o_player.score[6]*int64(sceneEx.GetBaseScore()),
							Cardinfo:    o_player.cards[:],
							StartCoin:   o_player.StartCoin,
							UserIcon:    o_player.Head,
							IsRob:       o_player.IsRob,
							Flag:        o_player.GetFlag(),
							Tax:         o_player.taxCoin,
							Platform:    o_player.Platform,
							Channel:     o_player.Channel,
							Promoter:    strconv.Itoa(int(o_player.PromoterTree)),
							PackageTag:  o_player.PackageID,
							InviterId:   o_player.InviterId,
							WBLevel:     o_player.WBLevel,
							IsFirst:     sceneEx.IsPlayerFirst(sceneEx.GetPlayer(o_player.SnId)),
							TableScore:  o_player.tableScore,
							LeaveCombat: int(o_player.score[6]),
							AllFight:    int(o_player.score[5]),
							GunScore:    int(o_player.score[4]),
							TestLog:     o_player.TestLog,
						}
						score := int64(0)
						for _, v := range o_player.score {
							score += v
						}
						p.AllScore = int(score)
						p.CardsO = model.ThirteenWaterPoker{
							Head:      o_player.cardsO.Head,
							Mid:       o_player.cardsO.Mid,
							End:       o_player.cardsO.End,
							PokerType: sceneEx.FormatCards(o_player.cardsO),
						}
						if p.CardsO.PokerType > 1000000 {
							p.SpecialName = rule.SpecialTypeName[p.CardsO.PokerType/1000000]
							copy(p.CardsO.Head[:], o_player.cards[:3])
							copy(p.CardsO.Mid[:], o_player.cards[3:8])
							copy(p.CardsO.End[:], o_player.cards[8:])
						} else if p.CardsO.PokerType > 10000 {
							p.HeadName = rule.PokersTypeName[p.CardsO.PokerType%1000000/10000]
							p.MidName = rule.PokersTypeName[p.CardsO.PokerType%10000/100]
							p.EndName = rule.PokersTypeName[p.CardsO.PokerType%100]
							p.IsDP = o_player.isDP
						}
						p.IsWin = int32(o_player.CurIsWin)
						person = append(person, p)
						///
						if !o_player.IsRob {
							//有真人 存真人的映射表
							totalin, totalout := int64(0), int64(0)
							if o_player.CurIsWin > 0 {
								totalout = o_player.gainCoin + o_player.taxCoin
							} else {
								totalin -= o_player.gainCoin
							}
							validFlow := totalin + totalout
							validBet := common.AbsI64(totalin - totalout)
							sceneEx.SaveGamePlayerListLog(o_player.SnId, base.GetSaveGamePlayerListLogParam(o_player.Platform, o_player.Channel, o_player.BeUnderAgentCode,
								o_player.PackageID, sceneEx.logid, o_player.InviterId, totalin, totalout, o_player.taxCoin,
								0, 0, o_player.gainCoin, validBet, validFlow, p.IsFirst, false))
						}
					}
				}
				for _, o_player := range sceneEx.PlayerBackup {
					if o_player != nil && o_player.isBilled {
						p := model.ThirteenWaterPerson{
							UserId:      o_player.SnId,
							ChangeCoin:  o_player.gainCoin + o_player.score[6]*int64(sceneEx.GetBaseScore()),
							Cardinfo:    o_player.cards[:],
							StartCoin:   o_player.StartCoin,
							UserIcon:    o_player.Head,
							IsRob:       o_player.IsRob,
							Flag:        o_player.flag,
							Tax:         o_player.taxCoin,
							ClubPump:    o_player.clubPump,
							Platform:    o_player.Platform,
							Channel:     o_player.Channel,
							Promoter:    strconv.Itoa(int(o_player.PromoterTree)),
							PackageTag:  o_player.PackageID,
							InviterId:   o_player.InviterId,
							WBLevel:     o_player.WBLevel,
							IsFirst:     sceneEx.IsPlayerFirst(sceneEx.GetPlayer(o_player.SnId)),
							TableScore:  o_player.tableScore,
							LeaveCombat: int(o_player.score[6]),
							AllFight:    int(o_player.score[5]),
							GunScore:    int(o_player.score[4]),
							TestLog:     o_player.TestLog,
						}
						score := int64(0)
						for _, v := range o_player.score {
							score += v
						}
						p.AllScore = int(score)
						p.CardsO = model.ThirteenWaterPoker{
							Head:      o_player.cardsO.Head,
							Mid:       o_player.cardsO.Mid,
							End:       o_player.cardsO.End,
							PokerType: sceneEx.FormatCards(o_player.cardsO),
						}
						if p.CardsO.PokerType > 1000000 {
							p.SpecialName = rule.SpecialTypeName[p.CardsO.PokerType/1000000]
							copy(p.CardsO.Head[:], o_player.cards[:3])
							copy(p.CardsO.Mid[:], o_player.cards[3:8])
							copy(p.CardsO.End[:], o_player.cards[8:])
						} else if p.CardsO.PokerType > 10000 {
							p.HeadName = rule.PokersTypeName[p.CardsO.PokerType%1000000/10000]
							p.MidName = rule.PokersTypeName[p.CardsO.PokerType%10000/100]
							p.EndName = rule.PokersTypeName[p.CardsO.PokerType%100]
							p.IsDP = o_player.isDP
						}
						p.IsWin = int32(o_player.CurIsWin)
						person = append(person, p)
					}
				}
				thirteenWaterType.PlayerData = person
				thirteenWaterType.PlayerCount = len(person)
				info, err := model.MarshalGameNoteByFIGHT(&thirteenWaterType)
				if err == nil {
					sceneEx.SaveGameDetailedLog(sceneEx.logid, info, &base.GameDetailedParam{})
				}
			}
		}
	}
}

func (this *StateBilled) OnLeave(s *base.Scene) {
	logger.Logger.Tracef("(this *StateBilled) OnLeave, sceneid=%v", s.GetSceneId())
	this.BaseState.OnLeave(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		//结束记录回放录像
		sceneEx.RecordReplayOver()
		for _, v := range sceneEx.players {
			if v != nil {
				v.UnmarkFlag(base.PlayerState_WaitNext)
				v.UnmarkFlag(base.PlayerState_GameBreak)
				v.MarkFlag(base.PlayerState_Ready)
				v.SyncFlag()
			}
		}

		hasLeave := false
		//剔除下线玩家
		for i := 0; i < sceneEx.GetPlayerNum(); i++ {
			player_data := sceneEx.seats[i]
			if player_data == nil {
				continue
			}
			if sceneEx.IsMatchScene() {
				continue
			}
			if !player_data.IsOnLine() {
				sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_DropLine, true)
				hasLeave = true
				continue
			}
			if player_data.IsRob {
				if s.CoinOverMaxLimit(player_data.GetCoin(), player_data.Player) {
					sceneEx.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true)
					hasLeave = true
					continue
				}
			}
			if !s.CoinInLimit(player_data.GetCoin()) {
				sceneEx.PlayerLeave(player_data.Player, s.NotCoinInLimitType(player_data.GetCoin()), true)
				hasLeave = true
				continue
			}
			if player_data.Trusteeship >= 1 {
				s.PlayerLeave(player_data.Player, common.PlayerLeaveReason_LongTimeNoOp, true)
				hasLeave = true
				continue
			}
			if player_data.isStand { // 站起离开
				s.PlayerLeave(player_data.Player, common.PlayerLeaveReason_Normal, true)
				hasLeave = true
				continue
			}
		}

		if !hasLeave && !sceneEx.IsRobFightGame() && !sceneEx.IsMatchScene() {
			s.TryDismissRob()
		}

		if s.CheckNeedDestroy() || (s.IsMatchScene() && (!s.MatchFinals || (s.MatchFinals && s.NumOfGames >= 2))) { // 非决赛打一场 决赛打两场
			sceneEx.SceneDestroy(true)
		}
		s.TryRelease()
	}
}

func (this *StateBilled) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.BaseState.OnPlayerOp(s, p, opcode, params) {
		return true
	}
	return false
}

func (this *StateBilled) OnTick(s *base.Scene) {
	this.BaseState.OnTick(s)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > rule.ThirteenWaterBilledTimeout {
			if sceneEx.CanStart() {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateStart)
			} else {
				s.ChangeSceneState(rule.ThirteenWaterSceneStateWait)
			}
			return
		}
	}
}

// //////////////////////////////////////////////////////////////////////////////
func (this *PolicyThirteen) RegisteSceneState(state base.SceneState) {
	if state == nil {
		return
	}
	stateid := state.GetState()
	if stateid < 0 || stateid >= rule.ThirteenWaterSceneStateMax {
		return
	}
	this.states[stateid] = state
}

func (this *PolicyThirteen) GetSceneState(s *base.Scene, stateid int) base.SceneState {
	if stateid >= 0 && stateid < rule.ThirteenWaterSceneStateMax {
		return this.states[stateid]
	}
	return nil
}

func init() {
	PolicyThirteenSingleton.RegisteSceneState(&StateWait{})
	PolicyThirteenSingleton.RegisteSceneState(&StateStart{})
	PolicyThirteenSingleton.RegisteSceneState(&StateSendCard{})
	PolicyThirteenSingleton.RegisteSceneState(&StateOp{})
	PolicyThirteenSingleton.RegisteSceneState(&StateShow{})
	PolicyThirteenSingleton.RegisteSceneState(&StateHit{})
	PolicyThirteenSingleton.RegisteSceneState(&StateBilled{})
	core.RegisteHook(core.HOOK_BEFORE_START, func() error {
		base.RegisteScenePolicy(common.GameID_Thirteen4, 0, PolicyThirteenSingleton)
		base.RegisteScenePolicy(common.GameID_Thirteen8, 0, PolicyThirteenSingleton)
		base.RegisteScenePolicy(common.GameID_ThirteenFree, 0, PolicyThirteenSingleton)
		base.RegisteScenePolicy(common.GameID_ThirteenFreeLaiZi, 0, PolicyThirteenSingleton)
		return nil
	})
}
