package main

import (
	"strconv"
	"time"

	"mongo.games.com/goserver/core"
	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/module"
	"mongo.games.com/goserver/core/task"

	"mongo.games.com/game/model"
	"mongo.games.com/game/worldsrv/internal"
)

const (
	// InvalidPlayerCacheSec 数据不存在，60秒内不再查询
	InvalidPlayerCacheSec int64 = 60

	InvalidPlayerCacheMax int   = 100000
	ListNumber            int32 = 300
)

func init() {
	module.RegisteModule(PlayerCacheMgrSingleton, time.Second, 0)
	//GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGIN, PlayerCacheMgrSingleton)
	//GameEventHandlerMgrSingleton.RegisteGameEventHandler(GAMEEVENT_LOGOUT, PlayerCacheMgrSingleton)
}

var PlayerCacheMgrSingleton = &PlayerCacheMgr{
	playerMap:        make(map[int32]*PlayerCacheItem),
	playerCbs:        make(map[int32][]func(*PlayerCacheItem, bool, bool)),
	playerInvalidIds: make(map[int32]int64),
	playerWaitClr:    make([]*PlayerCacheItem, 0, 128),
	DbSaver: &DbSaver{
		Tick:  ListNumber,
		index: 0,
		init:  false,
		list:  make([]*SaverArray, ListNumber),
		queue: make([]*BalanceQueue, 10),
		pool:  make(map[SaveTaskHandler]*SaverArray),
	},
}

type PlayerCacheItem struct {
	*model.PlayerData
}

func (p *PlayerCacheItem) CanDel() bool {
	//return !p.isOnline && time.Now().Unix()-p.lastTs > int64(ListNumber)
	return true
}

func (p *PlayerCacheItem) Time2Save() {
	// 并没有对缓存数据做什么操作，只是释放缓存
	if p.CanDel() {
		PlayerCacheMgrSingleton.playerWaitClr = append(PlayerCacheMgrSingleton.playerWaitClr, p)
	}
}

type PlayerCacheMgr struct {
	*DbSaver
	playerMap        map[int32]*PlayerCacheItem                     // snid; 玩家信息缓存
	playerCbs        map[int32][]func(*PlayerCacheItem, bool, bool) // snid; 等待执行的回掉方法
	playerInvalidIds map[int32]int64                                // snid; 防止频繁访问数据库
	playerWaitClr    []*PlayerCacheItem                             // 根据DbSaver缓冲失效策略释放玩家数据
}

func (c *PlayerCacheMgr) Get(plt string, snid int32, cb func(*PlayerCacheItem, bool, bool), createIfNotExist bool) {
	// 1.玩家缓冲数据存在
	if p, exist := c.playerMap[snid]; exist {
		cb(p, false, false)
		return
	}

	// 2.查询频率限制(限制数据库没有数据的情况)
	// 玩家不存在，避免打到db上
	// 数据库查询失败或没有玩家信息就不要重复查了
	if lastTs, exist := c.playerInvalidIds[snid]; exist {
		if time.Now().Unix()-lastTs < InvalidPlayerCacheSec {
			cb(nil, false, false)
			return
		}
		delete(c.playerInvalidIds, snid)
	}

	// 3.有正在查询中，等待查询结果
	// 查询队列，如果已经有在查数据库了，就等待数据库查询结果
	if cbs, exist := c.playerCbs[snid]; exist {
		cbs = append(cbs, cb)
		c.playerCbs[snid] = cbs
		return
	}
	c.playerCbs[snid] = []func(*PlayerCacheItem, bool, bool){cb}

	var isnew bool
	var replays []*internal.PlayerLoadReplay
	task.New(core.CoreObject(), task.CallableWrapper(func(o *basic.Object) interface{} {
		pi, flag := model.GetPlayerDataBySnId(plt, snid, true, createIfNotExist)
		isnew = flag
		for _, v := range internal.GetPlayerLoads() {
			replays = append(replays, v.Load(plt, snid, pi))
		}
		return pi
	}), task.CompleteNotifyWrapper(func(data interface{}, tt task.Task) {
		su := true
		pi, ok := data.(*model.PlayerData)
		if !ok || pi == nil {
			su = false
		}

		if su {
			for _, v := range replays {
				if v == nil {
					continue
				}
				if v.Err != nil {
					su = false
					break
				}
			}
		}

		if !su {
			c.cacheInvalidPlayerId(snid)
			delete(c.playerCbs, snid)
			for _, cb := range c.playerCbs[snid] {
				cb(nil, true, false)
			}
			return
		}

		if len(replays) == len(internal.GetPlayerLoads()) {
			for k, v := range internal.GetPlayerLoads() {
				if v == nil || replays[k] == nil {
					continue
				}
				v.Callback(pi, replays[k])
			}
		}

		// 查询成功，缓存数据，执行cb方法
		item, ok := c.playerMap[snid]
		if !ok {
			item = &PlayerCacheItem{PlayerData: pi}
			c.playerMap[snid] = item
			c.RegisterDbSaverTask(item)
		}
		if cbs, exist := c.playerCbs[snid]; exist {
			delete(c.playerCbs, snid)
			for _, cb := range cbs {
				cb(item, true, isnew)
			}
		}

	}), "PlayerCacheMgr.Get").StartByExecutor(strconv.Itoa(int(snid)))
}

func (c *PlayerCacheMgr) GetMore(plt string, snid []int32, cb func([]*PlayerCacheItem, bool)) {
	isAsyn := false
	count := len(snid)
	result := make([]*PlayerCacheItem, 0, count)
	innerCb := func(item *PlayerCacheItem, asyn, isnew bool) {
		if item != nil {
			result = append(result, item)
		}
		if asyn {
			isAsyn = true
		}
		count--
		if count == 0 {
			cb(result, isAsyn)
		}
	}
	for _, id := range snid {
		c.Get(plt, id, innerCb, false)
	}
}

func (c *PlayerCacheMgr) cacheInvalidPlayerId(snid int32) {
	if len(c.playerInvalidIds) >= InvalidPlayerCacheMax {
		for id := range c.playerInvalidIds {
			delete(c.playerInvalidIds, id)
			if len(c.playerInvalidIds) < InvalidPlayerCacheMax {
				break
			}
		}
	}
	c.playerInvalidIds[snid] = time.Now().Unix()
}

// UnCacheInvalidPlayerId 解除一次玩家数据查询频率限制
// 玩家数据不存在，限制查询频率，避免查询过于频繁
// 但是当玩家数据确认存在后，可以解除限制，提前允许查询到玩家数据
func (c *PlayerCacheMgr) UnCacheInvalidPlayerId(snid int32) {
	delete(c.playerInvalidIds, snid)
}

func (c *PlayerCacheMgr) ModuleName() string {
	return "PlayerCacheMgr"
}

func (c *PlayerCacheMgr) Init() {
	c.DbSaver.Init()
}

func (c *PlayerCacheMgr) Update() {
	// 执行Time2Save之后清除缓存
	c.DbSaver.Update()
	for _, p := range c.playerWaitClr {
		delete(c.playerMap, p.SnId)
		c.UnregisteDbSaveTask(p)
	}
	c.playerWaitClr = c.playerWaitClr[0:0]
}

func (c *PlayerCacheMgr) Shutdown() {
	module.UnregisteModule(c)
}
