package base

import (
	"mongo.games.com/game/common"
	"mongo.games.com/game/proto"
	"mongo.games.com/game/protocol/gamehall"
	"mongo.games.com/game/protocol/server"
	srvlibproto "mongo.games.com/goserver/srvlib/protocol"
	"time"

	"mongo.games.com/goserver/core/logger"
	"mongo.games.com/goserver/core/module"
	"mongo.games.com/goserver/core/netlib"
)

var SceneMgrSington = &SceneMgr{
	scenes:           make(map[int]*Scene),
	scenesByGame:     make(map[int]map[int]*Scene),
	scenesByGameFree: make(map[int32]map[int]*Scene),
	PlatformScene:    make(map[string]bool),
}

type SceneMgr struct {
	scenes           map[int]*Scene
	scenesByGame     map[int]map[int]*Scene
	scenesByGameFree map[int32]map[int]*Scene
	lastSendJackPot  time.Time
	PlatformScene    map[string]bool
}

func (this *SceneMgr) makeKey(gameid, gamemode int) int {
	return int(gameid*10000 + gamemode)
}

func (this *SceneMgr) CreateScene(s *netlib.Session, sceneId, gameMode, sceneMode, gameId int, platform string,
	params []int32, agentor, creator int32, replayCode string, hallId, groupId, totalOfGames int32,
	dbGameFree *server.DB_GameFree, bEnterAfterStart bool, baseScore int32, playerNum int, chessRank []int32, paramsEx ...int32) *Scene {
	scene := NewScene(s, sceneId, gameMode, sceneMode, gameId, platform, params, agentor, creator, replayCode,
		hallId, groupId, totalOfGames, dbGameFree, bEnterAfterStart, baseScore, playerNum, chessRank, paramsEx...)
	if scene == nil {
		logger.Logger.Error("(this *SceneMgr) CreateScene, scene == nil")
		return nil
	}
	this.scenes[scene.SceneId] = scene
	if _, ok := this.PlatformScene[platform]; !ok {
		this.PlatformScene[platform] = true
	}
	//
	key := this.makeKey(gameId, gameMode)
	if ss, exist := this.scenesByGame[key]; exist {
		ss[scene.SceneId] = scene
	} else {
		ss = make(map[int]*Scene)
		ss[scene.SceneId] = scene
		this.scenesByGame[key] = ss
	}
	//
	if ss, exist := this.scenesByGameFree[dbGameFree.GetId()]; exist {
		ss[scene.SceneId] = scene
	} else {
		ss = make(map[int]*Scene)
		ss[scene.SceneId] = scene
		this.scenesByGameFree[dbGameFree.GetId()] = ss
	}
	scene.OnStart()
	logger.Logger.Infof("(this *SceneMgr) CreateScene,New scene,id:[%d] replaycode:[%v]", scene.SceneId, replayCode)
	return scene
}

func (this *SceneMgr) DestroyScene(sceneId int) {
	if scene, exist := this.scenes[sceneId]; exist {
		scene.OnStop()
		//
		key := this.makeKey(scene.GameId, scene.GameMode)
		if ss, exist := this.scenesByGame[key]; exist {
			delete(ss, scene.SceneId)
		}
		//
		if ss, exist := this.scenesByGameFree[scene.GetGameFreeId()]; exist {
			delete(ss, scene.SceneId)
		}
		delete(this.scenes, sceneId)
		logger.Logger.Infof("(this *SceneMgr) DestroyScene, sceneid = %v", sceneId)
	}
}

func (this *SceneMgr) GetPlayerNumByGameFree(platform string, gamefreeid, groupId int32) int32 {
	var num int32
	if ss, exist := SceneMgrSington.scenesByGameFree[gamefreeid]; exist {
		for _, scene := range ss {
			if groupId != 0 {
				if scene.GroupId == groupId {
					cnt := scene.GetRealPlayerCnt()
					num += int32(cnt)
				}
			} else {
				if scene.Platform == platform {
					cnt := scene.GetRealPlayerCnt()
					num += int32(cnt)
				}
			}
		}
	}
	return num
}

func (this *SceneMgr) GetPlayerNumByGame(platform string, gameid, gamemode, groupId int32) map[int32]int32 {
	nums := make(map[int32]int32)
	key := this.makeKey(int(gameid), int(gamemode))
	if ss, exist := SceneMgrSington.scenesByGame[key]; exist {
		for _, scene := range ss {
			if groupId != 0 {
				if scene.GroupId == groupId {
					cnt := scene.GetRealPlayerCnt()
					nums[scene.GetGameFreeId()] = nums[scene.GetGameFreeId()] + int32(cnt)
				}
			} else {
				if scene.Platform == platform {
					cnt := scene.GetRealPlayerCnt()
					nums[scene.GetGameFreeId()] = nums[scene.GetGameFreeId()] + int32(cnt)
				}
			}
		}
	}
	return nums
}

func (this *SceneMgr) GetPlayersByGameFree(platform string, gamefreeid int32) []*Player {
	players := make([]*Player, 0)
	if ss, exist := SceneMgrSington.scenesByGameFree[gamefreeid]; exist {
		for _, scene := range ss {
			if scene.Platform == platform {
				for _, p := range scene.Players {
					if p != nil {
						players = append(players, p)
					}
				}
			}
		}
	}
	return players
}

func (this *SceneMgr) GetScene(sceneId int) *Scene {
	if s, exist := this.scenes[sceneId]; exist {
		return s
	}
	return nil
}
func (this *SceneMgr) GetSceneByGameId(platform string, gameId int32) []*Scene {
	key := this.makeKey(int(gameId), 0)
	var ss []*Scene
	if data, ok := this.scenesByGame[key]; ok {
		for _, scene := range data {
			if scene.Platform == platform {
				ss = append(ss, scene)
			}
		}
	}
	return ss
}
func (this *SceneMgr) JackPotSync(platform string, gameIds ...int32) {
	for _, gameId := range gameIds {
		ss := this.GetSceneByGameId(platform, gameId)
		if ss != nil {
			mgs := make(map[*netlib.Session][]*srvlibproto.MCSessionUnion)
			pack := &gamehall.SCHundredSceneGetGameJackpot{}
			for _, s := range ss {
				val := s.sp.GetJackPotVal(s)
				if val > 0 {
					jpfi := &gamehall.GameJackpotFundInfo{
						GameFreeId:  proto.Int32(s.DbGameFree.Id),
						JackPotFund: proto.Int64(val),
					}
					pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi)
				}
				for _, p := range s.Players {
					if p != nil && p.gateSess != nil && p.IsOnLine() {
						mgs[p.gateSess] = append(mgs[p.gateSess], &srvlibproto.MCSessionUnion{
							Mccs: &srvlibproto.MCClientSession{
								SId: proto.Int64(p.sid),
							},
						})
					}
				}
			}
			if len(pack.GameJackpotFund) == 0 || len(mgs) == 0 {
				continue
			}
			proto.SetDefaults(pack)
			//以平台为标识向该平台内所有玩家广播奖池变动消息，游戏内外的玩家可监听该消息，减少由gamesrv向worldsrv转发这一步
			//tags := []string{platform}
			//logger.Logger.Trace("jackpot avengers", pack)
			//PlayerMgrSington.BroadcastMessageToGroup(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, tags)

			for gateSess, v := range mgs {
				if gateSess != nil && len(v) != 0 {
					cPack, err := MulticastMaker.CreateMulticastPacket(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, v...)
					if err == nil {
						proto.SetDefaults(cPack)
						gateSess.Send(int(srvlibproto.SrvlibPacketID_PACKET_SS_MULTICAST), cPack)
					}
				}
			}
		}
	}
}
func (this *SceneMgr) OnMiniTimer() {
	for _, scene := range this.scenes {
		scene.SyncPlayerCoin()
	}
}

func (this *SceneMgr) OnHourTimer() {
	//	for _, scene := range this.scenes {
	//		scene.OnHourTimer()
	//	}
}

func (this *SceneMgr) OnDayTimer() {
	logger.Logger.Info("(this *SceneMgr) OnDayTimer")
	//	for _, scene := range this.scenes {
	//		scene.OnDayTimer()
	//	}
	PlayerMgrSington.OnDayTimer()
}

func (this *SceneMgr) OnWeekTimer() {
	logger.Logger.Info("(this *SceneMgr) OnWeekTimer")
	//	for _, scene := range this.scenes {
	//		scene.OnWeekTimer()
	//	}
}

func (this *SceneMgr) OnMonthTimer() {
	logger.Logger.Info("(this *SceneMgr) OnMonthTimer")
	//	for _, scene := range this.scenes {
	//		scene.OnMonthTimer()
	//	}
}

func (this *SceneMgr) RebindPlayerSnId(oldSnId, newSnId int32) {
	for _, s := range this.scenes {
		s.RebindPlayerSnId(oldSnId, newSnId)
	}
}

func (this *SceneMgr) DestoryAllScene() {
	for _, s := range this.scenes {
		s.Destroy(true)
	}
}

// //////////////////////////////////////////////////////////////////
// / Module Implement [beg]
// //////////////////////////////////////////////////////////////////
func (this *SceneMgr) ModuleName() string {
	return "SceneMgr"
}

func (this *SceneMgr) Init() {
	//RestoreMDump(dumpFileName)
	this.lastSendJackPot = time.Now()
}

func (this *SceneMgr) Update() {
	for _, s := range this.scenes {
		s.OnTick()
	}
	if time.Now().Sub(this.lastSendJackPot) > 0 {
		this.lastSendJackPot = time.Now().Add(time.Second * 3)
		for platform, _ := range this.PlatformScene {
			if platform != common.Platform_Sys {
				this.JackPotSync(platform,
					common.GameId_Avengers,
					common.GameId_IceAge,
					common.GameId_EasterIsland,
					common.GameId_CaiShen,
					common.GameId_TamQuoc)
			}
		}
	}
}

func (this *SceneMgr) Shutdown() {
	//WriteMDump(dumpFileName)
	for _, s := range this.scenes {
		s.Destroy(true)
	}
	module.UnregisteModule(this)
}

func init() {
	module.RegisteModule(SceneMgrSington, time.Millisecond*50, 0)
	//RegisteDayTimeChangeListener(SceneMgrSington)
}
