package decorate

import (
	"fmt"
	"math"
	decorateCfg "server/conf/decorate"
	limitedTimeEventCfg "server/conf/limited_time_event"
	"server/game/mod/item"
	GoUtil "server/game_util"
	"server/msg"
	"sort"
)

type Decorate struct {
	AreaId        int
	FinishList    map[int]struct{}
	Progress      int
	DecorateNum   int
	AreaReward    []int
	PartPool      map[int]int
	PartClassPool []int
	PartCost      map[int]*PartCostInfo
	LastPart      []int
}

type PartCostInfo struct {
	Items []*item.Item
}

func (d *Decorate) InitData() {
	if d.AreaId == 0 {
		d.AreaId = 1
		d.FinishList = make(map[int]struct{})
	}
	if d.PartCost == nil {
		d.PartCost = make(map[int]*PartCostInfo)
	}
	if d.PartPool == nil {
		d.PartPool = make(map[int]int)
	}
	if len(d.PartCost) == 0 {
		d.initPartCost(d.AreaId)
	}
	d.prunePartCost()
}

// prunePartCost 删除 PartCost 中属于已过区域的条目，若清空则初始化当前区域的零件消耗
func (d *Decorate) prunePartCost() {
	for k := range d.PartCost {
		if decorateCfg.GetAreaIdByIndoorId(k) < d.AreaId {
			delete(d.PartCost, k)
		}
	}
	if len(d.PartCost) == 0 {
		d.initPartCost(d.AreaId)
	}
}

// 装饰
func (d *Decorate) Decorate(areaId int, decorateId int) ([]*item.Item, error) {
	if d.AreaId != areaId {
		return nil, fmt.Errorf("areaId not match")
	}
	if _, ok := d.FinishList[decorateId]; ok {
		return nil, fmt.Errorf("decorateId already finished")
	}
	SortList := decorateCfg.GetAllSortIdByAreaId(areaId)
	if !GoUtil.InArray(decorateId, SortList) {
		return nil, fmt.Errorf("decorateId not in areaId")
	}
	d.FinishList[decorateId] = struct{}{}
	d.Progress++
	Item := decorateCfg.GetProgressReward(areaId, d.Progress)
	Item1 := decorateCfg.GetDecoarteReward(areaId, decorateId)
	Item = append(Item, Item1...)
	if d.Progress >= len(SortList) {
		d.AreaId++
		d.Progress = 0
		d.FinishList = make(map[int]struct{})
		d.prunePartCost()
	}

	d.DecorateNum++
	return Item, nil
}

// 获取装饰增加体力
func (d *Decorate) GetDecorateAddEnergy(cnt int) []*item.Item {
	EnergyNum := decorateCfg.GetDecorateAddEnergy()
	return []*item.Item{item.NewItem(item.ITEM_ENERGY_ID, EnergyNum*cnt)}
}

// 获取装饰消耗
func (d *Decorate) GetDecorateCostItem(AreaId, DecorateId int, DecorateOffIsExist bool) ([]*item.Item, int, []*item.Item) {
	Item := decorateCfg.GetStarCost(AreaId, DecorateId)
	PetExp := decorateCfg.GetDecoratePExp(AreaId, DecorateId)
	Id := decorateCfg.GetIdBySenceAndLv(AreaId, DecorateId)
	if DecorateOffIsExist {
		OffRate := limitedTimeEventCfg.GetDecorateOffDiscount(AreaId, DecorateId)
		Item = int(math.Ceil(float64(Item) * float64(OffRate)))
	}
	Items := []*item.Item{item.NewItem(item.ITEM_STAR_ID, Item)}
	PartItem := d.PartCost[Id]
	PartItemList := make([]*item.Item, 0)
	if PartItem != nil {
		Items = append(Items, PartItem.Items...)
		PartItemList = PartItem.Items
		delete(d.PartCost, Id)
	}
	// 清理旧区域零件；若当前区域全部消耗完则预加载下一区域
	for k := range d.PartCost {
		if decorateCfg.GetAreaIdByIndoorId(k) < d.AreaId {
			delete(d.PartCost, k)
		}
	}
	if len(d.PartCost) == 0 {
		d.initPartCost(d.AreaId + 1)
	}
	return Items, PetExp, PartItemList
}

// 一键装饰
func (d *Decorate) DecorateAll(Star int, DecorateOffIsExist bool) ([]*item.Item, []*item.Item, int, []int, []interface{}, int) {
	DecorateAll := decorateCfg.GetAllSortIdByAreaId(d.AreaId)
	SubAlice := GoUtil.SubSlices(DecorateAll, GoUtil.MapIntToIntSlice(d.FinishList))
	sort.Ints(SubAlice)
	SubItem := 0
	SubItems := make([]*item.Item, 0)
	AddItem := make([]*item.Item, 0)
	DecorateList := make([]int, 0)
	Log := make([]interface{}, 0)
	Num := 0
	PetExp := 0
	for _, v := range SubAlice {
		Id := decorateCfg.GetIdBySenceAndLv(d.AreaId, v)
		PartItem := d.PartCost[Id]
		if PartItem != nil {
			break
		}
		NeedStar := decorateCfg.GetStarCost(d.AreaId, v)
		if DecorateOffIsExist {
			OffRate := limitedTimeEventCfg.GetDecorateOffDiscount(d.AreaId, v)
			NeedStar = int(math.Ceil(float64(NeedStar) * float64(OffRate)))
		}
		if Star < NeedStar {
			break
		}
		Star -= NeedStar
		SubItem += NeedStar
		d.FinishList[v] = struct{}{}
		d.Progress++
		d.DecorateNum++
		Num++
		Item := decorateCfg.GetProgressReward(d.AreaId, d.Progress)
		Item = append(Item, decorateCfg.GetDecoarteReward(d.AreaId, v)...)
		if len(Item) > 0 {
			if d.Progress >= len(DecorateAll) {
				Log = append(Log, map[string]interface{}{
					"deco_step_id": fmt.Sprintf("%d_%d", d.AreaId+1, 0),
					"item_list":    Item,
				})
			} else {
				Log = append(Log, map[string]interface{}{
					"deco_step_id": fmt.Sprintf("%d_%d", d.AreaId, d.Progress),
					"item_list":    Item,
				})
			}

		}
		PetExp += decorateCfg.GetDecoratePExp(d.AreaId, v)
		AddItem = append(AddItem, Item...)
		DecorateList = append(DecorateList, v)
	}

	if d.Progress >= len(DecorateAll) {
		d.AreaId++
		d.Progress = 0
		d.FinishList = make(map[int]struct{})
		d.prunePartCost()
	}
	SubItems = append(SubItems, item.NewItem(item.ITEM_STAR_ID, SubItem))
	return SubItems, AddItem, Num, DecorateList, Log, PetExp
}

// 获取装饰信息
func (d *Decorate) BackData() *msg.ResDecorateInfo {
	var PartRes []*msg.DecoratePart
	for k, v := range d.PartCost {
		PartRes = append(PartRes, &msg.DecoratePart{
			Id:    int32(k),
			Items: item.ItemToMsg(v.Items),
		})
	}
	return &msg.ResDecorateInfo{
		AreaId:      int32(d.AreaId),
		MFinishList: GoUtil.MapIntToSlice(d.FinishList),
		RewardArea:  GoUtil.IntToInt32(d.AreaReward),
		Parts:       PartRes,
	}
}

// 获取装饰奖励
func (d *Decorate) GetDecorateReward(areaId int) ([]*item.Item, error) {
	if GoUtil.InArray(areaId, d.AreaReward) {
		return nil, fmt.Errorf("areaId already rewarded")
	}
	d.AreaReward = append(d.AreaReward, areaId)
	return decorateCfg.GetAreaReward(areaId), nil
}

func (d *Decorate) GetAreaId() int {
	return d.AreaId
}

func (d *Decorate) GetProgress() int {
	return d.Progress
}

func (d *Decorate) GetDecorateNum() int {
	return d.DecorateNum
}

// 获取零件
func (d *Decorate) GetPart(Num int) map[int]int {
	d.initPartPool()
	res := make(map[int]int)
	ClassNum := min(Num, 3)
	ClassList := d.GetPartClass(ClassNum)
	NewClassList := d.sortClass(ClassList)
	for i := range Num {
		Class := NewClassList[i%ClassNum]
		d.PartPool[Class]--
		res[Class]++
	}
	return res
}

func (d *Decorate) sortClass(Class []int) []int {
	d.initPartPool()
	type sortItem struct {
		Id  int
		Num int
	}
	sortList := make([]sortItem, len(Class))
	for i, v := range Class {
		sortList[i] = sortItem{Id: v, Num: d.PartPool[v]}
	}
	sort.Slice(sortList, func(i, j int) bool {
		return sortList[i].Num > sortList[j].Num
	})
	res := make([]int, len(Class))
	for i, v := range sortList {
		res[i] = v.Id
	}
	return res
}

func (d *Decorate) GetPartClass(Num int) []int {
	res := make([]int, 0, Num)
	for range Num {
		if len(d.PartClassPool) == 0 {
			d.initPartClassPool()
		}
		Id, newPool := GoUtil.PopSlice(d.PartClassPool)
		d.PartClassPool = newPool
		res = append(res, Id)
	}
	return res
}

// 初始化零件类别池，优先保证每个类别至少出现一次
func (d *Decorate) initPartClassPool() {
	if len(d.PartClassPool) == 0 {
		ClassList := decorateCfg.GetAllPartId()
		r1 := GoUtil.SubSlices(ClassList, d.LastPart)
		c1 := GoUtil.RandSlice(r1)
		r2 := GoUtil.SubSlices(ClassList, []int{c1})
		r3 := GoUtil.ShuffleArray(r2)
		d.PartClassPool = append([]int{c1}, r3...)
	}
}

func (d *Decorate) initPartPool() {
	IsEmpty := true
	for _, v := range d.PartPool {
		if v > 0 {
			IsEmpty = false
			break
		}
	}
	if IsEmpty {
		PartList := decorateCfg.GetAllPartId()
		for _, v := range PartList {
			d.PartPool[v] = 7
		}
	}
}

// 初始化零件消耗，预加载当前区域和下一区域的零件消耗，减少玩家装饰时的卡顿感
func (d *Decorate) initPartCost(AreaId int) {
	AllPartList := decorateCfg.GetPartNumByAreaId(AreaId)
	for k, v := range AllPartList {
		// 初始零件消耗写死
		if k == 33 {
			d.PartCost[k] = &PartCostInfo{
				Items: []*item.Item{item.NewItem(1515, 1)},
			}
			continue
		}
		if k == 34 {
			d.PartCost[k] = &PartCostInfo{
				Items: []*item.Item{item.NewItem(1505, 1)},
			}
			continue
		}
		if k == 35 {
			d.PartCost[k] = &PartCostInfo{
				Items: []*item.Item{item.NewItem(1505, 1), item.NewItem(1515, 1)},
			}
			continue
		}
		PartList := d.GetPart(v)
		Items := make([]*item.Item, 0)
		for k1, v1 := range PartList {
			Items = append(Items, item.NewItem(k1, v1))
		}
		d.PartCost[k] = &PartCostInfo{
			Items: Items,
		}
	}
}

func (d *Decorate) GetPartCost() map[int]int {
	res := make(map[int]int)
	for _, v := range d.PartCost {
		for _, v1 := range v.Items {
			res[v1.Id] += v1.Num
		}
	}
	return res
}
