package main

import (
	"math"
	"sort"

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

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	serverproto "mongo.games.com/game/protocol/server"
	webapi2 "mongo.games.com/game/protocol/webapi"
	"mongo.games.com/game/srvdata"
	"mongo.games.com/game/webapi"
)

func init() {
	ClockMgrSington.RegisteSinker(SceneMgrSingleton)
}

var SceneMgrSingleton = &SceneMgr{
	scenes:             make(map[int]*Scene),
	autoId:             common.MatchSceneStartId,
	coinSceneAutoId:    common.CoinSceneStartId,
	hundredSceneAutoId: common.HundredSceneStartId,
}

// SceneMgr 房间管理器
type SceneMgr struct {
	BaseClockSinker // 驱动时间事件

	scenes             map[int]*Scene // sceneid:Scene
	autoId             int            // 比赛场房间号
	coinSceneAutoId    int            // 金币场房间号
	hundredSceneAutoId int            // 百人场房间号
}

// AllocReplayCode 获取回访码
func (m *SceneMgr) AllocReplayCode() string {
	code, _ := model.GetOneReplayId()
	return code
}

// GenOneCoinSceneId 生产一个金币场房间id
func (m *SceneMgr) GenOneCoinSceneId() int {
	m.coinSceneAutoId++
	if m.coinSceneAutoId > common.CoinSceneMaxId {
		m.coinSceneAutoId = common.CoinSceneStartId
	}
	return m.coinSceneAutoId
}

// GenOneHundredSceneId 生产一个百人场房间id
func (m *SceneMgr) GenOneHundredSceneId() int {
	m.hundredSceneAutoId++
	if m.hundredSceneAutoId > common.HundredSceneMaxId {
		m.hundredSceneAutoId = common.HundredSceneStartId
	}
	return m.hundredSceneAutoId
}

// GenOneMatchSceneId 生产一个比赛场房间id
func (m *SceneMgr) GenOneMatchSceneId() int {
	m.autoId++
	if m.autoId > common.MatchSceneMaxId {
		m.autoId = common.MatchSceneStartId
	}
	return m.autoId
}

// GetScene 获取房间对象
func (m *SceneMgr) GetScene(sceneId int) *Scene {
	if s, exist := m.scenes[sceneId]; exist && !s.deleting {
		return s
	}
	return nil
}

// GetScenesByGame 根据游戏id查询房间
func (m *SceneMgr) GetScenesByGame(gameId int) []*Scene {
	var scenes []*Scene
	for _, value := range m.scenes {
		if value.gameId == gameId {
			scenes = append(scenes, value)
		}
	}
	return scenes
}

// GetScenesByGameFreeId 根据场次id查询房间
func (m *SceneMgr) GetScenesByGameFreeId(gameFreeId int32) []*Scene {
	var scenes []*Scene
	for _, value := range m.scenes {
		if value.dbGameFree.GetId() == gameFreeId {
			scenes = append(scenes, value)
		}
	}
	return scenes
}

// MarshalAllRoom 获取房间列表
func (m *SceneMgr) MarshalAllRoom(platform string, groupId, gameId int, gameMode, clubId, sceneMode, sceneId int,
	gameFreeId, snId int32, start, end, pageSize int32) ([]*webapi2.RoomInfo, int32, int32) {
	roomInfo := make([]*webapi2.RoomInfo, 0, len(m.scenes))
	var isNeedFindAll = false
	if model.GameParamData.IsFindRoomByGroup && platform != "" && snId != 0 && gameId == 0 &&
		gameMode == 0 && sceneId == -1 && groupId == 0 && clubId == 0 && sceneMode == 0 {
		p := PlayerMgrSington.GetPlayerBySnId(snId)
		if p != nil && p.Platform == platform {
			isNeedFindAll = true
		}
	}
	for _, s := range m.scenes {
		if (((s.limitPlatform != nil && s.limitPlatform.IdStr == platform) || platform == "") &&
			((s.gameId == gameId && s.gameMode == gameMode) || gameId == 0) &&
			(s.sceneId == sceneId || sceneId == 0) && (s.groupId == int32(groupId) || groupId == 0) &&
			(s.ClubId == int32(clubId) || clubId == 0) && (s.dbGameFree.GetId() == gameFreeId || gameFreeId == 0) &&
			(s.sceneMode == sceneMode || sceneMode == -1)) || isNeedFindAll {
			var platformName string
			if s.limitPlatform != nil {
				platformName = s.limitPlatform.IdStr
			}

			si := &webapi2.RoomInfo{
				Platform:   platformName,
				SceneId:    int32(s.sceneId),
				GameId:     int32(s.gameId),
				GameMode:   int32(s.gameMode),
				SceneMode:  int32(s.sceneMode),
				GroupId:    s.groupId,
				Creator:    s.creator,
				Agentor:    s.agentor,
				ReplayCode: s.replayCode,
				Params:     s.params,
				PlayerCnt:  int32(len(s.players) - s.robotNum),
				RobotCnt:   int32(s.robotNum),
				CreateTime: s.createTime.Unix(),
				BaseScore:  s.dbGameFree.BaseScore,
			}
			if common.IsLocalGame(s.gameId) {
				si.BaseScore = s.BaseScore
			}
			if s.paramsEx != nil && len(s.paramsEx) > 0 {
				si.GameFreeId = s.paramsEx[0]
			}
			if s.starting {
				si.Start = 1
			} else {
				si.Start = 0
			}
			if s.IsHundredScene() {
				si.Start = 1
			}
			if s.gameSess != nil {
				si.SrvId = s.gameSess.GetSrvId()
			}
			cnt := 0
			total := len(s.players)
			robots := []int32{}

			isContinue := false
			if snId != 0 {
				for _, p := range s.players {
					if p.SnId == int32(snId) {
						isContinue = true
						break
					}
				}
			} else {
				isContinue = true
			}
			if !isContinue {
				continue
			}

			//优先显示玩家
			for id, p := range s.players {
				if !p.IsRob || total < 10 {
					si.PlayerIds = append(si.PlayerIds, id)
					cnt++
				} else {
					robots = append(robots, id)
				}
				if cnt > 10 {
					break
				}
			}
			//不够再显示机器人
			if total > cnt && cnt < 10 && len(robots) != 0 {
				for i := 0; cnt < 10 && i < len(robots); i++ {
					si.PlayerIds = append(si.PlayerIds, robots[i])
					cnt++
					if cnt > 10 {
						break
					}
				}
			}
			roomInfo = append(roomInfo, si)
		}
	}

	sort.Slice(roomInfo, func(i, j int) bool {
		if roomInfo[i].CreateTime < roomInfo[j].CreateTime ||
			(roomInfo[i].CreateTime == roomInfo[j].CreateTime && roomInfo[i].SceneId < roomInfo[j].SceneId) {
			return true
		}
		return false
	})

	//分页处理
	roomSum := float64(len(roomInfo))                          //房间总数
	pageCount := int32(math.Ceil(roomSum / float64(pageSize))) //总页数
	if roomSum <= float64(start) {
		start = 0
	}
	if roomSum < float64(end) {
		end = int32(roomSum)
	}
	needList := roomInfo[start:end] //需要的房间列表
	if len(needList) > 0 {
		return needList, pageCount, int32(roomSum)
	}
	return nil, 0, int32(roomSum)
}

// CreateScene 创建房间
func (m *SceneMgr) CreateScene(agentor, creator int32, sceneId, gameId, gameMode, sceneMode int, clycleTimes int32,
	numOfGames int32, params []int32, gs *GameSession, limitPlatform *Platform, groupId int32, dbGameFree *serverproto.DB_GameFree,
	paramsEx ...int32) *Scene {
	logger.Logger.Trace("(this *SceneMgr) CreateScene ")
	s := NewScene(agentor, creator, sceneId, gameId, gameMode, sceneMode, clycleTimes, numOfGames, params, gs, limitPlatform, groupId,
		dbGameFree, paramsEx...)
	if s == nil {
		return nil
	}

	m.scenes[sceneId] = s
	if !s.IsMatchScene() && dbGameFree != nil && limitPlatform != nil {
		//平台水池设置
		gs.DetectCoinPoolSetting(limitPlatform.IdStr, dbGameFree.GetId(), s.groupId)
	}

	gs.AddScene(s)
	var platformName string
	if limitPlatform != nil {
		platformName = limitPlatform.IdStr
	}
	logger.Logger.Infof("(this *SceneMgr) CreateScene (gameId=%v, mode=%v), SceneId=%v groupid=%v platform=%v",
		gameId, gameMode, sceneId, groupId, platformName)
	return s
}

// CreateLocalGameScene 创建本地游戏房间
func (m *SceneMgr) CreateLocalGameScene(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 {
	logger.Logger.Trace("(this *SceneMgr) CreateLocalGameScene gameSite: ", gameSite, " sceneMode: ", sceneMode)
	s := NewLocalGameScene(creator, sceneId, gameId, gameSite, sceneMode, clycleTimes, params, gs, limitPlatform, playerNum, dbGameFree,
		baseScore, groupId, paramsEx...)
	if s == nil {
		return nil
	}

	m.scenes[sceneId] = s
	gs.AddScene(s)

	var platformName string
	if limitPlatform != nil {
		platformName = limitPlatform.IdStr
	}
	logger.Logger.Infof("(this *SceneMgr) CreateScene (gameId=%v), SceneId=%v platform=%v", gameId, sceneId, platformName)
	return s
}

// DestroyScene 解散房间
// 房间销毁，游戏服务断开
func (m *SceneMgr) DestroyScene(sceneId int, isCompleted bool) {
	logger.Logger.Trace("(this *SceneMgr) DestroyScene ")
	s, ok := m.scenes[sceneId]
	if !ok || s == nil {
		return
	}

	switch {
	case s.IsCoinScene():
		CoinSceneMgrSingleton.OnDestroyScene(s.sceneId)

	case s.IsHundredScene():
		HundredSceneMgrSington.OnDestroyScene(s.sceneId)

	case s.IsMatchScene():
		MatchSceneMgrSingleton.OnDestroyScene(s.sceneId)
	}

	s.gameSess.DelScene(s)
	s.OnClose()
	delete(m.scenes, s.sceneId)
	logger.Logger.Infof("(this *SceneMgr) DestroyScene, SceneId=%v", sceneId)
}

func (m *SceneMgr) OnPlayerLeaveScene(s *Scene, p *Player) {
	logger.Logger.Trace("(this *SceneMgr) OnPlayerLeaveScene", p.SnId)

	// 记录玩家在每个游戏场次最后进入的房间号
	// 只记录金币场
	if s.IsCoinScene() {
		const MINHOLD = 10
		const MAXHOLD = 20
		holdCnt := MINHOLD
		if s.csp != nil {
			holdCnt = s.csp.GetHasTruePlayerSceneCnt() + 2
			if holdCnt < MINHOLD {
				holdCnt = MINHOLD
			}
			if holdCnt > MAXHOLD {
				holdCnt = MAXHOLD
			}
		}
		if p.lastSceneId == nil {
			p.lastSceneId = make(map[int32][]int32)
		}
		id := s.dbGameFree.GetId()
		if sceneIds, exist := p.lastSceneId[id]; exist {
			if !common.InSliceInt32(sceneIds, int32(s.sceneId)) {
				sceneIds = append(sceneIds, int32(s.sceneId))
				cnt := len(sceneIds)
				if cnt > holdCnt {
					sceneIds = sceneIds[cnt-holdCnt:]
				}
				p.lastSceneId[id] = sceneIds
			}
		} else {
			p.lastSceneId[id] = []int32{int32(s.sceneId)}
		}
	}
}

// GetThirdScene 获取三方游戏房间
func (m *SceneMgr) GetThirdScene(i webapi.IThirdPlatform) *Scene {
	if i == nil {
		return nil
	}
	sceneId := i.GetPlatformBase().SceneId
	scene := m.scenes[sceneId]
	if scene != nil {
		return scene
	}

	gs := GameSessMgrSington.GetMinLoadSess(i.GetPlatformBase().BaseGameID)
	if gs != nil {
		limitPlatform := PlatformMgrSingleton.GetPlatform(DefaultPlatform)
		var gameMode = common.SceneMode_Thr
		dbGameFree := srvdata.PBDB_GameFreeMgr.GetData(i.GetPlatformBase().VultGameID)
		scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, i.GetPlatformBase().BaseGameID, gameMode, int(common.SceneMode_Thr), 1, -1,
			[]int32{}, gs, limitPlatform, 0, dbGameFree, i.GetPlatformBase().VultGameID)
		return scene
	} else {
		logger.Logger.Errorf("Get %v game min session failed.", i.GetPlatformBase().BaseGameID)
		return nil
	}
}

//=========================ClockSinker===============================

// InterestClockEvent 接收所有时间事件
func (m *SceneMgr) InterestClockEvent() int {
	return 1 << CLOCK_EVENT_MINUTE
}

func (m *SceneMgr) OnMiniTimer() {
	// 解散空闲房间
	for _, s := range m.scenes {
		if webapi.ThridPlatformMgrSington.FindPlatformByPlatformBaseGameId(s.gameId) != nil {
			continue
		}
		if s.IsCoinScene() {
			if s.IsLongTimeInactive() && s.dbGameFree.GetCreateRoomNum() == 0 { //预创建的房间，暂不过期销毁，判定依据:CreateRoomNum>0
				logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive CoinScene ForceDelete scene:%v IsLongTimeInactive", s.sceneId)
				s.ForceDelete(false)
			}
		} else if s.IsPrivateScene() {
			if s.IsLongTimeInactive() {
				logger.Logger.Warnf("SceneMgr.DeleteLongTimeInactive PrivateScene ForceDelete scene:%v IsLongTimeInactive", s.sceneId)
				s.ForceDelete(false)
			}
		}
	}
}
