package main

import (
	"math/rand"
	"mongo.games.com/game/worldsrv/internal"
	"time"

	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/netlib"
	"mongo.games.com/goserver/core/task"
	"mongo.games.com/goserver/core/utils"
	"mongo.games.com/goserver/srvlib"
	srvproto "mongo.games.com/goserver/srvlib/protocol"

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/proto"
	server_proto "mongo.games.com/game/protocol/server"
)

var PlayerMgrSington = &PlayerMgr{
	sidMap:           make(map[int64]*Player),
	snidMap:          make(map[int32]*Player),
	accountMap:       make(map[string]*Player),
	tokenMap:         make(map[string]*Player),
	players:          make([]*Player, 0, 1024),
	playerOfPlatform: make(map[string]map[int32]*Player),
	loading:          make(map[string]*PlayerPendingData),
}

type PlayerPendingData struct {
	sid int64 // 连接标识
	ts  int64 // 加载数据的开始时间
}

type PlayerMgr struct {
	BaseClockSinker
	sidMap           map[int64]*Player            // sid
	snidMap          map[int32]*Player            // snid
	accountMap       map[string]*Player           // accountid
	tokenMap         map[string]*Player           // 客服token
	players          []*Player                    // 只有真实玩家
	playerOfPlatform map[string]map[int32]*Player // platform:snid;只有真实玩家

	loading map[string]*PlayerPendingData // accountid,控制访问频率
}

// PlayerStatics 在线统计
type PlayerStatics struct {
	PlayerCnt       int         // 真人数量
	RobotCnt        int         // 机器人数量
	PlayerGamingCnt int         // 游戏中的真人数量
	RobotGamingCnt  int         // 游戏中的机器人数量
	GamingCnt       map[int]int // gameid:玩家人数（包含机器人）
}

// GetOnlineCount 在线人数统计
func (this *PlayerMgr) GetOnlineCount() *PlayerStatics {
	ps := &PlayerStatics{
		GamingCnt: make(map[int]int),
	}
	for _, player := range this.sidMap {
		if player != nil && player.IsOnLine() {
			if !player.IsRob {
				ps.PlayerCnt++
				if player.scene != nil {
					ps.PlayerGamingCnt++
				}
			} else {
				ps.RobotCnt++
				if player.scene != nil {
					ps.RobotGamingCnt++
				}
			}
			if player.scene != nil {
				ps.GamingCnt[player.scene.gameId] = ps.GamingCnt[player.scene.gameId] + 1
			}
		}
	}
	return ps
}

// IsOnline 判断玩家是否在线
func (this *PlayerMgr) IsOnline(snId int32) bool {
	player, ok := this.snidMap[snId]
	if ok {
		return player.IsOnLine()
	} else {
		return false
	}
}

// AddPlayer 缓存玩家数据，并添加到持久化管理器中
func (this *PlayerMgr) AddPlayer(sid int64, playerInfo *model.PlayerData, s *netlib.Session) bool {
	player := NewPlayer(sid, playerInfo, s)
	if player == nil {
		return false
	}

	if sid == 0 {
		logger.Logger.Warnf("(this *PlayerMgr) AddPlayer player sid == 0:")
		return false
	}

	logger.Logger.Trace("(this *PlayerMgr) AddPlayer Set player ip:", player.Ip)
	this.sidMap[sid] = player
	var oldp *Player
	if p, exist := this.snidMap[player.SnId]; exist {
		oldp = p
	}
	this.snidMap[player.SnId] = player
	this.accountMap[player.AccountId] = player
	if player.customerToken != "" {
		this.tokenMap[player.customerToken] = player
	}
	if !player.IsRob {
		var found bool
		for i, p := range this.players {
			if p.SnId == player.SnId {
				found = true
				logger.Logger.Warnf("(this *PlayerMgr) AddPlayer [this.players] found player exist snid=%v", player.SnId)
				this.players[i] = player
				break
			}
		}
		if !found {
			this.players = append(this.players, player)
		}
		if player.HeadUrl == "" {
			player.HeadUrl = niceIdMgr.GetRobHeadUrl(player.Head)
		}

		//平台玩家管理器
		if pp, exist := this.playerOfPlatform[player.Platform]; exist {
			pp[player.SnId] = player
		} else {
			pp = make(map[int32]*Player)
			pp[player.SnId] = player
			this.playerOfPlatform[player.Platform] = pp
		}

		logger.Logger.Tracef("###%v mount to DBSaver[AddPlayer]", player.Name)
		if oldp != nil { //删除旧的玩家
			DbSaver_Inst.UnregisteDbSaveTask(oldp)
		}
		DbSaver_Inst.RegisterDbSaverTask(player)
		niceIdMgr.NiceIdCheck(player.SnId)
	} else {
		player.NiceId = niceIdMgr.PopNiceId(player.SnId)
		player.HeadUrl = niceIdMgr.GetRobHeadUrlIdx()
	}
	return true
}

// DelPlayer 清除玩家缓存数据
// 一般真人是数据持久化后删除或数据没有修改，机器人不用持久化（机器人数据没有主动删除）
func (this *PlayerMgr) DelPlayer(snid int32) bool {
	player, ok := this.snidMap[snid]
	if !ok || player == nil {
		return false
	}

	if player.sid != 0 {
		delete(this.sidMap, player.sid)
	}
	delete(this.snidMap, player.SnId)
	delete(this.accountMap, player.AccountId)
	if player.customerToken != "" {
		delete(this.tokenMap, player.customerToken)
	}
	if player != nil && !player.IsRob {
		index := -1
		for i, p := range this.players {
			if p.SnId == snid {
				index = i
				break
			}
		}
		if index != -1 {
			this.players = append(this.players[:index], this.players[index+1:]...)
		}

		//平台玩家管理器
		if pp, exist := this.playerOfPlatform[player.Platform]; exist {
			delete(pp, player.SnId)
		}

		niceIdMgr.PushNiceId(player.NiceId)
	}

	for _, v := range internal.GetPlayerLoads() {
		v.Release(player.Platform, player.SnId)
	}

	player.OnLogouted()

	return true
}

// DroplinePlayer 玩家掉线或登出
// 1.玩家登出
// 2.玩家网络断开
// 3.被踢掉线
func (this *PlayerMgr) DroplinePlayer(p *Player) {
	delete(this.sidMap, p.sid)
}

// ReholdPlayer 玩家重连
// 1.登录获取玩家数据
func (this *PlayerMgr) ReholdPlayer(p *Player, newSid int64, newSess *netlib.Session) {
	if p.sid != 0 {
		delete(this.sidMap, p.sid)
	}

	if newSid == 0 {
		logger.Logger.Errorf("(this *PlayerMgr) ReholdPlayer(snid=%v, new=%v)", p.SnId, newSid)
	}

	p.sid = newSid
	p.gateSess = newSess
	p.SetOnline()
	this.sidMap[newSid] = p
}

// GetPlayer 获取玩家数据（玩家在线）
func (this *PlayerMgr) GetPlayer(id int64) *Player {
	if pi, ok := this.sidMap[id]; ok {
		return pi
	}
	return nil
}

func (this *PlayerMgr) GetPlayerBySnId(id int32) *Player {
	if p, ok := this.snidMap[id]; ok {
		return p
	}
	return nil
}

func (this *PlayerMgr) GetPlatformPlayerBySnId(platform string, id int32) *Player {
	if players, exit := this.playerOfPlatform[platform]; exit {
		if p, ok := players[id]; ok {
			return p
		}
	}
	return nil
}

// GetPlayersBySnIds 批量取出玩家信息
func (this *PlayerMgr) GetPlayersBySnIds(ids []int32) []*Player {
	var retPlayers []*Player
	for _, v := range ids {
		if p, ok := this.snidMap[v]; ok {
			retPlayers = append(retPlayers, p)
		}
	}
	return retPlayers
}

func (this *PlayerMgr) GetPlayerByAccount(acc string) *Player {
	if p, ok := this.accountMap[acc]; ok {
		return p
	}
	return nil
}

func (this *PlayerMgr) GetPlayerByToken(token string) *Player {
	if p, ok := this.tokenMap[token]; ok {
		return p
	}
	return nil
}

func (this *PlayerMgr) UpdatePlayerToken(p *Player, newToken string) {
	oldToken := p.customerToken
	if oldToken != newToken {
		if oldToken != "" {
			if _, ok := this.tokenMap[oldToken]; ok {
				delete(this.tokenMap, oldToken)
			}
		}
		if newToken != "" {
			this.tokenMap[newToken] = p
			p.customerToken = newToken
		}
	}
}

// BroadcastMessage 给所有玩家发消息
func (this *PlayerMgr) BroadcastMessage(packetid int, rawpack interface{}) bool {
	sc := &srvproto.BCSessionUnion{
		Bccs: &srvproto.BCClientSession{},
	}
	pack, err := BroadcastMaker.CreateBroadcastPacket(sc, packetid, rawpack)
	if err == nil && pack != nil {
		srvlib.ServerSessionMgrSington.Broadcast(int(srvproto.SrvlibPacketID_PACKET_SS_BROADCAST), pack, common.GetSelfAreaId(), srvlib.GateServerType)
		return true
	}
	return false
}

// BroadcastMessageToPlatform 给某个平台所有玩家发消息
func (this *PlayerMgr) BroadcastMessageToPlatform(platform string, packetid int, rawpack interface{}) {
	if platform == "" {
		this.BroadcastMessage(packetid, rawpack)
	} else {
		players := this.playerOfPlatform[platform]
		mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion)
		for _, p := range players {
			if p != nil && p.gateSess != nil && p.IsOnLine() /*&& p.Platform == platform*/ {
				mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{
					Mccs: &srvproto.MCClientSession{
						SId: proto.Int64(p.sid),
					},
				})
			}
		}
		for gateSess, v := range mgs {
			if gateSess != nil && len(v) != 0 {
				pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...)
				if err == nil {
					proto.SetDefaults(pack)
					gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
				}
			}
		}
	}
}

func (this *PlayerMgr) BroadcastMessageToPlatformByFunc(platform string, packetid int, rawpack interface{}, f func(p *Player) bool) {
	if platform == "" {
		this.BroadcastMessage(packetid, rawpack)
	} else {
		players := this.playerOfPlatform[platform]
		mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion)
		for _, p := range players {
			if p != nil && p.gateSess != nil && p.IsOnLine() && f(p) {
				mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{
					Mccs: &srvproto.MCClientSession{
						SId: proto.Int64(p.sid),
					},
				})
			}
		}
		for gateSess, v := range mgs {
			if gateSess != nil && len(v) != 0 {
				pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...)
				if err == nil {
					proto.SetDefaults(pack)
					gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
				}
			}
		}
	}
}

// BroadcastMessageToPlatformWithHall 给某个平台所有在大厅中的玩家发消息
func (this *PlayerMgr) BroadcastMessageToPlatformWithHall(platform string, snid int32, packetid int, rawpack interface{}) {
	if platform == "" {
		this.BroadcastMessage(packetid, rawpack)
	} else {
		player := this.GetPlayerBySnId(snid)
		if player != nil {
			players := this.playerOfPlatform[platform]
			mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion)
			for _, p := range players {
				if p != nil && p.gateSess != nil && p.IsOnLine() && p.scene == nil {
					if FriendMgrSington.IsShield(p.Platform, p.SnId, snid) {
						continue
					}
					mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{
						Mccs: &srvproto.MCClientSession{
							SId: proto.Int64(p.sid),
						},
					})
				}
			}
			for gateSess, v := range mgs {
				if gateSess != nil && len(v) != 0 {
					pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...)
					if err == nil {
						proto.SetDefaults(pack)
						gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
					}
				}
			}
		}
	}
}

// BroadcastMessageToGroup 发送群组消息
func (this *PlayerMgr) BroadcastMessageToGroup(packetid int, rawpack interface{}, tags []string) bool {
	pack := &server_proto.SSCustomTagMulticast{
		Tags: tags,
	}
	if byteData, ok := rawpack.([]byte); ok {
		pack.RawData = byteData
	} else {
		byteData, err := netlib.MarshalPacket(packetid, rawpack)
		if err == nil {
			pack.RawData = byteData
		} else {
			logger.Logger.Info("PlayerMgr.BroadcastMessageToGroup err:", err)
			return false
		}
	}
	srvlib.ServerSessionMgrSington.Broadcast(int(server_proto.SSPacketID_PACKET_SS_CUSTOMTAG_MULTICAST), pack, common.GetSelfAreaId(), srvlib.GateServerType)
	return true
}

// BroadcastMessageToTarget 给某些玩家发消息
func (this *PlayerMgr) BroadcastMessageToTarget(platform string, target []int32, packetid int, rawpack interface{}) {
	players := this.playerOfPlatform[platform]
	mgs := make(map[*netlib.Session][]*srvproto.MCSessionUnion)
	for _, p := range players {
		if p != nil && p.gateSess != nil && p.IsOnLine() /*&& p.Platform == platform*/ {
			if common.InSliceInt32(target, p.SnId) {
				mgs[p.gateSess] = append(mgs[p.gateSess], &srvproto.MCSessionUnion{
					Mccs: &srvproto.MCClientSession{
						SId: proto.Int64(p.sid),
					},
				})
			}
		}
	}
	for gateSess, v := range mgs {
		if gateSess != nil && len(v) != 0 {
			pack, err := MulticastMaker.CreateMulticastPacket(packetid, rawpack, v...)
			if err == nil {
				proto.SetDefaults(pack)
				gateSess.Send(int(srvproto.SrvlibPacketID_PACKET_SS_MULTICAST), pack)
			}
		}
	}
}

// 感兴趣所有clock event
func (this *PlayerMgr) InterestClockEvent() int {
	return (1 << CLOCK_EVENT_MAX) - 1
}

func (this *PlayerMgr) OnSecTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnSecTimer()
		})
	}
}

func (this *PlayerMgr) OnMiniTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnMiniTimer()
		})
	}
}

func (this *PlayerMgr) OnHourTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnHourTimer()
		})
	}
}

func (this *PlayerMgr) OnDayTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnDayTimer(false, true, 1)
		})
	}
}

func (this *PlayerMgr) OnMonthTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnMonthTimer()
		})
	}
}

func (this *PlayerMgr) OnWeekTimer() {
	for _, player := range this.players {
		utils.CatchPanic(func() {
			player.OnWeekTimer()
		})
	}
}

func (this *PlayerMgr) OnShutdown() {
	this.SaveAll()
}

// SaveAll 保存所有数据，dirty=true
func (this *PlayerMgr) SaveAll() {
	count := len(this.players)
	start := time.Now()
	saveCnt := 0
	failCnt := 0
	nochangeCnt := 0
	logger.Logger.Info("===@PlayerMgr.SaveAll BEG@=== TotalCount:", count)
	for i, p := range this.players {
		idx := i + 1
		if p.dirty {
			if model.SavePlayerData(p.PlayerData) {
				logger.Logger.Infof("===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v save [ok] @=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs)
				saveCnt++
			} else {
				logger.Logger.Warnf("===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v save [error]@=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs)
				failCnt++
			}
		} else {
			logger.Logger.Infof("nochange===@SavePlayerData %v/%v snid:%v coin:%v safebox:%v coinpayts:%v safeboxts:%v gamets:%v nochange [ok]@=", idx, count, p.SnId, p.Coin, p.SafeBoxCoin, p.CoinPayTs, p.SafeBoxCoinTs, p.GameCoinTs)
			nochangeCnt++
		}
		for _, v := range internal.GetPlayerLoads() {
			v.Save(p.Platform, p.SnId, true, true)
		}
	}
	logger.Logger.Infof("===@PlayerMgr.SaveAll END@===, total:%v saveCnt:%v failCnt:%v nochangeCnt:%v take:%v", count, saveCnt, failCnt, nochangeCnt, time.Now().Sub(start))
}

// 黑名单事件
//func (this *PlayerMgr) OnAddBlackInfo(blackinfo *BlackInfo) {
//	if blackinfo.Snid > 0 {
//		if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil {
//			p.PlayerData.BlacklistType = int32(blackinfo.BlackType)
//			p.dirty = true
//			p.Time2Save()
//		} else {
//			task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
//				model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(blackinfo.BlackType))
//				return nil
//			}), nil, "PlayerMgrOnAddBlackInfo").Start()
//		}
//	}
//}

//func (this *PlayerMgr) OnEditBlackInfo(blackinfo *BlackInfo) {
//	//nothing
//	//if blackinfo.Snid > 0 {
//	//	if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil {
//	//		p.PlayerData.BlacklistType = int32(blackinfo.BlackType)
//	//		p.dirty = true
//	//		p.Time2Save()
//	//	} else {
//	//		task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
//	//			model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(blackinfo.BlackType))
//	//			return nil
//	//		}), nil, "PlayerMgrOnEditBlackInfo").Start()
//	//	}
//	//}
//}

//func (this *PlayerMgr) OnRemoveBlackInfo(blackinfo *BlackInfo) {
//	//nothing
//	//if blackinfo.Snid > 0 {
//	//	if p := this.GetPlayerBySnId(blackinfo.Snid); p != nil {
//	//		p.PlayerData.BlacklistType = 0
//	//		p.dirty = true
//	//		p.Time2Save()
//	//	} else {
//	//		task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
//	//			model.UpdatePlayerBlacklistType(blackinfo.Platform, blackinfo.Snid, int32(0))
//	//			return nil
//	//		}), nil, "PlayerMgrOnRemoveBlackInfo").Start()
//	//	}
//	//}
//}

func (this *PlayerMgr) KickoutByPlatform(name string) {
	for _, p := range this.players {
		if name == "" || p.Platform == name {
			p.Kickout(common.KickReason_Disconnection)
		}
	}
}

func (this *PlayerMgr) UpdateAllPlayerPackageTag(packageTag, platform, channel, promoter string, promoterTree, tagkey int32) int {
	var cnt int
	for _, p := range this.players {
		if p != nil && !p.IsRob {
			if p.PackageID == packageTag {
				p.Platform = platform
				p.Channel = channel
				p.BeUnderAgentCode = promoter
				p.PromoterTree = promoterTree
				//p.TagKey = tagkey
				p.dirty = true
				cnt++
			}
		}
	}
	return cnt
}

// LoadRobots 预加载机器人数据
func (this *PlayerMgr) LoadRobots() {
	if model.GameParamData.PreLoadRobotCount > 0 {
		task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
			tsBeg := time.Now()
			robots := model.GetRobotPlayers(model.GameParamData.PreLoadRobotCount)
			tsEnd := time.Now()
			logger.Logger.Tracef("GetRobotPlayers take:%v total:%v", tsEnd.Sub(tsBeg), len(robots))
			return robots
		}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
			if robots, ok := data.([]*model.PlayerData); ok {
				if robots != nil {
					for i := 0; i < len(robots); i++ {
						if this.GetPlayerBySnId(robots[i].SnId) == nil {
							player := NewPlayer(0, robots[i], nil)
							if player != nil {
								this.snidMap[player.SnId] = player
								this.accountMap[player.AccountId] = player
								if player.customerToken != "" {
									this.tokenMap[player.customerToken] = player
								}
							}
						}
					}
				}
			}
		}), "GetRobotPlayers").Start()
	}
}

func (this *PlayerMgr) StartLoading(accid string, sid int64) bool {
	ts := time.Now().Unix()
	if d, exist := this.loading[accid]; exist {
		d.sid = sid
		if ts-d.ts > 300 {
			d.ts = ts
			return false
		}
		return true
	}
	this.loading[accid] = &PlayerPendingData{sid: sid, ts: ts}
	return false
}

func (this *PlayerMgr) EndPlayerLoading(accid string) int64 {
	if d, exist := this.loading[accid]; exist {
		delete(this.loading, accid)
		return d.sid
	}
	return 0
}

//func PlayerRankGe(p1, p2 *Player, n int) bool {
//	switch n {
//	case 0:
//		if p1.TotalCoin == p2.TotalCoin {
//			return p1.SnId < p2.SnId
//		} else {
//			return p1.TotalCoin > p2.TotalCoin
//		}
//	case 1:
//		if p1.CoinPayTotal == p2.CoinPayTotal {
//			return p1.SnId < p2.SnId
//		} else {
//			return p1.CoinPayTotal > p2.CoinPayTotal
//		}
//	case 2:
//		if p1.CoinExchangeTotal == p2.CoinExchangeTotal {
//			return p1.SnId < p2.SnId
//		} else {
//			return p1.CoinExchangeTotal > p2.CoinExchangeTotal
//		}
//	case 3:
//		a := p1.Coin + p1.SafeBoxCoin - p1.CoinPayTotal
//		b := p2.Coin + p2.SafeBoxCoin - p2.CoinPayTotal
//		if a == b {
//			return p1.SnId < p2.SnId
//		} else {
//			return a > b
//		}
//
//	}
//	return false
//}

//	func (this *PlayerMgr) GetRank() map[string][]*model.Rank {
//		ret := make(map[string][]*model.Rank)
//		ls := make(map[string]*list.List)
//
//		platforms := PlatformMgrSingleton.Platforms
//		for p := range platforms {
//			ret[p] = make([]*model.Rank, 0)
//			ls[p] = list.New()
//		}
//
//		for _, player := range this.players {
//			if player.IsRob {
//				continue
//			}
//
//			p := player.PlayerData.Platform
//			if _, ok := platforms[p]; !ok {
//				continue
//			}
//
//			l := ls[p]
//			for n := l.Front(); n != nil; n = n.Next() {
//				if np, ok := n.Value.(*Player); ok {
//					if PlayerRankGe(player, np) {
//						l.InsertBefore(player, n)
//						goto CHECK
//					}
//				}
//				//else {
//				//	logger.Logger.Warnf("PlayerMgr.GetRank n.Value.(*Player) fail")
//				//	continue
//				//}
//			}
//
//			l.PushBack(player)
//		CHECK:
//			if l.Len() > model.MAX_RANK_COUNT {
//				l.Remove(l.Back())
//			}
//		}
//
//		for p := range platforms {
//			l := ls[p]
//			for n := l.Front(); n != nil; n = n.Next() {
//				if np, ok := n.Value.(*Player); ok {
//					ret[p] = append(ret[p], &model.Rank{
//						SnId:      np.PlayerData.SnId,
//						Name:      np.PlayerData.Name,
//						Head:      np.PlayerData.Head,
//						VIP:       np.PlayerData.VIP,
//						TotalCoin: np.PlayerData.TotalCoin,
//					})
//				}
//			}
//		}
//
//		return ret
//	}

//func (this *PlayerMgr) GetAssetRank(platform string) []*model.Rank {
//	ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT)
//	l := list.New()
//
//	for _, player := range this.players {
//		if player.IsRob {
//			continue
//		}
//
//		if player.PlayerData.Platform != platform {
//			continue
//		}
//
//		for n := l.Front(); n != nil; n = n.Next() {
//			if np, ok := n.Value.(*Player); ok {
//				if PlayerRankGe(player, np, 0) {
//					l.InsertBefore(player, n)
//					goto CHECK
//				}
//			}
//		}
//
//		l.PushBack(player)
//	CHECK:
//		if l.Len() > model.MAX_RANK_COUNT {
//			l.Remove(l.Back())
//		}
//	}
//
//	for n := l.Front(); n != nil; n = n.Next() {
//		if np, ok := n.Value.(*Player); ok {
//			ret = append(ret, &model.Rank{
//				SnId:      np.PlayerData.SnId,
//				Name:      np.PlayerData.Name,
//				Head:      np.PlayerData.Head,
//				VIP:       np.PlayerData.VIP,
//				TotalCoin: np.PlayerData.TotalCoin,
//			})
//		}
//	}
//
//	return ret
//}

//func (this *PlayerMgr) GetRechargeLists(platform string) []*model.Rank {
//	ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT)
//	l := list.New()
//
//	for _, player := range this.players {
//		if player.IsRob {
//			continue
//		}
//
//		if player.PlayerData.Platform != platform {
//			continue
//		}
//
//		for n := l.Front(); n != nil; n = n.Next() {
//			if np, ok := n.Value.(*Player); ok {
//				if PlayerRankGe(player, np, 1) {
//					l.InsertBefore(player, n)
//					goto CHECK
//				}
//			}
//		}
//
//		l.PushBack(player)
//	CHECK:
//		if l.Len() > model.MAX_RANK_COUNT {
//			l.Remove(l.Back())
//		}
//	}
//
//	for n := l.Front(); n != nil; n = n.Next() {
//		if np, ok := n.Value.(*Player); ok {
//			ret = append(ret, &model.Rank{
//				SnId:      np.PlayerData.SnId,
//				Name:      np.PlayerData.Name,
//				Head:      np.PlayerData.Head,
//				VIP:       np.PlayerData.VIP,
//				TotalCoin: np.PlayerData.CoinPayTotal,
//			})
//		}
//	}
//
//	return ret
//}

//func (this *PlayerMgr) GetExchangeLists(platform string) []*model.Rank {
//	ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT)
//	l := list.New()
//
//	for _, player := range this.players {
//		if player.IsRob {
//			continue
//		}
//
//		if player.PlayerData.Platform != platform {
//			continue
//		}
//
//		for n := l.Front(); n != nil; n = n.Next() {
//			if np, ok := n.Value.(*Player); ok {
//				if PlayerRankGe(player, np, 2) {
//					l.InsertBefore(player, n)
//					goto CHECK
//				}
//			}
//		}
//
//		l.PushBack(player)
//	CHECK:
//		if l.Len() > model.MAX_RANK_COUNT {
//			l.Remove(l.Back())
//		}
//	}
//
//	for n := l.Front(); n != nil; n = n.Next() {
//		if np, ok := n.Value.(*Player); ok {
//			ret = append(ret, &model.Rank{
//				SnId:      np.PlayerData.SnId,
//				Name:      np.PlayerData.Name,
//				Head:      np.PlayerData.Head,
//				VIP:       np.PlayerData.VIP,
//				TotalCoin: np.PlayerData.CoinExchangeTotal,
//			})
//		}
//	}
//
//	return ret
//}

//func (this *PlayerMgr) GetProfitLists(platform string) []*model.Rank {
//	ret := make([]*model.Rank, 0, model.MAX_RANK_COUNT)
//	l := list.New()
//
//	for _, player := range this.players {
//		if player.IsRob {
//			continue
//		}
//
//		if player.PlayerData.Platform != platform {
//			continue
//		}
//
//		for n := l.Front(); n != nil; n = n.Next() {
//			if np, ok := n.Value.(*Player); ok {
//				if PlayerRankGe(player, np, 3) {
//					l.InsertBefore(player, n)
//					goto CHECK
//				}
//			}
//		}
//
//		l.PushBack(player)
//	CHECK:
//		if l.Len() > model.MAX_RANK_COUNT {
//			l.Remove(l.Back())
//		}
//	}
//
//	for n := l.Front(); n != nil; n = n.Next() {
//		if np, ok := n.Value.(*Player); ok {
//			ret = append(ret, &model.Rank{
//				SnId: np.PlayerData.SnId,
//				Name: np.PlayerData.Name,
//				Head: np.PlayerData.Head,
//				VIP:  np.PlayerData.VIP,
//				//TotalCoin: np.PlayerData.ProfitCoin,
//			})
//		}
//	}
//
//	return ret
//}

//func (this *PlayerMgr) DeletePlayerByPlatform(platform string) {
//	var dels []*Player
//	for _, p := range this.players {
//		if p != nil && p.Platform == platform {
//			p.Kickout(common.KickReason_Disconnection)
//			dels = append(dels, p)
//		}
//	}
//
//	for _, p := range dels {
//		if p != nil {
//			p.isDelete = true
//			if p.scene == nil {
//				this.DelPlayer(p.SnId)
//			}
//		}
//	}
//}

func (this *PlayerMgr) StatsOnline() model.PlayerOLStats {
	stats := model.PlayerOLStats{
		PlatformStats: make(map[string]*model.PlayerStats),
		RobotStats: model.PlayerStats{
			InGameCnt: make(map[int32]map[int32]int32),
		},
	}

	for _, p := range this.sidMap {
		if p != nil {
			if p.IsRob {
				pps := &stats.RobotStats
				if pps != nil {
					if p.scene == nil {
						pps.InHallCnt++
					} else {
						if g, exist := pps.InGameCnt[int32(p.scene.gameId)]; exist {
							g[p.scene.dbGameFree.GetId()]++
						} else {
							g := make(map[int32]int32)
							pps.InGameCnt[int32(p.scene.gameId)] = g
							g[p.scene.dbGameFree.GetId()]++
						}
					}
				}
			} else {
				var pps *model.PlayerStats
				var exist bool
				if pps, exist = stats.PlatformStats[p.Platform]; !exist {
					pps = &model.PlayerStats{InGameCnt: make(map[int32]map[int32]int32)}
					stats.PlatformStats[p.Platform] = pps
				}

				if pps != nil {
					if p.scene == nil {
						pps.InHallCnt++
					} else {
						if g, exist := pps.InGameCnt[int32(p.scene.gameId)]; exist {
							g[p.scene.dbGameFree.GetId()]++
						} else {
							g := make(map[int32]int32)
							pps.InGameCnt[int32(p.scene.gameId)] = g
							g[p.scene.dbGameFree.GetId()]++
						}
					}
				}
			}
		}
	}
	return stats
}

func (p *PlayerMgr) UpdateName(snId int32, name string) {
	player := p.GetPlayerBySnId(snId)
	if player == nil {
		return
	}
	player.setName(name)
	player.dirty = true
}

func (p *PlayerMgr) UpdateHead(snId, head int32) {
	player := p.GetPlayerBySnId(snId)
	if player == nil {
		return
	}
	player.Head = head
	//0:男 1:女
	player.Sex = (player.Head%2 + 1) % 2
	player.dirty = true
	player.changeIconTime = time.Now()
}

func (p *PlayerMgr) UpdateHeadOutline(snId, outline int32) {
	player := p.GetPlayerBySnId(snId)
	if player == nil {
		return
	}
	player.HeadOutLine = outline
	player.dirty = true
}

func (p *PlayerMgr) UpdateHeadUrl(snId int32, url string) {
	player := p.GetPlayerBySnId(snId)
	if player == nil {
		return
	}
	if player.HeadUrl != url {
		player.HeadUrl = url
		player.dirty = true
	}
}

//func (p *PlayerMgr) ModifyActSwitchToPlayer(platform string, modify bool) {
//	if modify { //活动开关修改了才去更新活动开关
//		if players, ok := p.playerOfPlatform[platform]; ok {
//			for _, p := range players {
//				if p != nil && !p.IsRob {
//					p.ModifyActSwitch()
//				}
//			}
//		}
//	}
//}

/*
推荐好友规则
1.优先判断在线玩家人数N

	（1）N≥20；每次刷新，从在线玩家中随机6个
	（2）N＜20；则填充机器人，保证N=20，每次填充的机器人头像和昵称随机；然后从N中随机6个

2.刷新有CD（暂定20s），刷新过后进入cd
*/

type RecommendFriend struct {
	Snid    int32
	Name    string
	Head    int32
	HeadUrl string
	RoleId  int32
}

// RecommendFriendRule 推荐好友
func (this *PlayerMgr) RecommendFriendRule(platform string, snid int32) []RecommendFriend {
	if platform == "" {
		return nil
	} else {
		rets := []RecommendFriend{}
		players := this.playerOfPlatform[platform]
		for _, player := range players { //优先真人
			if player.SnId != snid && !FriendMgrSington.IsFriend(platform, snid, player.SnId) {
				roleId := common.DefaultRoleId
				if player.Roles != nil {
					roleId = int(player.Roles.ModId)
				}
				ret := RecommendFriend{
					Snid:    player.SnId,
					Name:    player.Name,
					Head:    player.Head,
					HeadUrl: player.HeadUrl,
					RoleId:  int32(roleId),
				}
				rets = append(rets, ret)
				if len(rets) >= 20 {
					break
				}
			}
		}
		if len(rets) < 20 {
			for _, player := range this.snidMap { //其次机器人
				if player.IsRob {
					roleId := common.DefaultRoleId
					if player.Roles != nil {
						roleId = int(player.Roles.ModId)
					}
					ret := RecommendFriend{
						Snid:    player.SnId,
						Name:    player.Name,
						Head:    player.Head,
						HeadUrl: player.HeadUrl,
						RoleId:  int32(roleId),
					}
					rets = append(rets, ret)
					if len(rets) >= 20 {
						break
					}
				}
			}
		}
		needIdxs := []int{}
		if rets != nil {
			if len(rets) >= 6 {
				for {
					if len(needIdxs) >= 6 {
						break
					}
					randIdx := rand.Intn(len(rets))
					if !common.InSliceInt(needIdxs, randIdx) {
						needIdxs = append(needIdxs, randIdx)
					}
				}
			} else {
				for i := 0; i < len(rets); i++ {
					needIdxs = append(needIdxs, i)
				}
			}
		}
		ret := []RecommendFriend{}
		for _, idx := range needIdxs {
			ret = append(ret, rets[idx])
		}
		return ret
	}
}

func init() {
	//BlackListMgrSington.RegisterObserver(PlayerMgrSington)
	PlayerSubjectSign.AttachName(PlayerMgrSington)
	PlayerSubjectSign.AttachHead(PlayerMgrSington)
	PlayerSubjectSign.AttachHeadOutline(PlayerMgrSington)
	PlayerSubjectSign.AttachHeadUrl(PlayerMgrSington)

	PlayerSubjectSign.AttachName(FriendMgrSington)
	PlayerSubjectSign.AttachHead(FriendMgrSington)

	// 定时器
	ClockMgrSington.RegisteSinker(PlayerMgrSington)
}
