package rank_util

import (
	"sync"
)

// RankUtil
// @description:排行工具类
type RankUtil struct {
	// maxCount 排行榜最大数量
	maxCount int

	// compar 对比函数 返回含义 -1:a<b 0:a=b 1:a>b
	compar func(a, b interface{}) int

	// dataList 排行数据
	dataList []*Model

	// dataDict 数据字典
	dataDict map[string]*Model

	// dataLock 数据锁
	dataLock sync.RWMutex
}

// NewRankUtil
// @description: 构造排行对象
// parameter:
//		@mc:最大容量
//		@comp:元素比对方法
// return:
func NewRankUtil(mc int, comp func(a, b interface{}) int) *RankUtil {
	result := &RankUtil{
		maxCount: mc,
		compar:   comp,
		dataList: make([]*Model, 0, mc),
		dataDict: make(map[string]*Model, mc),
	}

	return result
}

// GetMaxCount
// @description: 获取排行榜最大数量
// parameter:
//		@receiver r:
// return:
//		@int:
func (r *RankUtil) GetMaxCount() int {
	return r.maxCount
}

// GetCount
// @description: 获取排行榜当前数量
// parameter:
//		@receiver r:
// return:
//		@int:
func (r *RankUtil) GetCount() int {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	return len(r.dataList)
}

// IsFull
// @description: 判断排行榜是否已经满了
// parameter:
//		@receiver r:
// return:
//		@bool:
func (r *RankUtil) IsFull() bool {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	return len(r.dataList) == r.maxCount
}

// GetAll
// @description: 获取排行榜内容
// parameter:
//		@receiver r:
// return:
//		@[]*Model:
func (r *RankUtil) GetAll() []*Model {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	result := make([]*Model, 0, len(r.dataList))
	for _, item := range r.dataList {
		if item == nil || item.obj == nil {
			break
		}

		result = append(result, item)
	}

	return result
}

// GetRankListBySkip
// @description: 获取排行榜内容
// parameter:
//		@receiver r:
//		@skip:跳过的数量
//		@count:跳过后需要返回的数量
// return:
//		@[]*Model:
func (r *RankUtil) GetRankListBySkip(skip, count int) []*Model {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	result := make([]*Model, 0, count)
	cn := len(r.dataList)
	if skip >= cn {
		return result
	}

	for i := 0; i < count; i++ {
		item := r.dataList[skip+i]
		if item == nil || item.obj == nil {
			break
		}

		result = append(result, item)
	}

	return result
}

// GetItemForK
// @description: 根据key获取对象
// parameter:
//		@receiver r:
//		@k:key
// return:
//		@*Model:
func (r *RankUtil) GetItemForK(k string) *Model {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	item, _ := r.dataDict[k]

	return item
}

// GetItemForRank
// @description: 根据排名获取对应对象
// parameter:
//		@receiver r:
//		@rank:排名
// return:
//		@*Model:
func (r *RankUtil) GetItemForRank(rank int) *Model {
	r.dataLock.RLock()
	defer r.dataLock.RUnlock()

	if len(r.dataList) < rank {
		return nil
	}

	return r.dataList[rank-1]
}

// Refresh
// @description: 刷新排行榜
// parameter:
//		@receiver r:
//		@k:key
//		@o:排行持有的对象，业务层不应该持有该对象。否则业务层更改内容，会导致排行榜内容被改变
//		@isup:变动是否升高
// return:
//		@changeRank:排行是否变动
//		@dm:如果有对象，因为排行变动导致掉出排行榜，则返回该对象，否则返回nil
func (r *RankUtil) Refresh(k string, o interface{}, isup bool) (changeRank bool, dm *Model) {
	r.dataLock.Lock()
	defer r.dataLock.Unlock()

	changeRank = false
	curItem, exists := r.dataDict[k]
	if isup {
		if !exists {
			newRank := len(r.dataList) + 1
			curItem = &Model{rank: newRank, key: k, obj: o}
			//  排行榜未满，必定入榜，则先加入排行榜
			if newRank <= r.maxCount {
				r.dataList = append(r.dataList, curItem)
				r.dataDict[curItem.key] = curItem
				changeRank = true
				_, dm = r.upRank(curItem)
			} else {
				changeRank, dm = r.upRank(curItem)
			}
		} else {
			curItem.obj = o
			changeRank, dm = r.upRank(curItem)
		}
	} else {
		//  如果在排行榜内，从之前往下比较
		if exists {
			curItem.obj = o
			changeRank = r.downRank(curItem)
		} else {
			//  如果不在排行榜内，直接结束
		}
	}

	return
}

// Delete
// @description: 删除k指定的对象,很耗性能，不建议高频使用
// parameter:
//		@receiver r:
//		@k:key
// return:
//		@bool:true:代表key存在排行榜中，并且删除成功  false:其他情况
func (r *RankUtil) Delete(k string) bool {
	r.dataLock.Lock()
	defer r.dataLock.Unlock()

	m, exists := r.dataDict[k]
	if !exists {
		return false
	}

	//从当前位置向后移动，到最后的时候，删除
	for {
		if m.rank >= len(r.dataList) {
			break
		}

		am := r.dataList[m.rank]
		r.exchange(m, am)
		m = am
	}

	//最后一个应该是要删除的对象
	r.dataList = append(r.dataList[:m.rank-1:r.maxCount], r.dataList[m.rank:]...)
	delete(r.dataDict, k)

	return true
}

// upRank
// @description: 排行升高
// parameter:
//		@receiver r:
//		@curItem:当前对象
// return:
//		@changeRank:排行是否变动
//		@dm:因排行变动被移除排行的对象
func (r *RankUtil) upRank(curItem *Model) (changeRank bool, dm *Model) {
	changeRank = false
	for {
		// rank=1,排在第一位，下标为0，故这里需要-2为上一位的下标
		beforeIndex := curItem.rank - 2
		if beforeIndex < 0 {
			break
		}

		beforeItem := r.dataList[beforeIndex]
		if r.compar(curItem.obj, beforeItem.obj) <= 0 {
			break
		}

		//  交换操作
		tdm := r.exchange(curItem, beforeItem)
		if tdm != nil {
			dm = tdm
		}

		curItem = beforeItem
		changeRank = true
	}

	return
}

// downRank
// @description: 排行降低
// parameter:
//		@receiver r:
//		@curItem:当前对象
// return:
//		@bool:
func (r *RankUtil) downRank(curItem *Model) bool {
	changeRank := false

	for {
		// rank=1,排在第一位，下标为0，需要对比rank=2的，下标为1，故这里直接取rank即可
		afterIndex := curItem.rank
		if afterIndex >= len(r.dataList) {
			break
		}

		afterItem := r.dataList[afterIndex]
		if r.compar(afterItem.obj, curItem.obj) <= 0 {
			break
		}

		//  交换操作
		r.exchange(curItem, afterItem)

		curItem = afterItem
		changeRank = true
	}

	return changeRank
}

// exchange
// @description: 排行对象交换
// parameter:
//		@receiver r:
//		@a:对象a
//		@b:对象b
// return:
//		@delRank:因交换被移除排行榜的对象
func (r *RankUtil) exchange(a, b *Model) (dm *Model) {
	tempObj := a.obj
	tempKey := a.key

	a.obj = b.obj
	a.key = b.key
	b.obj = tempObj
	b.key = tempKey

	if a.rank > r.maxCount {
		delete(r.dataDict, a.key)
		dm = a
	} else {
		r.dataDict[a.key] = a
	}
	if b.rank > r.maxCount {
		delete(r.dataDict, b.key)
		dm = b
	} else {
		r.dataDict[b.key] = b
	}

	return
}
