package main

import (
	"math"
	"math/rand"
	"time"

	rawproto "google.golang.org/protobuf/proto"

	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/netlib"
	"mongo.games.com/goserver/srvlib"
	srvlibproto "mongo.games.com/goserver/srvlib/protocol"

	"mongo.games.com/game/common"
	"mongo.games.com/game/gamerule/tienlen"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	hallproto "mongo.games.com/game/protocol/gamehall"
	playerproto "mongo.games.com/game/protocol/player"
	serverproto "mongo.games.com/game/protocol/server"
	"mongo.games.com/game/srvdata"
)

const (
	MatchSceneState_Waiting = iota //等待状态
	MatchSceneState_Running        //进行状态
	MatchSceneState_Billed         //结算状态
)

const (
	// PlayerHistoryModel .
	PlayerHistoryModel = iota + 1

	// BIGWIN_HISTORY_MODEL .
	BIGWIN_HISTORY_MODEL

	// GameHistoryModel .
	GameHistoryModel
)

type PlayerGameCtx struct {
	takeCoin             int64 //进房时携带的金币量
	enterTs              int64 //进入时间
	totalConvertibleFlow int64 //进房时玩家身上的总流水
}

// Scene 场景(房间)
type Scene struct {
	sceneId         int                      //场景id
	gameId          int                      //游戏id
	gameMode        int                      //游戏模式
	sceneMode       int                      //房间模式,参考common.SceneMode_XXX
	params          []int32                  //场景参数
	paramsEx        []int32                  //其他扩展参数
	playerNum       int                      //人数
	robotNum        int                      //机器人数量
	robotLimit      int                      //最大限制机器人数量
	preInviteRobNum int                      //准备邀请机器人的数量
	creator         int32                    //创建者账号id
	agentor         int32                    //代理者id
	replayCode      string                   //回放码
	currRound       int32                    //当前第几轮
	totalRound      int32                    //总共几轮
	clycleTimes     int32                    //循环次数
	deleting        bool                     //正在删除
	starting        bool                     //正在开始
	closed          bool                     //房间已关闭
	force           bool                     //强制删除
	hadCost         bool                     //是否已经扣过房卡
	inTeahourse     bool                     //是否在棋牌馆
	players         map[int32]*Player        //玩家
	audiences       map[int32]*Player        //观众
	seats           [9]*Player               //座位
	gameSess        *GameSession             //所在gameserver
	sp              ScenePolicy              //场景上的一些业务策略
	createTime      time.Time                //创建时间
	lastTime        time.Time                //最后活跃时间
	startTime       time.Time                //开始时间
	dirty           bool                     //脏标记
	applyTimes      map[int32]int32          //申请坐下次数
	limitPlatform   *Platform                //限制平台
	groupId         int32                    //组id
	hallId          int32                    //厅id
	state           int32                    //场景当前状态
	fishing         int32                    //渔场的鱼潮状态
	gameCtx         map[int32]*PlayerGameCtx //进入房间的环境
	dbGameFree      *serverproto.DB_GameFree //
	ClubId          int32
	clubRoomID      string  //俱乐部包间ID
	clubRoomPos     int32   //
	clubRoomTax     int32   //
	createFee       int32   //创建房间的费用
	manualDelete    bool    //是否手动解散
	GameLog         []int32 //游戏服务器同步的录单
	JackPotFund     int64   //游戏服务器同步的奖池
	State           int32   //当前游戏状态，后期放到ScenePolicy里去处理
	StateTs         int64   //切换到当前状态的时间
	StateSec        int32   //押注状态的秒数
	BankerListNum   int32   //庄家列表数量
	matchParams     []int32 //比赛参数
	matchState      int     //比赛状态
	quitMatchSnids  []int32 //退赛玩家id
	gameSite        int     //tienlen游戏场次区分 1.初级 2.中级 3.高级场
	BaseScore       int32   //tienlen游戏底分
	matchId         int32   //比赛场id

	csp *CoinScenePool // 所在场景池
}

// NewScene 创建房间
func NewScene(agentor, creator int32, id, gameId, gameMode, sceneMode int, clycleTimes, numOfGames int32, params []int32,
	gs *GameSession, limitPlatform *Platform, groupId int32, dbGameFree *serverproto.DB_GameFree, paramsEx ...int32) *Scene {
	sp := GetScenePolicy(gameId, gameMode)
	if sp == nil {
		logger.Logger.Errorf("NewScene sp == nil, gameId=%v gameMode=%v", gameId, gameMode)
		return nil
	}
	s := &Scene{
		sceneId:       id,
		hallId:        dbGameFree.Id,
		playerNum:     0,
		creator:       creator,
		agentor:       agentor,
		gameId:        gameId,
		gameMode:      gameMode,
		sceneMode:     sceneMode,
		params:        params,
		paramsEx:      paramsEx,
		clycleTimes:   clycleTimes,
		players:       make(map[int32]*Player),
		audiences:     make(map[int32]*Player),
		gameSess:      gs,
		sp:            sp,
		createTime:    time.Now(),
		limitPlatform: limitPlatform,
		groupId:       groupId,
		gameCtx:       make(map[int32]*PlayerGameCtx), //进入房间的环境
		dbGameFree:    dbGameFree,
		currRound:     0,
		totalRound:    numOfGames,
	}
	// 从房间配置参数获取，最大房间人数
	s.playerNum = int(sp.GetPlayerNum(s))
	s.lastTime = s.createTime

	if s.IsHallScene() || s.IsCoinScene() {
		code := SceneMgrSingleton.AllocReplayCode()
		s.replayCode = code
	}
	if s.dbGameFree.GetMatchMode() == 0 {
		s.RandRobotCnt()
	}
	if s.IsMatchScene() {
		s.BaseScore = 10
	}
	s.sp.OnStart(s)
	return s
}

func NewLocalGameScene(creator int32, sceneId, gameId, gameSite, sceneMode int, clycleTimes int32, params []int32,
	gs *GameSession, limitPlatform *Platform, playerNum int, dbGameFree *serverproto.DB_GameFree, baseScore, groupId int32, paramsEx ...int32) *Scene {
	sp := GetScenePolicy(gameId, 0)
	if sp == nil {
		logger.Logger.Errorf("NewLocalGameScene sp == nil, gameId=%v ", gameId)
		return nil
	}
	s := &Scene{
		sceneId:       sceneId,
		hallId:        dbGameFree.Id,
		playerNum:     playerNum,
		creator:       creator,
		gameId:        gameId,
		sceneMode:     sceneMode,
		params:        params,
		paramsEx:      paramsEx,
		clycleTimes:   clycleTimes,
		players:       make(map[int32]*Player),
		audiences:     make(map[int32]*Player),
		gameSess:      gs,
		sp:            sp,
		createTime:    time.Now(),
		limitPlatform: limitPlatform,
		groupId:       groupId,
		gameCtx:       make(map[int32]*PlayerGameCtx), //进入房间的环境
		dbGameFree:    dbGameFree,
		gameSite:      gameSite,
		BaseScore:     baseScore,
	}
	if s.playerNum <= 0 {
		s.playerNum = int(sp.GetPlayerNum(s))
	}
	if s.BaseScore <= 0 {
		s.BaseScore = int32(sp.GetBaseCoin(s))
	}
	s.lastTime = s.createTime

	code := SceneMgrSingleton.AllocReplayCode()
	s.replayCode = code
	s.sp.OnStart(s)
	return s
}

func (this *Scene) RebindPlayerSnId(oldSnId, newSnId int32) {
	if this.creator == oldSnId {
		this.creator = newSnId
	}
	if this.agentor == oldSnId {
		this.agentor = newSnId
	}
	if p, exist := this.players[oldSnId]; exist {
		delete(this.players, oldSnId)
		this.players[newSnId] = p
	}
	if p, exist := this.audiences[oldSnId]; exist {
		delete(this.audiences, oldSnId)
		this.audiences[newSnId] = p
	}
}

func (this *Scene) RobotIsLimit() bool {
	if this.robotLimit != 0 {
		if this.robotNum >= this.robotLimit {
			return true
		}
	}
	return false
}

func (this *Scene) PlayerEnter(p *Player, pos int, ischangeroom bool) bool {
	logger.Logger.Infof("(this *Scene:%v) PlayerEnter(%v, %v) ", this.sceneId, p.SnId, pos)

	if p.IsRob {
		if this.robotLimit != 0 {
			if !model.GameParamData.IsRobFightTest {
				//增加所有机器人对战场的
				if this.robotNum+1 > this.robotLimit {
					logger.Logger.Warnf("(this *Scene:%v) PlayerEnter(%v) robot num limit(%v)", this.sceneId, p.SnId, this.robotLimit)
					return false
				}
			}
		}
	}

	// 非百人，设置座位
	if !this.IsHundredScene() {
		if pos != -1 {
			if this.seats[pos] != nil {
				for i := 0; i < this.playerNum; i++ {
					if this.seats[i] == nil {
						p.pos = i
						this.seats[i] = p
						break
					}
				}
			} else {
				p.pos = pos
				this.seats[pos] = p
			}
		} else {
			for i := 0; i < this.playerNum; i++ {
				if this.seats[i] == nil {
					p.pos = i
					this.seats[i] = p
					break
				}
			}
		}
	}

	p.scene = this
	this.players[p.SnId] = p
	this.gameSess.AddPlayer(p)

	switch {
	case this.IsCoinScene():

	case this.IsHundredScene():
		// todo 删除这个标记
		HundredSceneMgrSington.OnPlayerEnter(p, this.paramsEx[0])
	case this.IsMatchScene():

	}

	// 如果正在等待比赛,退赛
	if !this.IsMatchScene() {
		isWaiting, tmid := TournamentMgr.IsMatchWaiting(p.Platform, p.SnId)
		if isWaiting {
			TournamentMgr.CancelSignUp(p.Platform, tmid, p.SnId)
		}
	}

	takeCoin := p.Coin
	leaveCoin := int64(0)
	gameTimes := rand.Int31n(100)
	matchParams := []int32{} //排名、段位、假snid、假角色

	if this.IsMatchScene() && p.matchCtx != nil {
		takeCoin = int64(p.matchCtx.grade)
		matchParams = append(matchParams, p.matchCtx.rank) //排名
		ms := MatchSeasonMgrSington.GetMatchSeason(p.SnId)
		if ms != nil {
			matchParams = append(matchParams, ms.Lv) //段位
		} else {
			if p.IsRob {
				//robotRandLv := MatchSeasonRankMgrSington.CreateRobotLv()
				matchParams = append(matchParams, p.matchCtx.copyLv) //机器人随机段位
			} else {
				matchParams = append(matchParams, 1) //段位默认值
			}
		}
		matchParams = append(matchParams, p.matchCtx.copySnid)   //假snid
		matchParams = append(matchParams, p.matchCtx.copyRoleId) //假snid
	} else {
		if p.IsRob {
			if len(this.paramsEx) > 0 { //机器人携带金币动态调整
				gps := PlatformMgrSingleton.GetGameFree(this.limitPlatform.IdStr, this.paramsEx[0])
				if gps != nil {
					dbGameFree := gps.DbGameFree
					if gps.GroupId != 0 {
						pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId)
						if pgg != nil {
							dbGameFree = pgg.DbGameFree
						}
					}

					flag := false
					if common.IsLocalGame(this.gameId) {
						baseScore := this.BaseScore
						arrs := srvdata.PBDB_CreateroomMgr.Datas.Arr
						tmpIds := []int32{}
						for i := 0; i < len(arrs); i++ {
							arr := arrs[i]
							if int(arr.GameId) == this.gameId && int(arr.GameSite) == this.gameSite {
								betRange := arr.GetBetRange()
								if len(betRange) == 0 {
									continue
								}
								for j := 0; j < len(betRange); j++ {
									if betRange[j] == baseScore && len(arr.GetGoldRange()) > 0 && arr.GetGoldRange()[0] != 0 {
										tmpIds = append(tmpIds, arr.GetId())
										break
									}
								}
							}
						}
						if len(tmpIds) > 0 {
							randId := common.RandInt32Slice(tmpIds)
							crData := srvdata.PBDB_CreateroomMgr.GetData(randId)
							if crData != nil {
								goldRange := crData.GetGoldRange()
								if len(goldRange) == 2 {
									takeCoin = common.RandFromRangeInt64(int64(goldRange[0]), int64(goldRange[1]))
									flag = true
								} else if len(goldRange) == 1 {
									takeCoin = common.RandFromRangeInt64(int64(goldRange[0]), 2*int64(goldRange[0]))
									flag = true
								}
								leaveCoin = int64(goldRange[0])
								for _, id := range tmpIds {
									tmp := srvdata.PBDB_CreateroomMgr.GetData(id).GetGoldRange()
									if int64(tmp[0]) < leaveCoin && tmp[0] != 0 {
										leaveCoin = int64(tmp[0])
									}
								}
							}
						} else {
							logger.Logger.Warn("gameId: ", this.gameId, " gameSite: ", this.gameSite, " baseScore: ", baseScore)
						}
						if leaveCoin > takeCoin {
							logger.Logger.Warn("robotSnId: ", p.SnId, " baseScore: ", baseScore, " takeCoin: ", takeCoin, " leaveCoin: ", leaveCoin)
						}
						if takeCoin > p.Coin {
							p.Coin = takeCoin
						}
					}
					//if !flag && this.IsCoinScene() && !this.IsTestScene() {
					//	if expectEnterCoin, expectLeaveCoin, ExpectGameTime, ok := RobotCarryMgrEx.RandOneCarry(dbGameFree.GetId()); ok && expectEnterCoin > dbGameFree.GetLimitCoin() && expectEnterCoin < dbGameFree.GetMaxCoinLimit() {
					//		takeCoin = int64(expectEnterCoin)
					//		leaveCoin = int64(expectLeaveCoin)
					//		//如果带入金币和离开金币比较接近，就调整离开金币值
					//		var delta = takeCoin - leaveCoin
					//		if math.Abs(float64(delta)) < float64(takeCoin/50) {
					//			if leaveCoin = takeCoin + delta*(10+rand.Int63n(50)); leaveCoin < 0 {
					//				leaveCoin = 0
					//			}
					//		}
					//		gameTimes = ExpectGameTime * 2
					//		flag = true
					//	}
					//}

					if !flag {
						takerng := dbGameFree.GetRobotTakeCoin()
						if len(takerng) >= 2 && takerng[1] > takerng[0] {
							if takerng[0] < dbGameFree.GetLimitCoin() {
								takerng[0] = dbGameFree.GetLimitCoin()
							}
							takeCoin = int64(common.RandInt(int(takerng[0]), int(takerng[1])))
						} else {
							maxlimit := int64(dbGameFree.GetMaxCoinLimit())
							if maxlimit != 0 && p.Coin > maxlimit {
								logger.Logger.Trace("Player coin:", p.Coin)
								//在下限和上限之间随机，并对其的100的整数倍
								takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit)))
								logger.Logger.Trace("Take coin:", takeCoin)
							}
							if maxlimit == 0 && this.IsCoinScene() {
								maxlimit = int64(common.RandInt(10, 50)) * int64(dbGameFree.GetLimitCoin())
								takeCoin = int64(common.RandInt(int(dbGameFree.GetLimitCoin()), int(maxlimit)))
								logger.Logger.Trace("Take coin:", takeCoin)
							}
						}
						takeCoin = takeCoin / 100 * 100
						//离场金币
						leaverng := dbGameFree.GetRobotLimitCoin()
						if len(leaverng) >= 2 {
							leaveCoin = int64(leaverng[0] + rand.Int63n(leaverng[1]-leaverng[0]))
						}
					}

					// 象棋积分
					chessScore := dbGameFree.GetChessScoreParams()
					if len(chessScore) == 2 {
						p.ChessGrade = int64(common.RandInt(int(chessScore[0]), int(chessScore[1])))
					}

					bankerLimit := this.dbGameFree.GetBanker()
					if bankerLimit != 0 {
						//上庄AI携带
						if /*this.gameId == common.GameId_HundredBull ||*/
						this.gameId == common.GameId_RollCoin ||
							this.gameId == common.GameId_RollAnimals ||
							this.gameId == common.GameId_DragonVsTiger ||
							this.gameId == common.GameId_Baccarat {
							if this.BankerListNum < 3 {
								if rand.Intn(100) < 5 {
									randCoin := int64(math.Floor(float64(bankerLimit) * 1 / float64(this.dbGameFree.GetSceneType())))
									takeCoin = rand.Int63n(randCoin) + int64(bankerLimit)
									if takeCoin > p.Coin {
										//加钱速度慢 暂时不用
										//ExePMCmd(p.gateSess, fmt.Sprintf("%v%v%v", common.PMCmd_AddCoin, common.PMCmd_SplitToken, takeCoin))
									}
								}
							}
						}
					}
					if takeCoin > p.Coin {
						p.Coin = takeCoin
					}
				}
			}
		}
	}

	if p.IsRob {
		this.robotNum++
		p.RobotRandName()
		p.RandRobotVip(this)

		name := this.GetSceneName()
		logger.Logger.Tracef("(this *Scene) PlayerEnter(%v) robot(%v) robotlimit(%v)", name, this.robotNum, this.robotLimit)
	}

	//todo:send add msg to gamesrv
	data, err := p.MarshalData(this.gameId)
	if err == nil {
		var gateSid int64
		if p.gateSess != nil {
			if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil {
				sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0)
				gateSid = sessionId.Get()
			}
		}
		isQuMin := false
		//if !p.IsRob {
		//	pt := PlatformMgrSingleton.GetPackageTag(p.PackageID)
		//	if pt != nil && pt.SpreadTag == 1 {
		//		isQuMin = true
		//	}
		//}
		msg := &serverproto.WGPlayerEnter{
			Sid:        proto.Int64(p.sid),
			SnId:       proto.Int32(p.SnId),
			GateSid:    proto.Int64(gateSid),
			SceneId:    proto.Int(this.sceneId),
			PlayerData: data,
			IsLoaded:   proto.Bool(ischangeroom),
			IsQM:       proto.Bool(isQuMin),
			IParams:    p.MarshalIParam(),
			SParams:    p.MarshalSParam(),
			CParams:    p.MarshalCParam(),
		}
		//sa, err2 := p.MarshalSingleAdjustData(this.dbGameFree.Id)
		//if err2 == nil && sa != nil {
		//	msg.SingleAdjust = sa
		//}
		if this.ClubId != 0 {

		}
		p.takeCoin = takeCoin
		p.sceneCoin = takeCoin
		p.enterts = time.Now()

		if !p.IsRob { //保存下进入时的环境
			this.gameCtx[p.SnId] = &PlayerGameCtx{
				takeCoin:             p.takeCoin,
				enterTs:              p.enterts.Unix(),
				totalConvertibleFlow: p.TotalConvertibleFlow,
			}
		}
		msg.TakeCoin = proto.Int64(takeCoin)
		msg.ExpectLeaveCoin = proto.Int64(leaveCoin)
		msg.ExpectGameTimes = proto.Int32(gameTimes)
		msg.Pos = proto.Int(p.pos)
		if matchParams != nil {
			for _, param := range matchParams {
				msg.MatchParams = append(msg.MatchParams, param)
			}
		}

		// 道具
		dbItemArr := srvdata.PBDB_GameItemMgr.Datas.Arr
		if dbItemArr != nil {
			msg.Items = make(map[int32]int32)
			for _, dbItem := range dbItemArr {
				msg.Items[dbItem.Id] = proto.Int32(0)
				itemInfo := BagMgrSingleton.GetItem(p.SnId, dbItem.Id)
				if itemInfo != nil {
					msg.Items[dbItem.Id] = proto.Int32(int32(itemInfo.ItemNum))
				}
			}
		}

		// 排位积分
		ret := RankMgrSingleton.GetPlayerSeason(p.SnId)
		if ret != nil && ret.PlayerRankSeason != nil {
			msg.RankScore = make(map[int32]int64)
			for k, v := range ret.RankType {
				if v != nil {
					msg.RankScore[k] = v.Score
				}
			}
		}

		if p.IsRobot() {
			msg.RankScore = make(map[int32]int64)
			rankScore := this.dbGameFree.GetRankScoreParams()
			if len(rankScore) == 2 {
				//todo 根据游戏id获取排位类型不太方便
				switch {
				case common.IsTienLen(int(this.dbGameFree.GetGameId())):
					msg.RankScore[tienlen.RankType] = int64(common.RandInt(int(rankScore[0]), int(rankScore[1])))
				}
			}
		}

		proto.SetDefaults(msg)
		this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_PLAYERENTER), msg)
		logger.Logger.Tracef("SSPacketID_PACKET_WG_PLAYERENTER Scene:%v ;PlayerEnter(%v, %v)", this.sceneId, p.SnId, pos)
		this.lastTime = time.Now()
		return true
	} else {
		logger.Logger.Warnf("(this *Scene:%v) PlayerEnter(%v, %v) Marshal player data error %v", this.sceneId, p.SnId, pos, err)
		this.DelPlayer(p)
		return false
	}
}

func ExePMCmd(s *netlib.Session, cmd string) {
	CSPMCmd := &playerproto.CSPMCmd{
		Cmd: proto.String(cmd),
	}
	proto.SetDefaults(CSPMCmd)
	logger.Logger.Trace("CSPMCmd:", CSPMCmd)
	s.Send(int(playerproto.PlayerPacketID_PACKET_CS_PMCMD), CSPMCmd)
}

func (this *Scene) GetPlayerGameCtx(snid int32) *PlayerGameCtx {
	if ctx, exist := this.gameCtx[snid]; exist {
		return ctx
	}
	return nil
}

func (this *Scene) PlayerLeave(p *Player, reason int) {
	logger.Logger.Infof("(this *Scene:%v) PlayerLeave(%v, %v) ", this.sceneId, p.SnId, reason)
	//if !this.IsMatchScene() {
	//pack := &hall_proto.SCLeaveRoom{
	//	Reason:    proto.Int(reason),
	//	OpRetCode: hall_proto.OpResultCode_Game_OPRC_Sucess_Game,
	//	Mode:      proto.Int(0),
	//	RoomId:    proto.Int(this.sceneId),
	//}
	//proto.SetDefaults(pack)
	//p.SendToClient(int(hall_proto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack)
	pack := &hallproto.SCQuitGame{
		Id:     int32(this.dbGameFree.Id),
		Reason: proto.Int(reason),
	}
	pack.OpCode = hallproto.OpResultCode_Game_OPRC_Sucess_Game
	proto.SetDefaults(pack)
	p.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_QUITGAME), pack)
	//}

	//其他人直接从房间退出来
	this.DelPlayer(p)

	// 玩家最后所在游戏
	p.LastGameId = int(this.dbGameFree.GetGameId())

	this.lastTime = time.Now()
}

func (this *Scene) DelPlayer(p *Player) bool {
	if p.scene != this {
		inroomid := 0
		if p.scene != nil {
			inroomid = p.scene.sceneId
		}
		logger.Logger.Warnf("(this *Scene) DelPlayer found player:%v in room:%v but room:%v", p.SnId, inroomid, this.sceneId)
	}
	if this.gameSess != nil {
		this.gameSess.DelPlayer(p)
	}
	delete(this.players, p.SnId)
	if !p.IsRob {
		delete(this.gameCtx, p.SnId)
	}

	p.scene = nil
	SceneMgrSingleton.OnPlayerLeaveScene(this, p)

	switch {
	case this.IsHundredScene():
		HundredSceneMgrSington.OnPlayerLeave(p)
	//case this.IsHallScene():
	//	PlatformMgrSingleton.OnPlayerLeaveScene(this, p)
	//	for i := 0; i < this.playerNum; i++ {
	//		if this.seats[i] == p {
	//			p.pos = -1
	//			this.seats[i] = nil
	//			break
	//		}
	//	}
	case this.IsCoinScene() || this.IsMatchScene():
		for i := 0; i < this.playerNum; i++ {
			if this.seats[i] == p {
				p.pos = -1
				this.seats[i] = nil
				break
			}
		}
	}

	if p.IsRob {
		this.robotNum--
		name := this.GetSceneName()
		logger.Logger.Tracef("(this *Scene) PlayerLeave(%v) robot(%v) robotlimit(%v)", name, this.robotNum, this.robotLimit)
	}
	//from gameserver, so don't need send msg
	return true
}

func (this *Scene) AudienceEnter(p *Player, ischangeroom bool) bool {
	logger.Logger.Infof("(this *Scene:%v) AudienceEnter(%v) ", this.sceneId, p.SnId)
	p.scene = this
	p.applyPos = -1
	this.audiences[p.SnId] = p
	this.gameSess.AddPlayer(p)
	if this.IsHundredScene() {
		HundredSceneMgrSington.OnPlayerEnter(p, this.paramsEx[0])
	}
	//todo:send add msg to gamesrv
	data, err := p.MarshalData(this.gameId)
	if err == nil {
		var gateSid int64
		if p.gateSess != nil {
			if srvInfo, ok := p.gateSess.GetAttribute(srvlib.SessionAttributeServerInfo).(*srvlibproto.SSSrvRegiste); ok && srvInfo != nil {
				sessionId := srvlib.NewSessionIdEx(srvInfo.GetAreaId(), srvInfo.GetType(), srvInfo.GetId(), 0)
				gateSid = sessionId.Get()
			}
		}
		isQuMin := false
		//if !p.IsRob {
		//	pt := PlatformMgrSingleton.GetPackageTag(p.PackageID)
		//	if pt != nil && pt.SpreadTag == 1 {
		//		isQuMin = true
		//	}
		//}
		msg := &serverproto.WGPlayerEnter{
			Sid:        proto.Int64(p.sid),
			SnId:       proto.Int32(p.SnId),
			GateSid:    proto.Int64(gateSid),
			SceneId:    proto.Int(this.sceneId),
			PlayerData: data,
			IsLoaded:   proto.Bool(ischangeroom),
			IsQM:       proto.Bool(isQuMin),
			IParams:    p.MarshalIParam(),
			SParams:    p.MarshalSParam(),
			CParams:    p.MarshalCParam(),
		}

		if !p.IsRob { //保存下进入时的环境
			this.gameCtx[p.SnId] = &PlayerGameCtx{
				takeCoin:             p.takeCoin,
				enterTs:              p.enterts.Unix(),
				totalConvertibleFlow: p.TotalConvertibleFlow,
			}
		}

		takeCoin := p.Coin
		p.takeCoin = takeCoin
		msg.TakeCoin = proto.Int64(takeCoin)
		proto.SetDefaults(msg)
		this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_AUDIENCEENTER), msg)
		p.enterts = time.Now()
		this.lastTime = time.Now()
		return true
	}

	return false
}

func (this *Scene) AudienceLeave(p *Player, reason int) {
	logger.Logger.Infof("(this *Scene:%v) AudienceLeave(%v, %v) ", this.sceneId, p.SnId, reason)
	pack := &hallproto.SCLeaveRoom{
		Reason:    proto.Int(reason),
		OpRetCode: hallproto.OpResultCode_Game_OPRC_Sucess_Game,
		Mode:      proto.Int(0),
		RoomId:    proto.Int(this.sceneId),
	}
	proto.SetDefaults(pack)
	p.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_LEAVEROOM), pack)
	//观众直接从房间退出来
	this.DelAudience(p)
	this.lastTime = time.Now()
}

func (this *Scene) DelAudience(p *Player) bool {
	logger.Logger.Infof("(this *Scene:%v) DelAudience(%v) ", this.sceneId, p.SnId)
	if p.scene != this {
		return false
	}
	if this.gameSess != nil {
		this.gameSess.DelPlayer(p)
	}
	delete(this.audiences, p.SnId)
	if !p.IsRob {
		delete(this.gameCtx, p.SnId)
	}
	p.scene = nil
	p.applyPos = -1
	SceneMgrSingleton.OnPlayerLeaveScene(this, p)
	if this.IsHundredScene() {
		HundredSceneMgrSington.OnPlayerLeave(p)
	}
	//from gameserver, so don't need send msg
	return true
}

//观众坐下
//func (this *Scene) AudienceSit(p *Player, pos int) bool {
//	logger.Logger.Infof("(this *Scene:%v) AudienceSit(%v, %v, %v) ", this.sceneId, p.SnId, pos)
//	if _, exist := this.audiences[p.SnId]; exist {
//		if pos == -1 && !this.IsHundredScene() { //自动匹配;百人场没座位概念
//			for i := 0; i < this.playerNum; i++ {
//				if this.seats[i] == nil {
//					pos = i
//					break
//				}
//			}
//		}
//		if pos != -1 || this.IsHundredScene() {
//			if !this.IsHundredScene() {
//				if this.seats[pos] != nil {
//					return false
//				}
//				p.pos = pos
//				p.applyPos = -1
//				this.seats[pos] = p
//			}
//			delete(this.audiences, p.SnId)
//		}
//
//		p.scene = this
//		this.players[p.SnId] = p
//
//		NpcServerAgentSington.OnPlayerEnterScene(this, p)
//		if this.IsCoinScene() {
//			CoinSceneMgrSingleton.OnPlayerEnter(p, int32(this.sceneId))
//		} else if this.IsHallScene() {
//			PlatformMgrSingleton.OnPlayerEnterScene(this, p)
//		}
//
//		msg := &protocol.WGAudienceSit{
//			SnId:    proto.Int32(p.SnId),
//			SceneId: proto.Int(this.sceneId),
//			Pos:     proto.Int(pos),
//		}
//		p.takeCoin = p.Coin
//		msg.TakeCoin = proto.Int64(p.Coin)
//		proto.SetDefaults(msg)
//		this.SendToGame(int(protocol.MmoPacketID_PACKET_WG_AUDIENCESIT), msg)
//		this.lastTime = time.Now()
//		return true
//	}
//	return false
//}

func (this *Scene) AudienceSit(p *Player, pos int) bool {
	logger.Logger.Infof("(this *Scene:%v) AudienceSit(%v, %v, %v) ", this.sceneId, p.SnId, pos, this.dbGameFree.GetId())
	if _, exist := this.audiences[p.SnId]; exist {
		delete(this.audiences, p.SnId)
		p.scene = this
		this.players[p.SnId] = p

		//NpcServerAgentSington.OnPlayerEnterScene(this, p)
		if this.IsCoinScene() {
			//CoinSceneMgrSingleton.OnPlayerEnter(p, this.dbGameFree.GetId())
		} else if this.IsHallScene() {

		}
		msg := &serverproto.WGAudienceSit{
			SnId:    proto.Int32(p.SnId),
			SceneId: proto.Int(this.sceneId),
			Pos:     proto.Int(pos),
		}
		p.takeCoin = p.Coin
		msg.TakeCoin = proto.Int64(p.Coin)
		proto.SetDefaults(msg)
		this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_AUDIENCESIT), msg)
		this.lastTime = time.Now()
		return true
	}
	return false
}

func (this *Scene) HasPlayer(p *Player) bool {
	if _, exist := this.players[p.SnId]; exist {
		return true
	}
	return false
}

func (this *Scene) HasAudience(p *Player) bool {
	if _, exist := this.audiences[p.SnId]; exist {
		return true
	}
	return false
}

func (this *Scene) GetPlayer(id int32) *Player {
	if p, exist := this.players[id]; exist {
		return p
	}
	return nil
}

func (this *Scene) GetAudience(id int32) *Player {
	if p, exist := this.audiences[id]; exist {
		return p
	}
	return nil
}

func (this *Scene) GetPlayerPos(snId int32) int {
	for index, value := range this.seats {
		if value == nil {
			continue
		}
		if value.SnId == snId {
			return index
		}
	}
	return -1
}

func (this *Scene) GetPlayerCnt() int {
	return len(this.players)
}

func (this *Scene) GetAudienceCnt() int {
	return len(this.audiences)
}

func (this *Scene) IsFull() bool {
	return this.GetPlayerCnt() >= this.playerNum
}

func (this *Scene) IsEmpty() bool {
	return this.GetPlayerCnt() == 0
}

func (this *Scene) AllIsRobot() bool {
	return len(this.players) == this.robotNum
}

func (this *Scene) OnClose() {
	scDestroyRoom := &hallproto.SCDestroyRoom{
		RoomId:    proto.Int(this.sceneId),
		OpRetCode: hallproto.OpResultCode_Game_OPRC_Sucess_Game,
		IsForce:   proto.Int(1),
	}
	proto.SetDefaults(scDestroyRoom)
	this.Broadcast(int(hallproto.GameHallPacketID_PACKET_SC_DESTROYROOM), scDestroyRoom, 0)

	this.closed = true
	PlatformMgrSingleton.OnChangeSceneState(this, this.closed)
	this.sp.OnStop(this)
	//NpcServerAgentSington.OnSceneClose(this)
	for _, p := range this.players {
		this.DelPlayer(p)
	}
	for _, p := range this.audiences {
		this.DelAudience(p)
	}
	this.players = nil
	this.audiences = nil
	this.gameSess = nil
}

func (this *Scene) SendToGame(packetId int, pack interface{}) bool {
	if this.gameSess != nil {
		this.gameSess.Send(packetId, pack)
		return true
	}
	return false
}
func (this *Scene) SendToClient(packetid int, rawpack interface{}, excludeId int32) {
	for snid, value := range this.players {
		if snid == excludeId {
			continue
		}
		value.SendToClient(packetid, rawpack)
	}
}
func (this *Scene) BilledRoomCard(snid []int32) {
	if this.sp != nil {
		this.sp.BilledRoomCard(this, snid)
	}
}

func (this *Scene) IsLongTimeInactive() bool {
	tNow := time.Now()
	//删除超过指定不活跃时间的房间
	if len(this.players) == 0 && tNow.Sub(this.lastTime) > time.Second*time.Duration(model.GameParamData.SceneMaxIdle) {
		return true
	}
	return false
}

func (this *Scene) ForceDelete(isManual bool) {
	this.manualDelete = isManual
	this.deleting = true
	this.force = true
	PlatformMgrSingleton.OnChangeSceneState(this, this.force)
	pack := &serverproto.WGDestroyScene{
		SceneId: proto.Int(this.sceneId),
	}
	proto.SetDefaults(pack)
	this.SendToGame(int(serverproto.SSPacketID_PACKET_WG_DESTROYSCENE), pack)
	logger.Logger.Warnf("(this *Scene) ForceDelete() sceneid=%v", this.sceneId)

	//if this.sceneId == SceneMgrSingleton.GetDgSceneId() {
	//	for _, value := range PlayerMgrSington.sidMap {
	//		if value.scene == nil {
	//			continue
	//		}
	//		if value.scene.sceneId == SceneMgrSingleton.GetDgSceneId() {
	//			value.DgGameLogout()
	//			pack := &hallproto.SCLeaveDgGame{}
	//			pack.OpRetCode = hallproto.OpResultCode_Game_OPRC_Sucess_Game
	//			value.SendToClient(int(hallproto.GameHallPacketID_PACKET_SC_LEAVEDGGAME), pack)
	//		}
	//	}
	//}
	if this.IsPreCreateScene() {
		CoinSceneMgrSingleton.delayCache = append(CoinSceneMgrSingleton.delayCache, CreateRoomCache{gameFreeId: this.dbGameFree.GetId(), platformName: this.limitPlatform.IdStr})
	}
}

func (this *Scene) Shutdown() {
	if this.hadCost && this.sp != nil {
		this.sp.OnShutdown(this)
	}
}

// 小游戏场
func (this *Scene) IsMiniGameScene() bool {
	return this.sceneId >= common.MiniGameSceneStartId && this.sceneId < common.MiniGameSceneMaxId
}

// 比赛场
func (this *Scene) IsMatchScene() bool {
	return this.sceneId >= common.MatchSceneStartId && this.sceneId < common.MatchSceneMaxId
}

// 大厅场
func (this *Scene) IsHallScene() bool {
	return this.sceneId >= common.HallSceneStartId && this.sceneId < common.HallSceneMaxId
}

// 金币场
func (this *Scene) IsCoinScene() bool {
	return this.sceneId >= common.CoinSceneStartId && this.sceneId < common.CoinSceneMaxId
}

// 百人场
func (this *Scene) IsHundredScene() bool {
	return this.sceneId >= common.HundredSceneStartId && this.sceneId < common.HundredSceneMaxId
}

// 私人房间
func (this *Scene) IsPrivateScene() bool {
	return this.sceneId >= common.PrivateSceneStartId && this.sceneId < common.PrivateSceneMaxId || this.sceneMode == common.SceneMode_Private
}

func (this *Scene) IsRankMatch() bool {
	if this.dbGameFree == nil {
		return false
	}
	return this.dbGameFree.RankType > 0
}

func (this *Scene) IsTestScene() bool {
	if this.dbGameFree != nil {
		return this.dbGameFree.GetSceneType() == -1
	}
	if len(this.paramsEx) > 0 {
		dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(this.paramsEx[0])
		if dbGameFree != nil {
			return dbGameFree.GetSceneType() == -1
		}
	}
	return false
}

func (this *Scene) GetSceneName() string {
	if len(this.paramsEx) > 0 {
		dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(this.paramsEx[0])
		if dbGameFree != nil {
			return dbGameFree.GetName() + dbGameFree.GetTitle()
		}
	}
	return "[unknow scene name]"
}
func (this *Scene) RandRobotCnt() {
	if len(this.paramsEx) > 0 {
		gps := PlatformMgrSingleton.GetGameFree(this.limitPlatform.IdStr, this.paramsEx[0])
		if gps != nil {
			dbGameFree := gps.DbGameFree
			if gps.GroupId != 0 {
				pgg := PlatformGameGroupMgrSington.GetGameGroup(gps.GroupId)
				if pgg != nil {
					dbGameFree = pgg.DbGameFree
				}
			}
			numrng := dbGameFree.GetRobotNumRng()
			if len(numrng) >= 2 {
				if numrng[1] == numrng[0] {
					this.robotLimit = int(numrng[0])
				} else {
					if numrng[1] < numrng[0] {
						numrng[1], numrng[0] = numrng[0], numrng[1]
					}
					this.robotLimit = int(numrng[1]) //int(numrng[0] + rand.Int31n(numrng[1]-numrng[0]) + 1)
				}
			}
		}
	}
}

func (this *Scene) isPlatform(platform string) bool {
	if platform == "0" || platform == this.limitPlatform.IdStr {
		return true
	}
	return false
}

func (this *Scene) GetWhitePlayerCnt() int {
	var cnt int
	for _, p := range this.players {
		if p.WBLevel > 0 {
			cnt++
		}
	}
	return cnt
}

func (this *Scene) GetBlackPlayerCnt() int {
	var cnt int
	for _, p := range this.players {
		if p.WBLevel < 0 {
			cnt++
		}
	}
	return cnt
}

func (this *Scene) GetLostPlayerCnt() int {
	var cnt int
	for _, p := range this.players {
		if p.GDatas != nil {
			if d, exist := p.GDatas[this.dbGameFree.GetGameDif()]; exist {
				if d.Statics.TotalIn > d.Statics.TotalOut {
					cnt++
				}
			}
		}
	}
	return cnt
}

func (this *Scene) GetWinPlayerCnt() int {
	var cnt int
	for _, p := range this.players {
		if p.GDatas != nil {
			if d, exist := p.GDatas[this.dbGameFree.GetGameDif()]; exist {
				if d.Statics.TotalIn < d.Statics.TotalOut {
					cnt++
				}
			}
		}
	}
	return cnt
}

func (this *Scene) GetTruePlayerCnt() int {
	return len(this.players) - this.robotNum
}

// 炸金花房间有几个新手
func (this *Scene) GetFoolPlayerCnt() int {
	var cnt int
	for _, p := range this.players {
		if p.IsFoolPlayer != nil && p.IsFoolPlayer[this.dbGameFree.GetGameDif()] {
			cnt++
		}
	}
	return cnt
}

func (this *Scene) GetPlayerType(gameid, gamefreeid int32) (types []int32) {
	for _, p := range this.players {
		t := int32(0)
		if p.IsRob {
			t = common.PlayerType_Rob
		} else if p.WBLevel < 0 {
			t = common.PlayerType_Black
		} else if p.WBLevel > 0 {
			t = common.PlayerType_White
		} else {
			pt := p.CheckType(gameid, gamefreeid)
			if pt != nil {
				t = pt.GetId()
			} else {
				t = common.PlayerType_Undefine
			}
		}
		if !common.InSliceInt32(types, t) {
			types = append(types, t)
		}
	}
	return
}

func (this *Scene) Broadcast(packetid int, msg rawproto.Message, excludeSid int64) {
	mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion)
	for _, p := range this.players {
		if p != nil {
			if p.sid != excludeSid {
				if p.gateSess != nil && p.IsOnLine() {
					mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{
						Mccs: &srvlibproto.MCClientSession{
							SId: p.sid,
						},
					})
				}
			}
		}
	}
	for _, p := range this.audiences {
		if p != nil && p.sid != excludeSid {
			if p.gateSess != nil && p.IsOnLine() {
				mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{
					Mccs: &srvlibproto.MCClientSession{
						SId: p.sid,
					},
				})
			}
		}
	}

	for gateSess, v := range mgs {
		if gateSess != nil && len(v) != 0 {
			pack, err := MulticastMaker.CreateMulticastPacket(packetid, msg, v...)
			if err == nil {
				proto.SetDefaults(pack)
				gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
			}
		}
	}
}

func (this *Scene) HasSameIp(ip string) bool {
	for _, p := range this.players {
		if !p.IsRob {
			if p.GMLevel == 0 && p.Ip == ip {
				return true
			}
		}
	}

	return false
}

func (this *Scene) IsPreCreateScene() bool {
	return this.dbGameFree.GetCreateRoomNum() > 0
}

func (this *Scene) PlayerTryChange() {
	var member []*Player
	var player *Player
	for _, value := range this.players {
		if !value.IsRob {
			member = append(member, value)
			player = value
		}
	}
	if len(member) <= 1 {
		return
	}
	gameFreeId := this.dbGameFree.GetId()
	gameConfig := PlatformMgrSingleton.GetGameFree(player.Platform, gameFreeId)
	if gameConfig != nil && gameConfig.DbGameFree.GetMatchMode() == 1 {
		return
	}
	for i := 0; i < len(member)-1; i++ {
		p := member[i]
		other := member[i+1:]
		if this.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlaceBySnid(other, p,
			this.dbGameFree.GetGameId(), this.dbGameFree.GetSamePlaceLimit()) {
			if p.scene.IsPrivateScene() {
				//if ClubSceneMgrSington.PlayerInChanging(p) {
				//	continue
				//}
				//ClubSceneMgrSington.PlayerTryChange(p, gameFreeId, []int32{int32(this.sceneId)}, false)
			} else {
				if CoinSceneMgrSingleton.PlayerInChanging(p) {
					continue
				}
				excludeSceneIds := p.lastSceneId[gameFreeId]
				CoinSceneMgrSingleton.PlayerTryChange(p, gameFreeId, excludeSceneIds, false)
			}
		}

	}
}

func (this *Scene) GetParamEx(idx int) int32 {
	if idx < 0 || idx > len(this.paramsEx) {
		return -1
	}

	return this.paramsEx[idx]
}

func (this *Scene) SetParamEx(idx int, val int32) {
	cnt := len(this.paramsEx)
	if idx >= 0 && idx < cnt {
		this.paramsEx[idx] = val
	}
}

func (this *Scene) TryForceDelectMatchInfo() {
	if !this.IsMatchScene() {
		return
	}
	if len(this.players) == 0 {
		return
	}
	if this.matchId == 0 {
		return
	}
	if players, exist := TournamentMgr.players[this.matchId]; exist {
		for _, player := range this.players {
			if player != nil && !player.IsRob {
				if TournamentMgr.IsMatching(player.SnId) {
					delete(players, player.SnId)
				}
			}
		}
	}
}
