/*
用于生成唯一的、递增的Id。生成的规则如下：
1、生成的Id包含一个固定前缀值
2、为了生成尽可能多的不重复数字，所以使用int64来表示一个数字，其中：
0 000000000000000 0000000000000000000000000000 00000000000000000000
第一部分：1位，固定为0
第二部分：共TimeBit位，表示当前时间距离基础时间的秒数。范围为[0, math.Pow(2, TimeBit))，以2020-1-1 00:00:00为基准
第三部分：共IdentifierBit位，表示固定唯一标识(机器号或者服务器Id等)。范围为[0, math.Pow(2, IdentifierBit))
第四部分：共SeedBit位，表示自增种子。范围为[0, math.Pow(2, SeedBit))
3、总体而言，此规则支持每秒生成math.Pow(2, SeedBit)个不同的数字，并且在math.Pow(2, TimeBit)/60/60/24/365年的时间范围内有效
*/

/*
修改记录：
2020-03-04 14:30:00 调整了时间和唯一标识在Id中的位置，以便生成递增的Id
2020-04-20 21:10:00 同步了C＃版本的逻辑
*/

package idUtil

import (
	"fmt"
	"math"
	"sync"
	"time"
)

type TimeIdentifierSeedGenerator struct {
	timeBit        int32 // 时间戳所占的位数
	timeBitOffset  int32 // 时间戳的偏移位数
	startTimeStamp int64 // 起始时间戳
	endTimeStamp   int64 // 结束时间戳

	identifier          int64 // 唯一标识
	identifierBit       int32 // 唯一标识(机器号或者服务器Id等)所占的位数
	identifierBitOffset int32 // 唯一标识的偏移位数

	seedBit       int32 // 自增种子所占的位数
	seedBitOffset int32 // 自增种子的偏移位数
	minSeed       int64 // 最小的种子值
	maxSeed       int64 // 最大的种子值
	currSeed      int64 // 当前种子值

	mutex sync.Mutex // 锁对象
}

func (this *TimeIdentifierSeedGenerator) getTimeStamp() (int64, error) {
	if time.Now().Unix() > this.endTimeStamp {
		return 0, fmt.Errorf("Time's value is out of scope")
	}

	return time.Now().Unix() - this.startTimeStamp, nil
}

func (this *TimeIdentifierSeedGenerator) getNewSeed() int64 {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	if this.currSeed >= this.maxSeed {
		this.currSeed = this.minSeed
	} else {
		this.currSeed = this.currSeed + 1
	}

	return this.currSeed
}

// 生成新的Id
// identifier：Id的唯一标识值。取值范围必须可以用创建对象时指定的唯一标识值的位数来表示，否则会返回参数超出范围的错误
// 返回值：
// 新的Id
// 错误对象
func (this *TimeIdentifierSeedGenerator) GenerateNewId() (int64, error) {
	timestamp, err := this.getTimeStamp()
	if err != nil {
		return 0, err
	}
	seed := this.getNewSeed()
	id := (timestamp << this.timeBitOffset) | (this.identifier << this.identifierBitOffset) | (seed << this.seedBitOffset)

	return id, nil
}

// 创建新的Id生成器对象（为了保证Id的唯一，需要保证生成的对象全局唯一）
// timeBit + identifierBit + seedBit <= 63
// timeBit:时间的位数
// identifier:id唯一标识
// identifierBit:id唯一标识(机器号或者服务器Id等)的位数
// seedBit:自增种子的位数
// 返回值：
// 新的Id生成器对象
// 错误对象
func NewTimeIdentifierSeedGenerator(timeBit int32, identifier int64, identifierBit, seedBit int32) (*TimeIdentifierSeedGenerator, error) {
	// 之所以使用63位而不是64，是为了保证值为正数
	if timeBit+identifierBit+seedBit > 63 {
		return nil, fmt.Errorf("总位数%d超过63位，请调整所有值的合理范围。", timeBit+identifierBit+seedBit)
	}

	if identifier < 0 || identifier > int64(math.Pow(2, float64(identifierBit)))-1 {
		return nil, fmt.Errorf("唯一标识值溢出，有效范围为【0,%d】", int64(math.Pow(2, float64(identifierBit)))-1)
	}

	startTimeStamp := time.Date(2019, time.January, 1, 0, 0, 0, 0, time.Local).Unix()
	obj := &TimeIdentifierSeedGenerator{
		timeBit:        timeBit,
		startTimeStamp: startTimeStamp,
		endTimeStamp:   startTimeStamp + int64(math.Pow(2, float64(timeBit))) - 1,
		identifier:     identifier,
		identifierBit:  identifierBit,
		seedBit:        seedBit,
		minSeed:        0,
		maxSeed:        int64(math.Pow(2, float64(seedBit))) - 1,
		currSeed:       0,
	}

	obj.seedBitOffset = 0
	obj.identifierBitOffset = obj.seedBitOffset + obj.seedBit
	obj.timeBitOffset = obj.identifierBitOffset + obj.identifierBit

	return obj, nil
}
