package main

import (
	"sort"

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

	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/protocol/gamehall"
)

const (
	MatchTrueManForbid   int32 = -1 //禁止匹配真人
	MatchTrueManPriority       = 1  //优先匹配真人
)

type BaseCoinScenePool struct {
}

func (this *BaseCoinScenePool) New() interface{} {
	return nil
}

// CanEnter 检查入场条件
func (this *BaseCoinScenePool) CanEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode {
	// 具体场次的入场规则
	canEnterFunc := GetCoinSceneCanEnterFunc(int(pool.dbGameFree.GetGameId()))
	if canEnterFunc != nil {
		ret := canEnterFunc(pool, p)
		if ret != gamehall.OpResultCode_OPRC_Sucess {
			return ret
		}
	}

	// 通用入场检测
	if pool.dbGameFree.GetLimitCoin() != 0 && int64(pool.dbGameFree.GetLimitCoin()) > p.Coin {
		return gamehall.OpResultCode_OPRC_CoinNotEnough
	}
	if pool.dbGameFree.GetMaxCoinLimit() != 0 && int64(pool.dbGameFree.GetMaxCoinLimit()) < p.Coin && !p.IsRob {
		return gamehall.OpResultCode_OPRC_CoinTooMore
	}

	return gamehall.OpResultCode_OPRC_Sucess
}

func (this *BaseCoinScenePool) CanAudienceEnter(pool *CoinScenePool, p *Player) gamehall.OpResultCode {
	return gamehall.OpResultCode_OPRC_Sucess
}

// PlayerEnter 玩家进入房间池
func (this *BaseCoinScenePool) PlayerEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) {
	// 配房规则
	// 如果全局为限制则看具体的游戏是否限制，如果全局为不限制则不考虑具体的游戏。
	sameIpLimit := !model.GameParamData.SameIpNoLimit
	if sameIpLimit && pool.dbGameFree.GetSameIpLimit() == 0 {
		sameIpLimit = false
	}

	// 通用匹配规则过滤
	matchTrueManRule := pool.dbGameFree.GetMatchTrueMan()

	scenes := make(map[int]*Scene)
	for sceneId, s := range pool.scenes {
		if s == nil || s.IsFull() || s.deleting {
			continue
		}

		if !common.Config.IsDevMode {
			// 禁止真人匹配
			if matchTrueManRule == MatchTrueManForbid && !p.IsRob && s.GetTruePlayerCnt() != 0 {
				continue
			}
			// 排除不能进的房间
			if common.InSliceInt32(exclude, int32(s.sceneId)) {
				continue
			}
			// 规避同ip的用户在一个房间内(GM除外)
			if sameIpLimit && p.GMLevel == 0 && s.HasSameIp(p.Ip) {
				continue
			}
			// 多少局只能禁止再配对
			if s.dbGameFree.GetSamePlaceLimit() > 0 && sceneLimitMgr.LimitSamePlace(p, s) {
				continue
			}
			// 牌局开始后禁止进入
			if sp, ok := s.sp.(*ScenePolicyData); ok {
				if s.starting && !sp.EnterAfterStart {
					continue
				}
			}
		}
		scenes[sceneId] = s
	}

	//优先黑白名单
	if scene == nil && len(scenes) != 0 {
		gameId := pool.dbGameFree.GetGameId()
		if p.WBLevel < 0 { //黑名单玩家
			var cntWhite int
			var cntLose int
			var whiteScene *Scene
			var loseScene *Scene
			for _, s := range scenes {
				if s != nil {
					if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) {
						continue
					}
					cnt := s.GetWhitePlayerCnt()
					if cnt > cntWhite {
						cntWhite = cnt
						whiteScene = s
					}
					cnt = s.GetLostPlayerCnt()
					if cnt > cntLose {
						cntLose = cnt
						loseScene = s
					}
				}
			}
			if whiteScene != nil {
				scene = whiteScene
			} else if loseScene != nil {
				scene = loseScene
			}
		} else if p.WBLevel > 0 { //白名单玩家
			var cntBlack int
			var cntWin int
			var blackScene *Scene
			var winScene *Scene
			for _, s := range scenes {
				if s != nil {
					if sceneLimitMgr.LimitAvgPlayer(s, len(pool.players)) {
						continue
					}
					cnt := s.GetBlackPlayerCnt()
					if cnt > cntBlack {
						cntBlack = cnt
						blackScene = s
					}
					cnt = s.GetWinPlayerCnt()
					if cnt > cntWin {
						cntWin = cnt
						winScene = s
					}

				}
			}
			if blackScene != nil {
				scene = blackScene
			} else if winScene != nil {
				scene = winScene
			}
		} else { //按类型匹配
			//优先真人
			if scene == nil && len(scenes) != 0 && matchTrueManRule == MatchTrueManPriority {
				var selScene []*Scene
				for _, value := range scenes {
					if value != nil {
						if value.GetTruePlayerCnt() > 0 && !value.IsFull() && !value.deleting {
							selScene = append(selScene, value)
						}
					}
				}
				if len(selScene) > 0 {
					sort.Slice(selScene, func(i, j int) bool {
						return selScene[i].GetTruePlayerCnt() > selScene[j].GetTruePlayerCnt()
					})
					scene = selScene[0]
				}
			}

			if scene == nil && len(scenes) != 0 {
				//1.其次游戏的配桌规则
				if scene == nil {
					matchFunc := GetCoinSceneMatchFunc(int(gameId))
					if matchFunc != nil {
						scene = matchFunc(pool, p, scenes, sameIpLimit, exclude)
						return gamehall.OpResultCode_OPRC_Sucess, scene
					}
				}
			}
		}
		// 随机
		if scene == nil {
			for _, v := range scenes {
				scene = v
				break
			}
		}
	}

	return gamehall.OpResultCode_OPRC_Sucess, scene
}

func (this *BaseCoinScenePool) AudienceEnter(pool *CoinScenePool, p *Player, exclude []int32, ischangeroom bool) (ret gamehall.OpResultCode, scene *Scene) {
	for _, s := range pool.scenes {
		if s != nil && len(s.players) != 0 && !s.deleting && !common.InSliceInt32(exclude, int32(s.sceneId)) {
			scene = s
			break
		}
	}
	return gamehall.OpResultCode_OPRC_Sucess, scene
}

func (this *BaseCoinScenePool) OnPlayerEnter(pool *CoinScenePool, p *Player, scene *Scene) {}

func (this *BaseCoinScenePool) PlayerLeave(pool *CoinScenePool, p *Player, reason int) bool {
	return true
}

func (this *BaseCoinScenePool) AudienceLeave(pool *CoinScenePool, p *Player, reason int) bool {
	return true
}

func (this *BaseCoinScenePool) OnPlayerLeave(pool *CoinScenePool, s *Scene, p *Player) {}

func (this *BaseCoinScenePool) NewScene(pool *CoinScenePool, p *Player) *Scene {
	gameId := int(pool.dbGameRule.GetGameId())
	gs := GameSessMgrSington.GetMinLoadSess(gameId)
	if gs == nil {
		logger.Logger.Errorf("Get %v game min session failed.", gameId)
		return nil
	}

	gameMode := pool.dbGameRule.GetGameMode()
	params := pool.dbGameRule.GetParams()
	var platformName string
	limitPlatform := PlatformMgrSingleton.GetPlatform(pool.platform)
	if limitPlatform == nil || !limitPlatform.Isolated {
		limitPlatform = PlatformMgrSingleton.GetPlatform(DefaultPlatform)
		platformName = DefaultPlatform
	} else {
		platformName = limitPlatform.IdStr
	}

	sceneId := SceneMgrSingleton.GenOneCoinSceneId()

	scene := SceneMgrSingleton.CreateScene(0, 0, sceneId, gameId, int(gameMode), int(common.SceneMode_Public),
		1, -1, params, gs, limitPlatform, pool.groupId, pool.dbGameFree, int32(pool.id))
	if scene != nil {
		scene.hallId = pool.id
		scene.csp = pool
		if pool.groupId != 0 {
			CoinSceneMgrSingleton.groupOfScene[int32(sceneId)] = pool.groupId
		} else {
			CoinSceneMgrSingleton.platformOfScene[int32(sceneId)] = platformName
		}
		return scene
	}
	return nil
}

func (this *BaseCoinScenePool) NewPreCreateScene(pool *CoinScenePool) *Scene {
	return this.NewScene(pool, nil)
}

func (this *BaseCoinScenePool) OnDestroyScene(pool *CoinScenePool, sceneId int) {}
