package game

import (
	"fmt"
	"math"
	"server/conf"
	mergeDataCfg "server/conf/merge_data"
	orderCfg "server/conf/order"
	"server/game/mod/item"
	limitedTimeEvent "server/game/mod/limited_time_event"
	"server/game/mod/order"
	"server/game/mod/quest"
	GoUtil "server/game_util"
	"server/msg"
	"server/pkg/github.com/name5566/leaf/log"
	"sort"

	"google.golang.org/protobuf/proto"
)

type PlayerChessData struct {
	Data      msg.ResPlayerChessData
	ColorData msg.ResChessColorData
}

func (p *PlayerChessData) LoadDataFromDB(dwUin interface{}) bool {

	return true
}

func (p *PlayerChessData) Reconnect(b bool) {

}

func (p *PlayerChessData) SaveDataFromDB(Key interface{}) bool {

	return true
}

func (p *PlayerChessData) ResPlayerChessData(player *Player) {
	agent := player.GetAgentByPlayer()
	Msg := &p.Data
	ChessMod := player.PlayMod.getChessMod()
	Msg.ChessList = ChessMod.BackData().ChessList
	Msg.MChessData = ChessMod.ChessMap
	data, _ := proto.Marshal(Msg)
	G_getGameLogic().PackResInfo(agent, "ResPlayerChessData", data)
}

func (p *PlayerChessData) ResChessColorData(player *Player) {
	agent := player.GetAgentByPlayer()
	data, _ := proto.Marshal(&p.ColorData)
	G_getGameLogic().PackResInfo(agent, "ResChessColorData", data)
}

// 同步棋盘数据
func (p *PlayerChessData) UpdatePlayerChessData(player *Player, buf []byte) error {
	update := &msg.UpdatePlayerChessData{}
	proto.Unmarshal(buf, update)
	LastMap := player.PlayMod.getChessMod().ChessMap
	AddChessList, AddNewEmit, err := p.HandleChess(player, update.MChessHandle)

	if err != nil {
		res := &msg.ResUpdatePlayerChessData{
			Code: msg.RES_CODE_FAIL,
			Msg:  err.Error(),
		}
		HandleStr := ""
		sort.Slice(update.MChessHandle, func(i, j int) bool {
			return update.MChessHandle[i].Id < update.MChessHandle[j].Id
		})
		for _, v := range update.MChessHandle {
			HandleStr += fmt.Sprintf("%v-%v-%v,", v.Id, v.ChessId, v.Type)
		}
		log.Debug("棋子操作队列错误, %v===%v===%v===%v", HandleStr, LastMap, update.MChessData, player.PlayMod.getChessMod().GetChessList())
		player.SendErrClienRes(res)
		return err
	}
	player.PushClientRes(&msg.ResUpdatePlayerChessData{
		Code: msg.RES_CODE_SUCCESS,
	})
	p.Data.MChessData = update.MChessData
	if !p.checkChessEqual(player) {
		res := &msg.ResUpdatePlayerChessData{
			Code: msg.RES_CODE_FAIL,
			Msg:  "棋子数据不一致",
		}
		HandleStr := ""
		sort.Slice(update.MChessHandle, func(i, j int) bool {
			return update.MChessHandle[i].Id < update.MChessHandle[j].Id
		})
		for _, v := range update.MChessHandle {
			HandleStr += fmt.Sprintf("%v-%v-%v,", v.Id, v.ChessId, v.Type)
		}
		log.Debug("棋子数据不一致地图, %v===%v===%v===%v", HandleStr, LastMap, update.MChessData, player.PlayMod.getChessMod().GetChessList())
		player.SendErrClienRes(res)
		player.TeLog("outsync_event", map[string]interface{}{
			"outsync_event": "UpdatePlayerChessDataFunc",
		})
		return fmt.Errorf("棋子数据不一致")
	}
	ChessMod := player.PlayMod.getChessMod()
	BaseMod := player.PlayMod.getBaseMod()
	OrderMod := player.PlayMod.getOrderMod()
	ChessMod.ChessMap = update.MChessData
	Update := OrderMod.CreateExtraOrder(AddChessList, AddNewEmit, ChessMod.GetUnlockChessList(), BaseMod.GetEnergyMul(), BaseMod.GetLevel())
	if Update {
		player.InitOrderItem()
		player.PushClientRes(OrderMod.BackData())
	}
	return nil
}

func (p *PlayerChessData) UpdateChessData(player *Player, MChessData map[string]int32) error {
	p.Data.MChessData = MChessData
	if !p.checkChessEqual(player) {
		res := &msg.ResUpdatePlayerChessData{
			Code: msg.RES_CODE_FAIL,
			Msg:  "棋子数据不一致",
		}
		log.Debug("棋子数据不一致地图, %v---%v---%v", player.PlayMod.getChessMod().ChessMap, p.Data.MChessData, player.PlayMod.getChessMod().GetChessList())
		player.SendErrClienRes(res)
		player.TeLog("outsync_event", map[string]interface{}{
			"outsync_event": "UpdatePlayerChessDataFunc",
		})
		return fmt.Errorf("棋子数据不一致")
	}
	player.PlayMod.getChessMod().ChessMap = MChessData
	return nil
}

// 检查棋子数据是否一致
func (p *PlayerChessData) checkChessEqual(player *Player) bool {
	if len(p.Data.MChessData) == 0 && conf.Server.GameName == "Merge_Pet_Local" {
		return true
	}
	ChessList := player.PlayMod.getChessMod().GetChessList()
	if len(ChessList) != len(p.Data.MChessData) {
		return false
	}
	aCopy := make([]int, 0, len(ChessList))
	bCopy := make([]int, 0, len(p.Data.MChessData))
	for _, v := range ChessList {
		aCopy = append(aCopy, int(v))
	}
	for _, v := range p.Data.MChessData {
		bCopy = append(bCopy, int(v))
	}
	isEqual := SlicesEqual(aCopy, bCopy)

	if isEqual {
		return true
	}
	// 找出aCopy多的元素和少的元素
	aMap := make(map[int]int)
	bMap := make(map[int]int)

	for _, v := range aCopy {
		aMap[v]++
	}
	for _, v := range bCopy {
		bMap[v]++
	}

	var extra []int
	var missing []int

	// 找出aCopy多的元素
	for k, v := range aMap {
		if bMap[k] < v {
			for i := 0; i < v-bMap[k]; i++ {
				extra = append(extra, k)
			}
		}
	}

	// 找出aCopy少的元素
	for k, v := range bMap {
		if aMap[k] < v {
			for i := 0; i < v-aMap[k]; i++ {
				missing = append(missing, k)
			}
		}
	}

	if len(extra) > 0 || len(missing) > 0 {
		log.Debug("棋子数据对比: aCopy多的元素=%v, aCopy少的元素=%v", extra, missing)
	}
	return false
}

// 棋子操作
func (p *PlayerChessData) HandleChess(player *Player, handle_list []*msg.ChessHandle) ([]int, []int, error) {
	sort.Slice(handle_list, func(i, j int) bool {
		return handle_list[i].Id < handle_list[j].Id
	})
	ChessMod := player.PlayMod.getChessMod()
	BaseMod := player.PlayMod.getBaseMod()
	HandbookMod := player.PlayMod.getHandbookMod()
	var addChessCostEnergy int
	var buyChess int
	LimitPush := false // 限时事件推送
	var TriggerList []*quest.Trigger
	var itemList []*item.Item
	var AddChessList []int
	var AddNewEmit []int
	EmitList := ChessMod.GetEmitList()
	for _, v := range handle_list {
		ChessId := int(v.ChessId)
		EmitId := int(v.Emit)
		switch v.Type {
		case msg.HANDLE_TYPE_ADD: //增加棋子
			err := ChessMod.AddChess(ChessId)
			if err != nil {
				return nil, nil, err
			}
			Type := mergeDataCfg.GetTypeById(ChessId)
			AddChessList = append(AddChessList, ChessId)
			if !GoUtil.InArray(EmitId, []int{561, 562, 563, 564, 701, 702, 703, 704, 705, 706, 0}) && Type != "Resource" { // 宝箱生成的棋子不扣体力
				addChessCostEnergy++
			}

			b := HandbookMod.SetHandbook(ChessId) // 添加图鉴
			if b {
				player.TeLog("collection_add", map[string]interface{}{
					"item_id":   ChessId,
					"item_name": mergeDataCfg.GetNameById(ChessId),
				})
				AddNewEmit = append(AddNewEmit, ChessId)
			}
			player.PushClientRes(HandbookMod.BackData())
		case msg.HANDLE_TYPE_COMPOSE: //合成棋子
			NewChessId, err := ChessMod.ComposeChess(ChessId)
			if err != nil {
				return nil, nil, err
			}
			b := HandbookMod.SetHandbook(NewChessId) // 添加图鉴
			if b {
				AddNewEmit = append(AddNewEmit, NewChessId)
			}
			AddChessList = append(AddChessList, NewChessId)
			NewChessIdLv := mergeDataCfg.GetLvById(NewChessId)
			TriggerList = append(TriggerList, &quest.Trigger{Label: quest.TRIGGER_LABEL_MERGELVTIME, A: []interface{}{NewChessIdLv}})
			TriggerList = append(TriggerList, &quest.Trigger{Label: quest.TRIGGER_LABEL_MERGETIME})
			player.PushClientRes(HandbookMod.BackData())
			triggerComposeChess(player, NewChessId, player.PlayMod.getBaseMod().EnergyMul, player.PlayMod.getChessMod().GetEmitList())
			// 获取活动道具
			ActItem := player.GetActivityItem(GoUtil.Int32ToInt(v.ActType))
			itemList = item.Merge(itemList, ActItem)
		case msg.HANDLE_TYPE_BUY: //购买棋子
			loseGold, err := ChessMod.BuyChess(ChessId)
			if err != nil {
				return nil, nil, err
			}
			AddChessList = append(AddChessList, ChessId)
			buyChess += loseGold
			itemList = append(itemList, &item.Item{Id: item.ITEM_STAR_ID, Num: int(-loseGold)})
		case msg.HANDLE_TYPE_SELL: //出售棋子
			items, err := ChessMod.SellChess(ChessId)
			if checkChess(ChessId, BaseMod.GetEnergyMul(), ChessMod.GetEmitList()) { // 在订单中无法生成的棋子 出售获得原价宠物币
				items = []*item.Item{{Id: item.ITEM_STAR_ID, Num: mergeDataCfg.GetStarById(ChessId)}}
			}
			LimitedTimeEventMod := player.PlayMod.getLimitedTimeEventMod()
			ChessType := mergeDataCfg.GetTypeById(ChessId)
			if LimitedTimeEventMod.CheckExist(limitedTimeEvent.EVENT_TYPE_PAYBACK_DAY) && ChessType == "Product" { // 限时活动 返利日
				err = LimitedTimeEventMod.SubPaybackDay()
				if err == nil {
					items = []*item.Item{{Id: item.ITEM_STAR_ID, Num: mergeDataCfg.GetStarById(ChessId)}}
				}
				LimitPush = true
			}
			if err != nil {
				return nil, nil, err
			}
			get_star_num := 0
			if len(items) > 0 && items[0].Id == item.ITEM_STAR_ID {
				get_star_num = items[0].Num
			}
			player.TeLog("sell_item", map[string]interface{}{
				"merge_item_id": ChessId,
				"product_name":  mergeDataCfg.GetNameById(ChessId),
				"get_star_num":  get_star_num,
			})
			itemList = item.Merge(itemList, items)
			// 获取活动道具
			ActItem := player.GetActivityItem(GoUtil.Int32ToInt(v.ActType))
			itemList = item.Merge(itemList, ActItem)
		case msg.HANDLE_TYPE_REMOVE: //移除棋子
			ChessMod.RemoveChess(ChessId)
		}

	}
	//扣除体力
	EnergyPow := BaseMod.GetEnergyMul()
	ReduceEneny := 0
	if addChessCostEnergy > 0 {
		ReduceEneny = int(math.Pow(2, float64(EnergyPow))) * addChessCostEnergy
		itemList = append(itemList, &item.Item{Id: item.ITEM_ENERGY_ID, Num: -ReduceEneny})
	}
	err := player.HandleItem(itemList, msg.ITEM_POP_LABEL_HandleChess.String())
	if err != nil {
		return nil, nil, err
	}
	NewEmitList := ChessMod.GetEmitList()
	var EmitProductList []string
	for _, v := range EmitList {
		ColorList := mergeDataCfg.GetEmitProduceType(v)
		EmitProductList = append(EmitProductList, ColorList...)
	}
	var NewEmitProductList []string
	for _, v := range NewEmitList {
		ColorList := mergeDataCfg.GetEmitProduceType(v)
		NewEmitProductList = append(NewEmitProductList, ColorList...)
	}
	OrderMod := player.PlayMod.getOrderMod()
	if !GoUtil.SlicesEqualString(EmitProductList, NewEmitProductList) {
		// 发射器系列变化，重新洗牌
		OrderMod.ResetChessPool()
	}

	// 限时事件 小猫戏法
	LimitedTimeEventMod := player.PlayMod.getLimitedTimeEventMod()
	if LimitedTimeEventMod.CheckExist(limitedTimeEvent.EVENT_TYPE_CAT_TRICK) {
		LimitedTimeEventMod.AddCatTrickEnergy(ReduceEneny)
		LimitPush = true
	}
	if LimitPush {
		player.PushClientRes(LimitedTimeEventMod.BackData())
	}
	player.QuestTriggerList(TriggerList)
	player.EmitRetireTrigger1()
	player.InitOrderItem()
	player.PushClientRes(ChessMod.BackData())
	player.PlayMod.save()
	return AddChessList, AddNewEmit, nil
}

func (p *PlayerChessData) ClearData() bool {
	return true
}

func checkChess(ChessId, EnergyMul int, Emit []int) bool {
	if len(Emit) == 0 {
		return false
	}
	ChessIdLv := mergeDataCfg.GetLvById(ChessId)
	ChessIdType := mergeDataCfg.GetTypeById(ChessId)
	if ChessIdType != "Product" {
		return false
	}
	ChessIdColor := mergeDataCfg.GetColorById(ChessId)
	if ChessIdColor == "Star" || ChessIdColor == "Energy" || ChessIdColor == "Diamond" {
		return false
	}
	EmitId := 0
	for _, v := range Emit {
		ProduceColor := mergeDataCfg.GetEmitProduceType(v)
		if GoUtil.InStringArray(ChessIdColor, ProduceColor) {
			EmitId = v
			break
		}
	}
	_, Max := getChesslvRange(EmitId, EnergyMul, false)
	Adjust := mergeDataCfg.GetAdjust(EmitId, ChessIdColor, EnergyMul)
	return ChessIdLv > Max-Adjust
}
func getChesslvRange(Emit int, EnergyMul int, IsCharge bool) (int, int) {
	RandEmitLv := mergeDataCfg.GetLvById(Emit)
	EmitId := mergeDataCfg.GetEmitId(Emit)
	RandMaxLv := mergeDataCfg.GetMaxLvById(Emit)
	RandEmitMinLv := mergeDataCfg.GetEmitMinLvById(EmitId)
	Ratio := mergeDataCfg.GetEmitRatio(EmitId)
	m := int(float64(1+RandEmitLv-RandEmitMinLv) / float64(1+RandMaxLv-RandEmitMinLv) / Ratio * 100)
	if IsCharge {
		m += 10
	}
	m = max(1, m)
	m = min(100, m)
	ChessMinLev := orderCfg.GetLvMin(EnergyMul)
	ChessMaxLev := orderCfg.GetLvMax(EnergyMul, m)
	return ChessMinLev, ChessMaxLev
}

func triggerComposeChess(player *Player, ChessId, EnergyMul int, Emit []int) {
	OrderMod := player.PlayMod.getOrderMod()
	if OrderMod.Step < 12 {
		return
	}
	if !checkChess(ChessId, EnergyMul, Emit) {
		return
	}
	ChessMod := player.PlayMod.getChessMod()
	ChessList := ChessMod.GetUnlockChessList()
	ChessNum := 0
	for _, v := range ChessList {
		if v == ChessId {
			ChessNum++
		}
	}
	OrderNum := 0
	for _, v := range OrderMod.GetOrderList() {
		if v.Type == order.Clean_Order_type && v.MergeId[0] == ChessId {
			OrderNum++
		}
	}
	if OrderNum >= ChessNum {
		return
	}
	OrderMod.CreateCleanOrder2([]int{ChessId})
	player.PushClientRes(OrderMod.BackData())
}
