package iceage

import (
	"encoding/json"
	"math"
	"math/rand"
	"mongo.games.com/game/common"
	"mongo.games.com/game/model"
	"mongo.games.com/game/protocol/gamehall"
	"mongo.games.com/game/protocol/iceage"
	"mongo.games.com/game/protocol/server"
	"mongo.games.com/goserver/core/basic"
	"mongo.games.com/goserver/core/task"
	"time"

	rule "mongo.games.com/game/gamerule/iceage"
	"mongo.games.com/game/gamesrv/base"
	"mongo.games.com/game/proto"
	"mongo.games.com/game/srvdata"
	"mongo.games.com/goserver/core/logger"
)

type IceAgeJackpot struct {
	createdTime time.Time
	userName    string
	priceValue  int64
	roomID      int64
	spinID      string
}

type IceAgeSceneData struct {
	*base.Scene                                  //房间信息
	players          map[int32]*IceAgePlayerData //玩家信息
	jackpot          *base.SlotJackpotPool       //奖池
	lastJackpotValue int64                       //上一次奖池变化时的值
	lastJackPot      time.Time                   //增加奖池时间
	lastBurstJackPot map[int32]time.Time         //爆池时间
}

func NewIceAgeSceneData(s *base.Scene) *IceAgeSceneData {
	return &IceAgeSceneData{
		Scene:   s,
		players: make(map[int32]*IceAgePlayerData),
	}
}

func (this *IceAgeSceneData) SaveData(force bool) {
}

func (this *IceAgeSceneData) OnPlayerLeave(p *base.Player, reason int) {
	if p, exist := this.players[p.SnId]; exist {
		delete(this.players, p.SnId)
	}
}

func (this *IceAgeSceneData) SceneDestroy(force bool) {
	//销毁房间
	this.Scene.Destroy(force)
}

func (this *IceAgeSceneData) init() bool {
	if this.GetDBGameFree() == nil {
		return false
	}
	params := this.GetDBGameFree().GetJackpot()
	this.jackpot = &base.SlotJackpotPool{}
	if this.jackpot.Small <= 0 {
		this.jackpot.Small = 0
		this.jackpot.VirtualJK = int64(params[rule.ICEAGE_JACKPOT_InitJackpot] * this.DbGameFree.GetBaseScore())
	}

	str := base.SlotsPoolMgr.GetPool(this.GetGameFreeId(), this.GetPlatform())
	if str != "" {
		jackpot := &base.SlotJackpotPool{}
		err := json.Unmarshal([]byte(str), jackpot)
		if err == nil {
			this.jackpot = jackpot
		}
	}

	if this.jackpot != nil {
		base.SlotsPoolMgr.SetPool(this.GetGameFreeId(), this.GetPlatform(), this.jackpot)
	}
	this.lastJackPot = time.Now()
	this.lastBurstJackPot = make(map[int32]time.Time)
	this.SetLastBurstJackPot()
	return true
}

const (
	SlotsData      = "SlotsData"
	SlotsData_V2   = "SlotsData_V2"
	BonusData      = "BonusData"
	BonusData_V2   = "BonusData_V2"
	DefaultData    = "DefaultData"
	DefaultData_v1 = "DefaultData_v1"
)

func getElementDistributionByName(name string) *server.DB_IceAgeElementRate {
	for i := range srvdata.PBDB_IceAgeElementRateMgr.Datas.Arr {
		item := srvdata.PBDB_IceAgeElementRateMgr.GetData(int32(i + 1))
		if item.GetModeName() == name {
			return item
		}
	}
	return nil
}
func getSlotsDataByElementDistribution(data []int32) []int {
	var t = make([]int, rule.ELEMENT_TOTAL)
	for i, _ := range t {
		t[i] = int(data[rand.Intn(len(data))])
	}
	return t
}
func getSlotsDataByGroupName(name string) []int {
	cardLib := rule.MissData[name]
	if len(cardLib) > 0 {
		return cardLib[rand.Intn(len(cardLib))]
	}
	return nil
}

type SpinResult struct {
	LinesInfo         []*iceage.IceAgeLinesInfo
	SlotsData         []*iceage.IceAgeCards
	TotalPrizeLine    int64   // 线条总金额+爆奖
	TotalPrizeBonus   int64   // 小游戏总金额
	BonusGameCnt      int32   // 小游戏次数
	TotalPrizeJackpot int64   // 爆奖总金额
	JackpotCnt        int     // 爆奖的次数
	AddFreeTimes      int32   // 新增免费次数
	IsJackpot         bool    // 是否爆奖
	TotalWinRate      int32   // 中奖总倍率
	TotalTaxScore     int64   // 税收
	WinLines          [][]int // 赢分的线
}

func (this *IceAgeSceneData) CalcSpinsPrize(cards []int, betLines []int64, distributionData []int32, bonusParams []int32, betValue int64, taxRate int32) (spinRes SpinResult) {

	tmpCards := make([]int, len(cards))
	copy(tmpCards, cards)

	calcTaxScore := func(score int64, taxScore *int64) int64 {
		tmpTaxScore := int64(float64(score) * float64(taxRate) / 10000.0)
		*taxScore += tmpTaxScore
		return score - tmpTaxScore
	}

	for loopIndex := 0; loopIndex < 99; loopIndex++ { // 避免死循环
		var winLines []int
		slotData := &iceage.IceAgeCards{}
		for _, card := range tmpCards {
			slotData.Card = append(slotData.Card, int32(card))
		}
		spinRes.SlotsData = append(spinRes.SlotsData, slotData)

		lineCount := 0
		for _, lineNum := range betLines {
			lineTemplate := rule.AllLineArray[int(lineNum)-1]
			edata := []int{}
			epos := []int32{}
			for _, pos := range lineTemplate {
				edata = append(edata, tmpCards[pos])
				epos = append(epos, int32(pos)+1)
			}
			head, count := rule.IsLine(edata)
			if head == 0 || count <= 0 {
				continue
			}

			var spinFree, prizeJackpot, bonus int64
			var prizesBonus = make([]int64, 0)
			if head == rule.Element_FREESPIN {
				spinFree = int64(rule.FreeSpinTimesRate[count-1])
			} else if head == rule.Element_JACKPOT && count == 5 {
				spinRes.IsJackpot = true
				spinRes.JackpotCnt++
				if spinRes.TotalPrizeJackpot == 0 { // 第一个爆奖 获取当前奖池所有
					prizeJackpot = this.jackpot.VirtualJK
				} else { // 之后的爆奖 奖励为奖池初值
					prizeJackpot = int64(this.GetDBGameFree().GetJackpot()[rule.ICEAGE_JACKPOT_InitJackpot] * this.GetDBGameFree().GetBaseScore())
				}
				prizeJackpot = calcTaxScore(prizeJackpot, &spinRes.TotalTaxScore)
				spinRes.TotalPrizeJackpot += prizeJackpot
			} else if head == rule.Element_BONUS && count >= 3 {
				bonus = betValue * int64(len(betLines)) * int64(bonusParams[rand.Intn(len(bonusParams))])
				bonus = calcTaxScore(bonus, &spinRes.TotalTaxScore)
				prizesBonus = []int64{bonus, bonus, bonus} // len大于0即可
				spinRes.BonusGameCnt++
			}

			curScore := int64(rule.LineScore[head][count-1]) * betValue
			curScore = calcTaxScore(curScore, &spinRes.TotalTaxScore)
			spinRes.TotalWinRate += int32(rule.LineScore[head][count-1])
			spinRes.TotalPrizeLine += curScore + prizeJackpot
			spinRes.TotalPrizeBonus += bonus
			spinRes.AddFreeTimes += int32(spinFree)

			line := &iceage.IceAgeLinesInfo{
				LineID:         proto.Int32(int32(lineNum)),
				Turn:           proto.Int32(int32(loopIndex + 1)),
				PrizeValue:     proto.Int64(curScore + prizeJackpot),
				PrizesFreespin: proto.Int64(spinFree),
				PrizesJackport: proto.Int64(prizeJackpot),
				PrizesBonus:    prizesBonus,
				Items:          epos[:count],
				RoleID:         proto.Int32(int32(head)),
			}
			spinRes.LinesInfo = append(spinRes.LinesInfo, line)
			winLines = append(winLines, int(line.GetLineID()))
			lineCount++
		}
		if winLines != nil {
			spinRes.WinLines = append(spinRes.WinLines, winLines)
		}

		if loopIndex == 98 {
			logger.Logger.Error("IceAgeSpinTimes99", cards, betLines, distributionData, bonusParams, betValue, taxRate)
		}

		// 没有匹配的线条 不用消除处理
		if lineCount == 0 {
			break
		}

		// 获取消除后的新线
		tmpCards = rule.MakePlan(tmpCards, distributionData, betLines)
		if len(tmpCards) == 0 {
			break
		}
	}
	return
}

func (this *IceAgeSceneData) BroadcastJackpot(sync bool) {
	if this.lastJackpotValue != this.jackpot.VirtualJK || sync {
		this.lastJackpotValue = this.jackpot.VirtualJK
		pack := &gamehall.SCHundredSceneGetGameJackpot{}
		jpfi := &gamehall.GameJackpotFundInfo{
			GameFreeId:  proto.Int32(this.DbGameFree.Id),
			JackPotFund: proto.Int64(this.jackpot.VirtualJK),
		}
		pack.GameJackpotFund = append(pack.GameJackpotFund, jpfi)
		proto.SetDefaults(pack)
		//以平台为标识向该平台内所有玩家广播奖池变动消息，游戏内外的玩家可监听该消息，减少由gamesrv向worldsrv转发这一步
		tags := []string{this.Platform}
		logger.Logger.Trace("jackpot avengers", pack)
		base.PlayerMgrSington.BroadcastMessageToGroup(int(gamehall.HundredScenePacketID_PACKET_SC_GAMEJACKPOT), pack, tags)
	}
}
func (this *IceAgeSceneData) PushCoinPool(prizeFundAdd int64, IsNovice bool) {
	if IsNovice {
		base.CoinPoolMgr.PushCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd)
	} else {
		base.CoinPoolMgr.PushCoin(this.GetGameFreeId(), this.GroupId, this.Platform, prizeFundAdd)
	}
}
func (this *IceAgeSceneData) PopCoinPool(winCoin int64, IsNovice bool) {
	if IsNovice {
		base.CoinPoolMgr.PopCoinNovice(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin)
	} else {
		base.CoinPoolMgr.PopCoin(this.GetGameFreeId(), this.GroupId, this.Platform, winCoin)
	}
}
func (this *IceAgeSceneData) RecordBurstLog(name string, wincoin, totalbet int64) {
	log := model.NewBurstJackpotLog(this.Platform, this.DbGameFree.GameId, this.GetGameFreeId(), name, wincoin, totalbet)
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		return model.InsertBurstJackpotLogs(log)
	}), nil, "InsertBurstJackpotLogs").Start()
}

func (this *IceAgeSceneData) BurstHistory(player *IceAgePlayerData) {
	task.New(nil, task.CallableWrapper(func(o *basic.Object) interface{} {
		return model.GetBurstJackpotLog(this.Platform, this.DbGameFree.GameId)
	}), task.CompleteNotifyWrapper(func(data interface{}, t task.Task) {
		var logsp []*iceage.IceAgeBurstHistoryInfo
		if data != nil {
			logs := data.([]model.BurstJackpotLog)
			if len(logs) > 0 {
				for _, log := range logs {
					logsp = append(logsp, &iceage.IceAgeBurstHistoryInfo{
						UserName:   log.Name,
						PriceValue: log.WinCoin,
						TotalBet:   log.TotalBet,
						Ts:         log.Ts,
					})
				}
			}
		}
		pack := &iceage.SCIceAgeBurstHistoryInfo{
			BurstLog: logsp,
		}
		logger.Logger.Trace("SCIceAgeBurstHistoryInfo:", pack)
		player.SendToClient(int(iceage.IceAgePacketID_PACKET_SC_ICEAGE_BURSTHISTORY), pack)
	}), "BurstHistory").Start()
}
func (this *IceAgeSceneData) GetLastBurstJackPot() time.Time {
	return this.lastBurstJackPot[this.GetGameFreeId()]
}
func (this *IceAgeSceneData) SetLastBurstJackPot() {
	var randT = rand.Intn(25200-7200+1) + 7200
	switch this.DbGameFree.SceneType {
	case 1:
		randT = rand.Intn(25200-7200+1) + 7200
	case 2:
		randT = rand.Intn(108000-72000+1) + 72000
	case 3:
		randT = rand.Intn(180000-108000+1) + 108000
	}
	this.lastBurstJackPot[this.GetGameFreeId()] = time.Now().Add(time.Second * time.Duration(randT))
}
func (this *IceAgeSceneData) AIAddJackPot() {
	if time.Now().Sub(this.lastJackPot) > 0 {
		var randT = rand.Intn(3) + 1
		switch this.DbGameFree.SceneType {
		case 1:
			randT = rand.Intn(3) + 1
		case 2:
			randT = rand.Intn(12-5) + 6
		case 3:
			randT = rand.Intn(20-9) + 10
		default:
			randT = rand.Intn(3) + 1
		}
		this.lastJackPot = time.Now().Add(time.Second * time.Duration(randT))
		//AI机器人
		setting := base.CoinPoolMgr.GetCoinPoolSetting(this.Platform, this.GetGameFreeId(), this.GroupId)
		var ctroRate int32
		if setting != nil {
			ctroRate = 9800
		}
		var val int64
		val = int64(math.Floor(float64(this.DbGameFree.GetBaseScore()) * float64(rule.LINENUM) * float64(ctroRate) / 10000))
		this.jackpot.VirtualJK += val
	}
}
func (this *IceAgeSceneData) AIBurstJackPot() {
	if time.Now().Sub(this.GetLastBurstJackPot()) > 0 {
		this.SetLastBurstJackPot()
		jackpotParams := this.DbGameFree.GetJackpot()
		var jackpotInit = int64(jackpotParams[rule.ICEAGE_JACKPOT_InitJackpot] * this.DbGameFree.GetBaseScore()) //奖池初始值

		//AI机器人爆奖
		val := this.jackpot.VirtualJK
		this.jackpot.VirtualJK = jackpotInit
		bet := this.DbGameFree.GetBaseScore() * int32(rule.LINENUM)
		this.RecordBurstLog(this.RandNickName(), val, int64(bet))
	}
}
func (this *IceAgeSceneData) KickPlayerByTime() {
	if time.Now().Sub(this.GameStartTime) > time.Second*3 {
		this.GameStartTime = time.Now()
		for _, p := range this.players {
			if p.IsOnLine() {
				p.leavetime = 0
				continue
			}
			p.leavetime++
			if p.leavetime < 60 {
				continue
			}
			//踢出玩家
			this.PlayerLeave(p.Player, common.PlayerLeaveReason_LongTimeNoOp, true)
		}
		//for _, p := range this.players {
		//	//游戏次数达到目标值
		//	todayGamefreeIDSceneData, _ := p.GetDaliyGameData(int(this.DbGameFree.GetId()))
		//	if !p.IsRob &&
		//		todayGamefreeIDSceneData != nil &&
		//		this.DbGameFree.GetPlayNumLimit() != 0 &&
		//		todayGamefreeIDSceneData.GameTimes >= int64(this.DbGameFree.GetPlayNumLimit()) {
		//		this.PlayerLeave(p.Player, common.PlayerLeaveReason_GameTimes, true)
		//	}
		//}
		if this.CheckNeedDestroy() {
			for _, player := range this.players {
				if !player.IsRob {
					if time.Now().Sub(player.LastOPTimer) > 10*time.Second {
						//离开有统计
						this.PlayerLeave(player.Player, common.PlayerLeaveReason_OnDestroy, true)
					}
				}
			}
			if this.GetRealPlayerCnt() == 0 {
				this.SceneDestroy(true)
			}
		}
	}
}
