package coroutine_timer

import (
	"fmt"
	"math"
	"time"

	"goutil/logUtil"
	"goutil/stringUtil"
)

const (
	//  启动暂停时间
	con_STAR_SLEEP_NUM = 3

	//  秒级定时器卡槽数量
	con_SECOND_SLOT_NUM = 60

	//分钟级定时器卡槽数量
	con_MINUTES_SLOT_NUM = 60
)

var (
	//  秒级定时器下标
	secIndex = 0

	//  秒级定时器当前开始时间
	secondStarTime int64

	//  秒级定时器槽
	secondsTimers [con_SECOND_SLOT_NUM]*timersModel

	//  分钟级定时器下标
	minIndex = 0

	//  分钟级定时器当前开始时间
	minStarTime int64

	//  分钟级定时器槽
	minutesTimers [con_MINUTES_SLOT_NUM]*timersModel

	//  其他定时器存放槽
	otherTimers *timersModel

	//  操作通道
	cmdChan chan *cmdModel
)

func init() {
	for i := 0; i < con_SECOND_SLOT_NUM; i++ {
		secondsTimers[i] = newTimersModel()
	}
	for i := 0; i < con_MINUTES_SLOT_NUM; i++ {
		minutesTimers[i] = newTimersModel()
	}

	otherTimers = newTimersModel()
	cmdChan = make(chan *cmdModel, 1000)
	secondStarTime = time.Now().Unix()
	minStarTime = secondStarTime + con_SECOND_SLOT_NUM

	go chanHandler()
}

// AddTimer
// @description: 添加定时回调
// parameter:
//
//	@afterSecond:延后多少时间执行
//	@exfun:执行方法
//	@obj:执行传入的参数
//
// return:
//
//	@string:
func AddTimer(afterSecond int, exfun func(interface{}), obj interface{}) string {
	tick := time.Now().Unix() + int64(afterSecond)
	return AddTimer3(tick, exfun, obj)
}

// AddTimer2
// @description: 添加定时回调
// parameter:
//
//	@t:执行时间点
//	@exfun:执行方法
//	@obj:执行传入的参数
//
// return:
//
//	@string:
func AddTimer2(t time.Time, exfun func(interface{}), obj interface{}) string {
	tick := t.Unix()
	return AddTimer3(tick, exfun, obj)
}

// AddTimer3
// @description: 添加定时回调
// parameter:
//
//	@tick:执行时间点
//	@exfun:执行方法
//	@obj:执行传入的参数
//
// return:
//
//	@newId:
func AddTimer3(tick int64, exfun func(interface{}), obj interface{}) (newId string) {
	newId = stringUtil.GetNewUUID()
	newObj := newTimerObj(newId, tick, exfun, obj)

	cnm := newCmdModel(cmd_add, newObj)
	cmdChan <- cnm

	return
}

// AddTimer4
// @description: 添加定时回调(此方法会在内部校验id，所以性能会比其他AddTimer方法低)
// parameter:
//
//	@id:定时id(外部需要自行保证id唯一)
//	@tick:执行时间点
//	@exfun:执行方法
//	@obj:执行传入的参数
//
// return:
//
//	@err:
func AddTimer4(id string, tick int64, exfun func(interface{}), obj interface{}) (err error) {
	newObj := newTimerObj(id, tick, exfun, obj)
	newObj.needCheckId = true

	//  加入处理队列
	cnm := newCmdModel(cmd_add, newObj)
	cmdChan <- cnm

	//  等待处理结束
	<-cnm.waitChan

	//  返回处理结果
	err = cnm.err

	return
}

// DeleteTimer
// @description: 删除定时器
// parameter:
//
//	@id:
//
// return:
func DeleteTimer(id string) {
	cnm := newCmdModel(cmd_del, id)
	cmdChan <- cnm
}

// chanHandler
// @description: channel处理
// parameter:
// return:
func chanHandler() {
	defer func() {
		if err := recover(); err != nil {
			logUtil.ErrorLog("coroutine-timer.excute err:%s", err)
		}
	}()

	//  暂停一下再处理，避免启动立即处理，其他数据还没准备好
	time.Sleep(con_STAR_SLEEP_NUM * time.Second)

	at := time.After(time.Second * 1)
	for {
		select {
		case cm := <-cmdChan:
			switch cm.cmd {
			case cmd_add:
				cmdAdd(cm)
			case cmd_del:
				cmdDel(cm)
			}
		case <-at:
			//  byron:需要处理时间后调导致跳时间的问题：调整后应该马上执行的

			//  计算需要执行的次数
			n := time.Now().Unix() - secondStarTime - int64(secIndex)
			if n > 0 {

				//  执行对应次数的方法 --- 正常应该只执行1此，调时间后，此处会追时间
				var i int64
				for i = 0; i < n; i++ {
					cmdRun()
				}
			}

			at = time.After(time.Second * 1)
		}
	}
}

// cmdAdd
// @description: 添加定时器
// parameter:
//
//	@cm:
//
// return:
func cmdAdd(cm *cmdModel) {
	newObj := cm.paramObj.(*timerObj)
	if newObj.needCheckId && checkTimerExist(newObj.id) {
		cm.err = fmt.Errorf("已经存在id=%s的timer", newObj.id)
		cm.waitChan <- struct{}{}
		return
	}

	//  如果执行时间比当前时间小，则放入最近的调度卡槽，以便尽快执行
	tick := newObj.tick
	if tick <= (secondStarTime + int64(secIndex)) {
		tick = (secondStarTime + int64(secIndex)) + 1
	}

	//  落在秒钟级别定时器上
	if tick < (secondStarTime + con_SECOND_SLOT_NUM) {
		index := (int)(tick - secondStarTime)
		secondsTimers[index].addTimer(newObj)
		cm.waitChan <- struct{}{}
		return
	}

	//  落在分钟级别定时器上
	if tick < (minStarTime + con_MINUTES_SLOT_NUM*con_SECOND_SLOT_NUM) {
		index := (int)(tick-minStarTime) / con_SECOND_SLOT_NUM
		minutesTimers[index].addTimer(newObj)
		cm.waitChan <- struct{}{}
		return
	}

	//落在小时级别定时器上
	otherTimers.addTimer(newObj)

	//  返回操作完成
	cm.waitChan <- struct{}{}
}

// cmdDel
// @description: 删除timer
// parameter:
//
//	@cm:
//
// return:
func cmdDel(cm *cmdModel) {
	id := cm.paramObj.(string)

	//  移除秒级别定时器
	for _, item := range secondsTimers {
		item.delTimer(id)
	}

	//  移除分种级定时器
	for _, item := range minutesTimers {
		item.delTimer(id)
	}

	//  移除时钟级定时器
	otherTimers.delTimer(id)

	//  返回操作完成
	cm.waitChan <- struct{}{}
}

// cmdRun
// @description: 运行定时器
// parameter:
// return:
func cmdRun() {
	defer func() {
		if err := recover(); err != nil {
			logUtil.ErrorLog("coroutine-timer.inExcute err:%s", err)
		}
	}()

	//  执行秒级定时器
	timers := getSencondTimers()
	if len(timers) == 0 {
		return
	}

	for _, t := range timers {
		go safeRun(t)
	}
}

// checkTimerExist
// @description: 校验timer是否存在
// parameter:
//
//	@id:id
//
// return:
//
//	@bool:
func checkTimerExist(id string) bool {
	//  秒级别定时器检测
	for _, item := range secondsTimers {
		if item.exist(id) {
			return true
		}
	}

	//  分种级定时器检测
	for _, item := range minutesTimers {
		if item.exist(id) {
			return true
		}
	}

	//  时钟级定时器检测
	return otherTimers.exist(id)
}

// getSencondTimers
// @description: 获取秒级定时器
// parameter:
// return:
//
//	@result:
func getSencondTimers() (result []*timerObj) {
	//  获取对应slot里面的定时对象
	result = secondsTimers[secIndex].getAllTimers2()
	secondsTimers[secIndex] = newTimersModel()
	secIndex++

	//  如果达到最大，则重新填装新的调度对象
	if secIndex == con_SECOND_SLOT_NUM {
		secIndex = 0
		secondStarTime = secondStarTime + con_SECOND_SLOT_NUM
		minTaskList := getMinutesTasks()
		for _, t := range minTaskList {
			index := t.tick - secondStarTime
			secondsTimers[index].addTimer(t)
		}
	}

	return
}

// getMinutesTasks
// @description: 获取分钟级定时器
// parameter:
// return:
//
//	@result:
func getMinutesTasks() (result []*timerObj) {
	//  获取对应slot里面的定时对象
	result = minutesTimers[minIndex].getAllTimers2()
	minutesTimers[minIndex] = newTimersModel()
	minIndex++

	//  如果达到最大，则重新填装新的调度对象
	if minIndex == con_MINUTES_SLOT_NUM {
		reInputMin()
	}

	return
}

// reInputMin
// @description: 重新填入分钟级定时器
// parameter:
// return:
func reInputMin() {
	minIndex = 0
	minStarTime = minStarTime + con_MINUTES_SLOT_NUM*con_SECOND_SLOT_NUM

	delMap := make(map[string]struct{})
	for _, t := range otherTimers.getAllTimers() {
		index := (t.tick - minStarTime) / con_SECOND_SLOT_NUM
		if index > math.MaxInt || index >= con_MINUTES_SLOT_NUM {
			continue
		}
		minutesTimers[index].addTimer(t)
		delMap[t.id] = struct{}{}
	}

	if len(delMap) > 0 {
		for k := range delMap {
			otherTimers.delTimer(k)
		}
	}
}

// safeRun
// @description: 安全运行定时器回调
// parameter:
//
//	@t:
//
// return:
func safeRun(t *timerObj) {
	defer func() {
		if err := recover(); err != nil {
			logUtil.ErrorLog("coroutine-timer.safeRun id:%s err:%s", t.id, err)
		}
	}()

	t.excuteAction(t.paramObj)
}
