package chesstitians

import (
	"math/rand"
	"time"

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

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

var ScenePolicyEntitySingleton = &ScenePolicyEntity{}

type ScenePolicyEntity struct {
	base.BaseScenePolicy
	states [rule.SceneStateMax]base.SceneState
}

// 创建场景扩展数据
func (this *ScenePolicyEntity) CreateSceneExData(s *base.Scene) interface{} {
	sceneEx := NewSceneEx(s)
	if sceneEx != nil {
		if sceneEx.init() {
			sceneEx.Clear()
			s.SetExtraData(sceneEx)
		}
	}
	return sceneEx
}

// 创建玩家扩展数据
func (this *ScenePolicyEntity) CreatePlayerExData(s *base.Scene, p *base.Player) interface{} {
	playerEx := &PlayerEx{Player: p}
	if playerEx != nil {
		playerEx.init()
		p.SetExtraData(playerEx)
	}
	return playerEx
}

// 场景开启事件
func (this *ScenePolicyEntity) OnStart(s *base.Scene) {
	logger.Logger.Trace("(this *ScenePolicyEntity) OnStart, GetSceneId()=", s.GetSceneId())

	sceneEx := NewSceneEx(s)
	if sceneEx != nil {
		if sceneEx.init() {
			sceneEx.Clear()
			s.SetExtraData(sceneEx)
			s.ChangeSceneState(rule.SceneStateWaitPlayer)
		}
	}
}

// 场景心跳事件
func (this *ScenePolicyEntity) OnTick(s *base.Scene) {
	if s == nil {
		return
	}
	if s.GetSceneState() != nil {
		s.GetSceneState().OnTick(s)
	}
}

// 玩家进入事件
func (this *ScenePolicyEntity) OnPlayerEnter(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerEnter, GetSceneId()=", s.GetSceneId(), " player=", p.GetName())
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {

		//自动带入金币
		pos := sceneEx.FindOnePos()
		if p.Pos != rule.InvalidPos && sceneEx.seats[p.Pos] == nil {
			pos = p.Pos
		}
		if pos < 0 || pos > 1 || sceneEx.seats[pos] != nil {
			p.MarkFlag(base.PlayerState_EnterSceneFailed)
			cnt := len(sceneEx.players)
			logger.Logger.Warnf("ScenePolicyEntity.OnPlayerEnter(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames)
			return
		}

		playerEx := &PlayerEx{Player: p}
		playerEx.init()
		playerEx.SetPos(pos)
		if sceneEx.GetGaming() {
			playerEx.MarkFlag(base.PlayerState_WaitNext)
		}
		p.SetExtraData(playerEx)
		sceneEx.seats[pos] = playerEx
		sceneEx.players[p.SnId] = playerEx

		if sceneEx.GetSeatPlayerCnt() == 1 {
			sceneEx.masterSnId = playerEx.SnId
			sceneEx.BroadcastUpdateMasterSnId(false)
		}

		//广播个人信息
		playerData := CreatePlayerData(p)
		if sceneEx.IsMatchScene() && p.IsRob {
			if len(p.MatchParams) > 2 {
				if p.MatchParams[2] != 0 {
					playerData.CopySnid = p.MatchParams[2]
				}
			}
			if len(p.MatchParams) > 3 {
				if p.MatchParams[3] != 0 {
					playerData.CopyRoleId = p.MatchParams[3]
				}
			}
		}
		pack := &chesstitians.SCChesstitiansPlayerEnter{
			Data: playerData,
		}
		proto.SetDefaults(pack)
		s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), pack, p.GetSid())

		//给自己发送房间信息
		SendRoomInfo(s, p, sceneEx, playerEx)

		s.FirePlayerEvent(p, base.PlayerEventEnter, nil)
	}
}

// 玩家离开事件
func (this *ScenePolicyEntity) OnPlayerLeave(s *base.Scene, p *base.Player, reason int) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerLeave, GetSceneId()=", s.GetSceneId(), " player=", p.SnId)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if playerEx, ok := p.GetExtraData().(*PlayerEx); ok {
			if this.CanChangeCoinScene(s, p) {
				LeavePlayerSnid := playerEx.SnId

				sceneEx.OnPlayerLeave(p, reason)
				s.FirePlayerEvent(p, base.PlayerEventLeave, []int64{int64(reason)})

				if LeavePlayerSnid == sceneEx.masterSnId {
					findOne := false
					for i := 0; i < sceneEx.GetPlayerNum(); i++ {
						playerExSeat := sceneEx.seats[i]
						if playerExSeat != nil && playerExSeat.SnId != sceneEx.masterSnId {
							sceneEx.masterSnId = playerExSeat.SnId
							sceneEx.BroadcastUpdateMasterSnId(true)
							findOne = true
							break
						}
					}
					if !findOne {
						sceneEx.masterSnId = 0
						sceneEx.BroadcastUpdateMasterSnId(false)
					}
				}

				if !playerEx.IsRob { //真人离开
					hadSeat := false
					for _, seat := range sceneEx.seats {
						if seat != nil {
							hadSeat = true
							break
						}
					}
					if !hadSeat { //座位上没有人了，把观众踢出去
						audiences := sceneEx.GetAudiences()
						for _, audience := range audiences {
							if audience != nil {
								s.AudienceLeave(audience, common.PlayerLeaveReason_RoomClose)
							}
						}
					}
				}
			}
		}

	}
}

// 玩家掉线
func (this *ScenePolicyEntity) OnPlayerDropLine(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerDropLine, GetSceneId()=", s.GetSceneId(), " player=", p.Name)
	s.FirePlayerEvent(p, base.PlayerEventDropLine, nil)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if sceneEx.GetGaming() {
			if s.IsMatchScene() || s.IsCoinScene() {
				p.MarkFlag(base.PlayerState_Auto)
				p.SyncFlag()
			}
			return
		}
		s.PlayerLeave(p, common.PlayerLeaveReason_DropLine, true)
	}
}

// 玩家重连
func (this *ScenePolicyEntity) OnPlayerRehold(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerRehold, GetSceneId()=", s.GetSceneId(), " player=", p.Name)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if playerEx, ok := p.GetExtraData().(*PlayerEx); ok {
			if p.IsMarkFlag(base.PlayerState_Auto) {
				p.UnmarkFlag(base.PlayerState_Auto)
				p.SyncFlag()
			}

			//发送房间信息给自己
			SendRoomInfo(s, p, sceneEx, playerEx)
			s.FirePlayerEvent(p, base.PlayerEventRehold, nil)
		}
	}
}

// 返回房间
func (this *ScenePolicyEntity) OnPlayerReturn(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerReturn, GetSceneId()=", s.GetSceneId(), " player=", p.Name)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if playerEx, ok := p.GetExtraData().(*PlayerEx); ok {
			if p.IsMarkFlag(base.PlayerState_Auto) {
				p.UnmarkFlag(base.PlayerState_Auto)
				p.SyncFlag()
			}

			//发送房间信息给自己
			SendRoomInfo(s, p, sceneEx, playerEx)
			s.FirePlayerEvent(p, base.PlayerEventReturn, nil)
		}
	}
}

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

func (this *ScenePolicyEntity) OnPlayerDevOp(s *base.Scene, p *base.Player, opcode int, paramStr []string, param int64) bool {
	if s == nil || p == nil {
		return false
	}
	logger.Logger.Trace("(this *ScenePolicyEntity) OnPlayerDevOp, GetSceneId()=", s.GetSceneId(), " player=", p.GetName(), " opcode=", opcode, " paramsStr=", paramStr, " param=", param)
	sceneEx, _ := s.GetExtraData().(*SceneEx)
	sceneEx.chess.SetChess(paramStr)
	//sceneEx.opPos = int32(param)

	snid := int32(param)

	for idx, seat := range sceneEx.seats {
		if seat.GetSnId() == snid {
			sceneEx.opPos = idx
			break
		}
	}

	sceneEx.lastMove = []int32{}

	//playerEx, _ := p.GetExtraData().(*PlayerEx)

	//pack := CreateRoomInfoPacket(s, p, sceneEx, playerEx)
	//s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack, 0)

	for _, seat := range sceneEx.seats {
		if seat != nil {
			pack := CreateRoomInfoPacket(s, seat.Player, sceneEx, seat)
			ok := seat.Player.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack)
			logger.Logger.Trace("SCChesstitiansSendRoomInfo isok : ", ok, ",pack", pack)
		}
	}

	return true
}

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

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

func (this *ScenePolicyEntity) OnAudienceLeave(s *base.Scene, p *base.Player, reason int) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("ScenePolicyEntity OnAudienceLeave, sceneId=", s.SceneId, " player=", p.SnId, " reason=", reason)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		sceneEx.BroadcastAudienceNum(p)
	}
}

func (this *ScenePolicyEntity) OnAudienceSit(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("ScenePolicyEntity OnAudienceSit, sceneId=", s.SceneId, " player=", p.SnId)
	sceneEx, ok := s.GetExtraData().(*SceneEx)
	if !ok {
		return
	}
	if s.Gaming && !s.GetBEnterAfterStart() {
		return
	}

	//自动带入金币
	pos := sceneEx.FindOnePos()
	if pos < 0 || pos > 3 || sceneEx.seats[pos] != nil {
		p.MarkFlag(base.PlayerState_EnterSceneFailed)
		cnt := len(sceneEx.players)
		logger.Logger.Warnf("ScenePolicyEntity.OnAudienceSit(scene:%v, player:%v) no found fit GetPos(), current player count:%v NumOfGames:%v", s.GetSceneId(), p.SnId, cnt, sceneEx.NumOfGames)
		return
	}

	playerEx := &PlayerEx{Player: p}
	playerEx.init()
	playerEx.SetPos(pos)
	p.UnmarkFlag(base.PlayerState_Audience)
	p.UnmarkFlag(base.PlayerState_Leave)
	if sceneEx.GetGaming() {
		playerEx.MarkFlag(base.PlayerState_WaitNext)
		p.SyncFlag()
	}

	p.SetExtraData(playerEx)
	sceneEx.seats[pos] = playerEx
	sceneEx.players[p.SnId] = playerEx
	//logger.Logger.Trace("广播个人信息,curSeatsNum: ", sceneEx.GetSeatPlayerCnt())
	if sceneEx.GetSeatPlayerCnt() == 1 {
		sceneEx.masterSnId = playerEx.SnId
		sceneEx.BroadcastUpdateMasterSnId(false)
	}

	//广播个人信息
	playerData := CreatePlayerData(p)
	pack := &chesstitians.SCChesstitiansPlayerEnter{
		Data: playerData,
	}
	proto.SetDefaults(pack)
	s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansPlayerEnter), pack, p.GetSid())

	//给自己发送房间信息
	SendRoomInfo(s, p, sceneEx, playerEx)
	sceneEx.BroadcastAudienceNum(p)

	s.FirePlayerEvent(p, base.PlayerEventEnter, nil)

}

func (this *ScenePolicyEntity) OnAudienceDropLine(s *base.Scene, p *base.Player) {
	if s == nil || p == nil {
		return
	}
	logger.Logger.Trace("ScenePolicyEntity OnAudienceDropLine, sceneId=", s.SceneId, " player=", p.SnId)
	s.AudienceLeave(p, common.PlayerLeaveReason_DropLine)
	if sceneEx, ok := s.ExtraData.(*SceneEx); ok {
		sceneEx.BroadcastAudienceNum(p)
	}
}

// 是否完成了整个牌局
func (this *ScenePolicyEntity) IsCompleted(s *base.Scene) bool {
	if s == nil {
		return false
	}
	return false
}

// 是否可以强制开始
func (this *ScenePolicyEntity) IsCanForceStart(s *base.Scene) bool {
	return false
}

// 当前状态能否换桌
func (this *ScenePolicyEntity) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	if s == nil || p == nil {
		return false
	}
	if s.GetSceneState() != nil {
		return s.GetSceneState().CanChangeCoinScene(s, p)
	}
	return true
}

func (this *ScenePolicyEntity) ForceStart(s *base.Scene) {
	s.ChangeSceneState(rule.SceneStateWaitStart)
}

func CreatePlayerData(p *base.Player) *chesstitians.ChesstitiansPlayerData {
	pd := &chesstitians.ChesstitiansPlayerData{
		Name:        proto.String(p.Name),
		SnId:        proto.Int32(p.SnId),
		Head:        proto.Int32(p.Head),
		Sex:         proto.Int32(p.Sex),
		Coin:        proto.Int64(p.GetCoin()),
		Flag:        proto.Int(p.GetFlag()),
		Longitude:   proto.Int32(p.Longitude),
		Latitude:    proto.Int32(p.Latitude),
		City:        proto.String(p.City),
		VIP:         proto.Int32(p.VIP),
		HeadOutLine: proto.Int32(p.HeadOutLine),
		NiceId:      proto.Int32(p.NiceId),
		Pos:         proto.Int(p.GetPos()),
		ChessGrade:  proto.Int64(p.ChessGrade),
		IsRob:       proto.Bool(p.IsRob),
	}
	ex, ok := p.GetExtraData().(*PlayerEx)
	if ok {
		pd.IsWin = ex.isWin
		pd.WinTimes = ex.winTimes
		pd.OtherScore = ex.otherScore
		pd.NextRank = ex.nextRank
		pd.WinScore = ex.winScore
		pd.WinCoin = ex.winCoin
		pd.OldChessGrade = ex.oldGrade
		pd.TotalTime = ex.GetTotalTime()
	}
	if p.Roles != nil {
		pd.RoleId = proto.Int32(p.Roles.ModId)
	}
	if p.Items != nil {
		pd.Items = make(map[int32]int32)
		for id, num := range p.Items {
			pd.Items[id] = proto.Int32(num)
		}
	}
	if len(p.MatchParams) > 0 {
		pd.MatchRankId = p.MatchParams[0]
	}
	if len(p.MatchParams) > 1 {
		pd.Lv = p.MatchParams[1]
	}
	logger.Logger.Trace("ChesstitiansCreatePlayerData pd : ", pd)

	return pd
}

func CreateRoomInfoPacket(s *base.Scene, p *base.Player, sceneEx *SceneEx, playerEx *PlayerEx) *chesstitians.SCChesstitiansRoomInfo {
	pack := &chesstitians.SCChesstitiansRoomInfo{
		RoomId:       proto.Int(s.GetSceneId()),
		Creator:      proto.Int32(s.GetCreator()),
		GameId:       proto.Int(s.GetGameId()),
		RoomMode:     proto.Int(s.GetSceneMode()),
		Params:       s.GetParams(),
		State:        proto.Int32(int32(s.GetSceneState().GetState())),
		TimeOut:      proto.Int(s.GetSceneState().GetTimeout(s)),
		NumOfGames:   proto.Int(sceneEx.NumOfGames),
		TotalOfGames: proto.Int(sceneEx.TotalOfGames),
		CurOpIdx:     proto.Int(-1),
		MasterSnid:   proto.Int32(sceneEx.masterSnId),
		AudienceNum:  proto.Int(s.GetAudiencesNum()),
		BaseScore:    proto.Int32(s.BaseScore),
		MaxPlayerNum: proto.Int(s.GetPlayerNum()),
		GameFreeId:   proto.Int32(s.GetDBGameFree().GetId()),
		SceneType:    proto.Int32(s.GetDBGameFree().GetSceneType()),
		// 比赛场相关
		Round:        proto.Int32(s.MatchRound),
		CurPlayerNum: proto.Int32(s.MatchCurPlayerNum),
		NextNeed:     proto.Int32(s.MatchNextNeed),
		// 计步
		StepSnId:  proto.Int32(sceneEx.stepSnId),
		StepNum:   int64(sceneEx.stepNum),
		StepLimit: int64(sceneEx.stepLimit),
		// 求和
		DrawSnId: proto.Int32(sceneEx.drawSnId),
		// 发起再来一局的玩家
		NextSnId: proto.Int32(sceneEx.nextSnId),
	}

	pack.Variant = int32(sceneEx.chess.GetVariant())

	// 生成棋数据
	pack.Chess = sceneEx.chess.GetChess()
	pack.Act = sceneEx.chess.GetAct()

	pack.Castling = sceneEx.chess.GetCastling()
	pack.CambodianMove = sceneEx.chess.GetCambodianMove()

	pack.Enpassant = int32(sceneEx.chess.GetEnPassant())
	pack.ChessRound = int32(sceneEx.chess.GetRound())

	pack.Check = sceneEx.chess.GetCheck()
	pack.Checkmate = sceneEx.chess.GetCheckmate()

	for _, v := range sceneEx.lastMove {
		pack.LastMove = append(pack.LastMove, int32(v))
	}

	pack.IsMatch = int32(0)
	// 0.普通场 1.锦标赛 2.冠军赛 3.vip专属
	if s.IsMatchScene() {
		pack.IsMatch = s.MatchType
	}
	pack.MatchFinals = 0
	if s.MatchFinals {
		pack.MatchFinals = 1
		if s.NumOfGames >= 2 {
			pack.MatchFinals = 2
		}
	}
	if s.GetSceneState().GetState() == rule.SceneStateBilled {
		sceneEx.opPos = -1
		pack.CurOpIdx = proto.Int32(-1)
	}
	if s.GetSceneState().GetState() >= rule.SceneStatePlayerOp {
		pack.CurOpIdx = proto.Int32(int32(sceneEx.opPos))
	}
	//玩家信息.第一个必然是自己
	pd := CreatePlayerData(p)
	if playerEx != nil {
		// 是黑棋的标记？1黑棋，0白棋
		if playerEx.isBlack {
			pd.IsBlack = proto.Int32(1)
		} else {
			pd.IsBlack = proto.Int32(0)
		}
	}
	pack.Players = append(pack.Players, pd)

	//剩下的按座位排序
	for i := 0; i < sceneEx.GetPlayerNum(); i++ {
		nowPlayer := sceneEx.seats[i]
		if nowPlayer != nil {
			if nowPlayer.SnId != p.SnId {
				pd1 := CreatePlayerData(nowPlayer.Player)
				if sceneEx.IsMatchScene() && nowPlayer.IsRob {
					if len(nowPlayer.MatchParams) > 2 {
						if nowPlayer.MatchParams[2] != 0 {
							pd1.CopySnid = nowPlayer.MatchParams[2]
						}
					}
					if len(nowPlayer.MatchParams) > 3 {
						if nowPlayer.MatchParams[3] != 0 {
							pd1.CopyRoleId = nowPlayer.MatchParams[3]
						}
					}
				}
				// 是黑棋的标记？1黑棋，0白棋
				if nowPlayer.isBlack {
					pd1.IsBlack = proto.Int32(1)
				} else {
					pd1.IsBlack = proto.Int32(0)
				}
				pack.Players = append(pack.Players, pd1)
			}
		}
	}
	proto.SetDefaults(pack)
	return pack
}

func SendRoomInfo(s *base.Scene, p *base.Player, sceneEx *SceneEx, playerEx *PlayerEx) {
	pack := CreateRoomInfoPacket(s, p, sceneEx, playerEx)
	ok := p.SendToClient(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomInfo), pack)
	logger.Logger.Trace("SCChesstitiansSendRoomInfo isok : ", ok, ",pack", pack)
}

//=====================================
// SceneStateBase 状态基类
//=====================================

type SceneStateBase struct {
}

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

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

func (this *SceneStateBase) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	return !p.IsGameing() || s.GetDestroyed()
}

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

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

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

func (this *SceneStateBase) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	logger.Logger.Trace("SceneBaseStateChesstitians.", " s.GetSceneId() : ", s.GetSceneId(), " p.SnId : ", p.SnId, " opcode : ", opcode, " params ", params)

	sceneEx, _ := s.GetExtraData().(*SceneEx)
	if sceneEx == nil {
		return true
	}
	playerEx, _ := p.GetExtraData().(*PlayerEx)
	if playerEx == nil {
		return true
	}
	return false
}

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

// BroadcastRoomState 广播房间状态
func (this *SceneStateBase) BroadcastRoomState(s *base.Scene, state int, params ...int64) {
	pack := &chesstitians.SCChesstitiansRoomState{
		State:  proto.Int(state),
		Params: params,
	}
	proto.SetDefaults(pack)
	s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansRoomState), pack, 0)
}

//=====================================
// SceneStateWaitPlayer 等待玩家进入
//=====================================

type SceneStateWaitPlayer struct {
	SceneStateBase
}

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

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

func (this *SceneStateWaitPlayer) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	if s.IsMatchScene() {
		return false
	}
	return true
}

func (this *SceneStateWaitPlayer) OnEnter(s *base.Scene) {
	this.SceneStateBase.OnEnter(s)

	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		sceneEx.Clear()
		sceneEx.SetGaming(false)
		this.BroadcastRoomState(s, this.GetState())
	}
}

// 状态离开时
func (this *SceneStateWaitPlayer) OnLeave(s *base.Scene) {
	this.SceneStateBase.OnLeave(s)
	logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId())
}

// 玩家操作
func (this *SceneStateWaitPlayer) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) {
		return true
	}
	return true
}

// 玩家事件
func (this *SceneStateWaitPlayer) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *SceneWaitPlayerStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode)
	this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params)

	if _, ok := p.GetExtraData().(*PlayerEx); ok {
		if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
			switch evtcode {
			case base.PlayerEventEnter:
				// 段位等级没有设置不能玩游戏，不应该有这个错误的
				if len(sceneEx.ChessRank) == 0 && s.GetGameId() != common.GameId_ChesstitiansCambodianRobot {
					logger.Logger.Errorf("ChessRank not found Platform:%v", sceneEx.Platform)
					sceneEx.SceneDestroy(true)
					return
				}

				//如果有人进入, 检查在线人是否能够开启游戏,够的话，切换到延迟开启状态
				if sceneEx.CanStart() {
					logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnPlayerEvent s.ChangeSceneState(SceneStateWaitStart) %v", s.GetSceneId())
					s.ChangeSceneState(rule.SceneStateWaitStart)
				}
			}
		}
	}
}

func (this *SceneStateWaitPlayer) OnTick(s *base.Scene) {
	this.SceneStateBase.OnTick(s)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if s.CheckNeedDestroy() {
			sceneEx.SceneDestroy(true)
			return
		}
		if sceneEx.CanStart() {
			logger.Logger.Tracef("(this *SceneWaitPlayerStateChesstitians) OnTick s.ChangeSceneState(SceneStateWaitStart) %v", s.GetSceneId())
			s.ChangeSceneState(rule.SceneStateWaitStart)
		} else if !s.GetGaming() && s.GetRobotNum() == 1 {
			tNow := time.Now()
			for _, p := range s.Players {
				if p.IsRob && tNow.Sub(p.GetLastOPTimer()) > time.Second*time.Duration(30+rand.Int63n(60)) {
					//p.LastOPTimer = tNow.Add(time.Minute * 30)
					s.PlayerLeave(p, common.PlayerLeaveReason_Normal, true)
				}
			}
		}
	}
}

//=====================================
// SceneStateWaitStart 开始游戏
//=====================================

type SceneStateWaitStart struct {
	SceneStateBase
}

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

func (this *SceneStateWaitStart) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.SceneStateWaitPlayer, rule.SceneStateWaitStart, rule.SceneStateChessInit:
		return true
	default:
		return false
	}
}

// 当前状态能否换桌
func (this *SceneStateWaitStart) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	if s.IsMatchScene() {
		return false
	}
	return true
}

func (this *SceneStateWaitStart) OnEnter(s *base.Scene) {
	this.SceneStateBase.OnEnter(s)

	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		sceneEx.Clear()
		sceneEx.SetGaming(false)
		this.BroadcastRoomState(s, this.GetState())
		logger.Logger.Trace("(this *SceneWaitStartStateChesstitians) OnEnter", this.GetState())
	}
}

// 状态离开时
func (this *SceneStateWaitStart) OnLeave(s *base.Scene) {
	this.SceneStateBase.OnLeave(s)
	logger.Logger.Tracef("(this *SceneWaitStartStateChesstitians) OnLeave", this.GetState())
}

// 玩家操作
func (this *SceneStateWaitStart) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) {
		return true
	}
	sceneEx, _ := s.GetExtraData().(*SceneEx)
	if sceneEx != nil {
		playerEx, _ := p.GetExtraData().(*PlayerEx)
		if playerEx != nil {
			opRetCode := chesstitians.OpResultCode_OPRC_Error
			if playerEx.SnId == sceneEx.masterSnId && this.GetState() == rule.SceneStateWaitStart {
				if sceneEx.IsMatchScene() {
					return false
				}
				switch int32(opcode) {
				case rule.PlayerOpStart: //房主开始游戏
					if sceneEx.CanStart() == false {
						s.ChangeSceneState(rule.SceneStateWaitPlayer)
					} else {
						opRetCode = chesstitians.OpResultCode_OPRC_Sucess
						s.ChangeSceneState(rule.SceneStateChessInit)
					}
				}
			}
			if opRetCode == chesstitians.OpResultCode_OPRC_Sucess {
				sceneEx.OnPlayerSCOp(p, opcode, opRetCode, params)
			} else {
				logger.Logger.Tracef("SceneWaitStartStateChesstitians OnPlayerOp snid:%v, masterSnId:%v", playerEx.SnId, sceneEx.masterSnId)
			}
		}
	}
	return true
}

// 玩家事件
func (this *SceneStateWaitStart) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *SceneWaitStartStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode)
	this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params)

	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		switch evtcode {
		case base.PlayerEventLeave:
			//如果有人退出, 检查在线人是否能够开启游戏,不够的话，切换到等待状态
			if !sceneEx.CanStart() {
				s.ChangeSceneState(rule.SceneStateWaitPlayer)
			}
		}
	}
}

func (this *SceneStateWaitStart) OnTick(s *base.Scene) {
	this.SceneStateBase.OnTick(s)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if sceneEx.IsMatchScene() {
			delayT := time.Second * 2
			if sceneEx.MatchRound != 1 { //第一轮延迟2s，其他延迟3s 配合客户端播放动画
				delayT = time.Second * 4
			}
			if time.Now().Sub(sceneEx.StateStartTime) > delayT {
				s.ChangeSceneState(rule.SceneStateChessInit) // 比赛场直接发牌
				return
			}
		}

		if time.Now().Sub(sceneEx.StateStartTime) > rule.WaitStartTimeout {
			//开始前再次检查开始条件
			if sceneEx.CanStart() {
				s.ChangeSceneState(rule.SceneStateChessInit)
			} else {
				s.ChangeSceneState(rule.SceneStateWaitPlayer)
			}
		}
	}
}

//=====================================
// SceneStateChessInit 棋盘初始化
//=====================================

type SceneStateChessInit struct {
	SceneStateBase
}

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

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

func (this *SceneStateChessInit) OnEnter(s *base.Scene) {
	this.SceneStateBase.OnEnter(s)

	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		sceneEx.Clear()
		sceneEx.SetGaming(true)
		sceneEx.GameNowTime = time.Now()
		sceneEx.AllPlayerEnterGame()

		//参与游戏次数
		for i := 0; i < sceneEx.GetPlayerNum(); i++ {
			playerEx := sceneEx.seats[i]
			if playerEx != nil {
				if playerEx.IsGameing() {
					playerEx.GameTimes++
				}
			}
		}

		s.NumOfGames++
		s.NotifySceneRoundStart(s.NumOfGames)
		this.BroadcastRoomState(s, this.GetState(), int64(s.NumOfGames))

		//同步防伙牌数据
		sceneEx.SyncScenePlayer()
		//发牌
		sceneEx.ChessInit()

	}
}

// 状态离开时
func (this *SceneStateChessInit) OnLeave(s *base.Scene) {
	this.SceneStateBase.OnLeave(s)

	logger.Logger.Tracef("(this *SceneHandCardStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId())
}

// 玩家操作
func (this *SceneStateChessInit) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) {
		return true
	}

	return true
}

// 玩家事件
func (this *SceneStateChessInit) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *SceneHandCardStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode)
	this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params)
}

func (this *SceneStateChessInit) OnTick(s *base.Scene) {
	this.SceneStateBase.OnTick(s)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		if time.Now().Sub(sceneEx.StateStartTime) > rule.HandCardTimeout {
			for i := 0; i < sceneEx.GetPlayerNum(); i++ {
				seat := sceneEx.seats[i]
				if !seat.isBlack {
					sceneEx.SetCurOpPos(seat.GetPos())
				}
			}
			s.ChangeSceneState(rule.SceneStatePlayerOp)
		}
	}
}

//=====================================
// SceneStatePlayerOp 玩家操作状态
//=====================================

type SceneStatePlayerOp struct {
	SceneStateBase
}

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

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

func (this *SceneStatePlayerOp) OnEnter(s *base.Scene) {
	this.SceneStateBase.OnEnter(s)
	this.BroadcastRoomState(s, this.GetState())
}

// 状态离开时
func (this *SceneStatePlayerOp) OnLeave(s *base.Scene) {
	this.SceneStateBase.OnLeave(s)

	logger.Logger.Tracef("(this *SceneHandCardStateChesstitians) OnLeave, sceneid=%v", s.GetSceneId())
}

// 玩家操作
func (this *SceneStatePlayerOp) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) {
		return true
	}

	this.OnPlayerOpNormal(s, p, opcode, params)
	this.OnPlayerOpAI(s, p, opcode, params)
	return true
}

func (this *SceneStatePlayerOp) OnPlayerOpNormal(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	sceneEx, ok := s.GetExtraData().(*SceneEx)
	if !ok {
		return true
	}
	if sceneEx == nil {
		return true
	}
	playerEx, ok := p.GetExtraData().(*PlayerEx)
	if !ok {
		return true
	}
	if playerEx == nil {
		return true
	}

	returnFunc := func(code chesstitians.OpResultCode) {
		if code == chesstitians.OpResultCode_OPRC_Sucess {
			// 可以成功行棋，则同步给所有的玩家
			sceneEx.BroadcastPlayerSCOp(playerEx.SnId, opcode, code, params)

			if sceneEx.winSnId != 0 {
				sceneEx.ChangeSceneState(rule.SceneStateBilled)
			}
		} else {
			// 若不能成功操作，仅发给操作的玩家
			sceneEx.OnPlayerSCOp(p, opcode, code, params)
		}
	}

	switch int32(opcode) {
	case rule.PlayerOpPlay: // 玩家行棋
		if sceneEx.GetCurOpPos() != playerEx.GetPos() {
			return false
		}

		if len(params) != 2 {
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true
		}

		if sceneEx.PlayChess(params) {
			// 轮换当前操作标记
			sceneEx.NextOpPos()
			sceneEx.StateStartTime = time.Now()
			returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
			return true
		}
		returnFunc(chesstitians.OpResultCode_OPRC_Error)
		return true

	case rule.PlayerOpSurrender: // 投降
		for _, seat := range sceneEx.seats {
			if seat != nil {
				if seat.GetSnId() != playerEx.GetSnId() {
					sceneEx.winSnId = seat.GetSnId()
					break
				}
			}
		}
		returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
		return true

	case rule.PlayerOpDraw: // 求和
		if len(params) == 0 {
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true
		}

		switch params[0] {
		case rule.RequestDraw:
			if sceneEx.RequestDraw(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true

		case rule.CancelDraw:
			if sceneEx.CancelDraw(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true

		case rule.RefuseDraw:
			if sceneEx.RefuseDraw(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true

		case rule.AgreeDraw:
			if sceneEx.AgreeDraw(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true
		}

	case rule.PlayerOpStep: // 计步操作
		// 参数校验
		if len(params) == 0 {
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true
		}

		switch params[0] {
		case rule.StepStart: // 开始计步
			if sceneEx.StartStep(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true

		case rule.StepCancel: // 取消计步
			if sceneEx.CancelStep(playerEx) {
				returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
				return true
			}
			returnFunc(chesstitians.OpResultCode_OPRC_Error)
			return true
		}

	case rule.PlayerOpTest: // 测试 强制同步给所有的玩家
		returnFunc(chesstitians.OpResultCode_OPRC_Sucess)
		return true
	}
	return true
}

func (this *SceneStatePlayerOp) OnPlayerOpAI(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	return true
}

// 玩家事件
func (this *SceneStatePlayerOp) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	logger.Logger.Trace("(this *SceneHandCardStateChesstitians) OnPlayerEvent, GetSceneId()=", s.GetSceneId(), " player=", p.Name, " evtcode=", evtcode)
	this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params)
}

func (this *SceneStatePlayerOp) OnTick(s *base.Scene) {
	this.SceneStateBase.OnTick(s)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		now := time.Now()

		f := func() {
			// 玩家操作超时，输
			for k, v := range sceneEx.seats {
				if k != sceneEx.GetCurOpPos() && v != nil {
					sceneEx.winSnId = v.GetSnId()
					sceneEx.ChangeSceneState(rule.SceneStateBilled)
					break
				}
			}
		}

		if now.Sub(sceneEx.StateStartTime) > rule.PlayerOpTimeout {
			f()
		}

		curPlayer := sceneEx.seats[sceneEx.GetCurOpPos()]
		if time.Duration(curPlayer.getTotalTime())*time.Second >= rule.PlayerTotalTime {
			f()
		}
	}
}

//=====================================
// SceneStateBilled 结算
//=====================================

type SceneStateBilled struct {
	SceneStateBase
}

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

func (this *SceneStateBilled) CanChangeTo(s base.SceneState) bool {
	switch s.GetState() {
	case rule.SceneStateWaitPlayer, rule.SceneStateWaitStart:
		return true
	default:
		return false
	}
}

func (this *SceneStateBilled) CanChangeCoinScene(s *base.Scene, p *base.Player) bool {
	return true
}

func (this *SceneStateBilled) OnEnter(s *base.Scene) {
	this.SceneStateBase.OnEnter(s)
	this.BroadcastRoomState(s, this.GetState())

	sceneEx, ok := s.GetExtraData().(*SceneEx)
	if !ok {
		return
	}

	pack := &chesstitians.SCChesstitiansGameBilled{}
	chessType := model.ChesstitiansType{
		GameId:      sceneEx.GameId,
		RoomId:      int32(sceneEx.GetSceneId()),
		RoomType:    int32(sceneEx.Scene.SceneType),
		NumOfGames:  int32(sceneEx.Scene.NumOfGames),
		BankId:      sceneEx.masterSnId,
		PlayerCount: rule.MaxNumOfPlayer,
		BaseScore:   s.BaseScore,
		TaxRate:     s.DbGameFree.GetTaxRate(),
		RoomMode:    s.GetSceneMode(),
	}

	var winPlayer *PlayerEx
	var losePlayer *PlayerEx
	var draw bool

	for _, seat := range sceneEx.seats {
		if seat != nil {
			if sceneEx.winSnId == seat.GetSnId() {
				winPlayer = seat
			} else {
				losePlayer = seat
			}
		}
	}

	if winPlayer == nil {
		draw = true
		winPlayer = sceneEx.seats[0]
		losePlayer = sceneEx.seats[1]
	}

	if draw { // 平局
		billData, chessPerson := sceneEx.ToBilled(winPlayer, losePlayer, rule.Draw)
		pack.Datas = append(pack.Datas, billData)
		chessType.PlayerData = append(chessType.PlayerData, chessPerson)

		//玩家2
		billData1, chessPerson1 := sceneEx.ToBilled(losePlayer, winPlayer, rule.Draw)
		pack.Datas = append(pack.Datas, billData1)
		chessType.PlayerData = append(chessType.PlayerData, chessPerson1)
	} else { // 有输赢
		billData, chessPerson := sceneEx.ToBilled(winPlayer, losePlayer, rule.Win)
		pack.Datas = append(pack.Datas, billData)
		chessType.PlayerData = append(chessType.PlayerData, chessPerson)

		// 输家
		billData1, chessPerson1 := sceneEx.ToBilled(losePlayer, winPlayer, rule.Lose)
		pack.Datas = append(pack.Datas, billData1)
		chessType.PlayerData = append(chessType.PlayerData, chessPerson1)
	}

	proto.SetDefaults(pack)
	s.Broadcast(int(chesstitians.ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled), pack, 0)
	logger.Logger.Trace("ChesstitiansPacketID_PACKET_SCChesstitiansGameBilled gameFreeId:", sceneEx.GetGameFreeId(), ";pack:", pack)

	info, err := model.MarshalGameNoteByFIGHT(&chessType)
	if err == nil {
		isSave := false
		var logid string
		for _, o_player := range chessType.PlayerData {
			if !sceneEx.Testing && !o_player.IsRob {
				if logid == "" {
					logid, _ = model.AutoIncGameLogId()
				}
				var totalin, totalout int64

				if o_player.GainCoin < 0 {
					totalin -= (o_player.GainCoin + o_player.GainTaxCoin)
				} else {
					totalout += (o_player.GainCoin + o_player.GainTaxCoin)
				}

				validFlow := totalin + totalout
				validBet := common.AbsI64(totalin - totalout)
				sceneEx.SaveFriendRecord(o_player.UserId, o_player.IsWin, o_player.GainCoin, sceneEx.GetBaseScore())

				// 游戏数据统计
				sceneEx.Statistics(&base.StaticParam{
					SnId:           o_player.UserId,
					Gain:           o_player.GainCoin,
					GainTax:        o_player.GainTaxCoin,
					IsAddTimes:     true,
					HasRobotGaming: sceneEx.GetGamingRobotCnt() > 0,
				})

				// 玩家游戏记录
				sceneEx.SaveGamePlayerListLog(o_player.UserId,
					base.GetSaveGamePlayerListLogParam(o_player.Platform, o_player.Channel, o_player.Promoter,
						o_player.PackageTag, logid, o_player.InviterId, totalin, totalout, o_player.GainTaxCoin,
						0, 0, o_player.GainCoin, validBet, validFlow, o_player.IsFirst, o_player.IsLeave))
				isSave = true
			}
		}
		if isSave {
			// 牌局记录
			sceneEx.SaveGameDetailedLog(logid, info, &base.GameDetailedParam{})
			if !sceneEx.IsMatchScene() {
				sceneEx.SetSystemCoinOut(sceneEx.SystemCoinOut())
				base.CoinPoolMgr.PushCoin(sceneEx.GetCoinSceneTypeId(), sceneEx.GetGroupId(), sceneEx.GetPlatform(), sceneEx.GetSystemCoinOut())
			}
		}
	}
	sceneEx.NotifySceneRoundPause()
}

// 玩家操作
func (this *SceneStateBilled) OnPlayerOp(s *base.Scene, p *base.Player, opcode int, params []int64) bool {
	if this.SceneStateBase.OnPlayerOp(s, p, opcode, params) {
		return true
	}

	sceneEx, ok := s.GetExtraData().(*SceneEx)
	if !ok {
		return true
	}
	if sceneEx == nil {
		return true
	}
	playerEx, ok := p.GetExtraData().(*PlayerEx)
	if !ok {
		return true
	}
	if playerEx == nil {
		return true
	}

	returnFunc := func(code chesstitians.OpResultCode) {
		if code == chesstitians.OpResultCode_OPRC_Sucess {
			sceneEx.BroadcastPlayerSCOp(playerEx.SnId, opcode, code, params)
		} else {
			// 若不能成功操作，仅发给操作的玩家
			sceneEx.OnPlayerSCOp(p, opcode, code, params)
		}
	}

	switch int32(opcode) {
	case rule.PlayerOpNextPlay: // 再来一局
		if playerEx.nextPlay {
			return false
		}
		playerEx.nextPlay = true
		returnFunc(chesstitians.OpResultCode_OPRC_Sucess)

		if sceneEx.nextSnId == 0 {
			sceneEx.nextSnId = playerEx.GetSnId()
		}

		if sceneEx.seats[0].nextPlay && sceneEx.seats[1].nextPlay ||
			sceneEx.GetGameId() == common.GameId_ChesstitiansCambodianRobot {
			s.ChangeSceneState(rule.SceneStateWaitStart)
		}

		return true
	}

	return true
}

// 玩家事件
func (this *SceneStateBilled) OnPlayerEvent(s *base.Scene, p *base.Player, evtcode int, params []int64) {
	this.SceneStateBase.OnPlayerEvent(s, p, evtcode, params)

	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		switch evtcode {
		case base.PlayerEventLeave:
			// 有人离开,解散房间
			for _, v := range sceneEx.seats {
				if v != nil {
					sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true)
				}
			}
		}
	}
}

func (this *SceneStateBilled) OnTick(s *base.Scene) {
	this.SceneStateBase.OnTick(s)

	sceneEx, ok := s.GetExtraData().(*SceneEx)
	if !ok {
		return
	}

	// 有机器人，让机器人离开
	for _, v := range sceneEx.seats {
		if v != nil && v.IsRob {
			sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true)
		}
	}

	if sceneEx.GetRealPlayerNum() == 1 {
		for _, v := range sceneEx.seats {
			if v != nil {
				sceneEx.PlayerLeave(v.Player, common.PlayerLeaveReason_OnBilled, true)
			}
		}
	}

	if sceneEx.GetPlayerCnt() == 0 {
		sceneEx.SceneDestroy(true)
	}
}

// 状态离开时
func (this *SceneStateBilled) OnLeave(s *base.Scene) {
	this.SceneStateBase.OnLeave(s)
	if sceneEx, ok := s.GetExtraData().(*SceneEx); ok {
		sceneEx.SetGaming(false)

		switch sceneEx.GetGameId() {
		case common.GameId_Chesstitians, common.GameId_ChesstitiansMakruk, common.GameId_ChesstitiansCambodian:
			hasLeave := false
			//剔除下线玩家
			for i := 0; i < sceneEx.GetPlayerNum(); i++ {
				playerEx := sceneEx.seats[i]
				if playerEx == nil {
					continue
				}
				playerEx.Clear()
				if sceneEx.IsMatchScene() {
					continue
				}
				if !playerEx.IsOnLine() {
					sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_DropLine, true)
					hasLeave = true
					continue
				}
				// 机器人金币离场检查
				if playerEx.IsRob {
					if s.CoinOverMaxLimit(playerEx.GetCoin(), playerEx.Player) {
						sceneEx.PlayerLeave(playerEx.Player, common.PlayerLeaveReason_Normal, true)
						hasLeave = true
						continue
					}
				}
				// 低于进场限额
				if !s.CoinInLimit(playerEx.GetCoin()) {
					sceneEx.PlayerLeave(playerEx.Player, s.NotCoinInLimitType(playerEx.GetCoin()), true)
					hasLeave = true
					continue
				}
			}

			if !hasLeave && !sceneEx.IsRobFightGame() && !sceneEx.IsMatchScene() {
				s.TryDismissRob()
			}
		case common.GameId_ChesstitiansCambodianRobot:
			sceneEx.SceneDestroy(true)
			return
		}

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

func (this *ScenePolicyEntity) RegisteSceneState(state base.SceneState) {
	if state == nil {
		return
	}
	stateId := state.GetState()
	if stateId < 0 || stateId >= rule.SceneStateMax {
		return
	}
	this.states[stateId] = state
}

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

func init() {
	ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateWaitPlayer{})
	ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateWaitStart{})
	ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateChessInit{})
	ScenePolicyEntitySingleton.RegisteSceneState(&SceneStatePlayerOp{})
	ScenePolicyEntitySingleton.RegisteSceneState(&SceneStateBilled{})

	core.RegisteHook(core.HOOK_BEFORE_START, func() error {
		base.RegisteScenePolicy(common.GameId_Chesstitians, 0, ScenePolicyEntitySingleton)
		base.RegisteScenePolicy(common.GameId_ChesstitiansMakruk, 0, ScenePolicyEntitySingleton)
		base.RegisteScenePolicy(common.GameId_ChesstitiansCambodian, 0, ScenePolicyEntitySingleton)
		base.RegisteScenePolicy(common.GameId_ChesstitiansCambodianRobot, 0, ScenePolicyEntitySingleton)
		return nil
	})
}
