package com.zhgd.xmgl.config;


/**
 * 0000000000 0 - 0000000000 0000000000 0000000000 0 - 00 0000000000 - 0000000000
 * 11位标识 -              31位时间戳 -                 12位服务序列号 -  10位顺序位
 * 注：
 * 1.生成ID缩短至16位
 * 2.31位精确到秒时间戳可使用68年，生成16位ID时保证使用60.5年
 * 3.12位服务序列号，可以支持部署4096个节点
 * 4.10位顺序位，每秒生成1024个ID
 * <p>
 * 16位极值               对应时间戳（1365367696）（2013-04-08 04:48:16）   对应数据中心ID    对应顺序位
 * 最大值：9007199254740991         2147483647（2081-04-26 08:02:23）             4095          1023
 * 16位值：1000000000000000          238418579（2020-10-27 16:11:15）              416             0
 * 最小值：         4195329                  1（2013-04-08 04:48:17）               1             1
 * <p>
 * 1111111111111111111111111111111 111111111111 1111111111
 * 0001110001101011111101010010011 000110100000 0000000000
 * 0000000000000000000000000000001 000000000001 0000000001
 *
 * @program: wisdomSite
 * @description:
 * @author: Mr.Peng
 * @create: 2021-12-16 18:19
 **/

public class IDGenerator {

    /**
     * 服务序列号
     */
    private long serviceSequence = 1L;

    /**
     * 时间戳开始计时时间 2013-04-08 04:48:16
     */
    private final long startTimestamp = 1365367696L;

    /**
     * 默认顺序位的初始值
     */
    private long sequence = 1L;
    /**
     * 顺序位2进制长度
     */
    private final long sequenceBits = 10L;

    /**
     * 服务序列号2进制长度
     */
    private final long serviceSequenceBits = 12L;


    /**
     * 服务序列号右移位数
     */
    private final long serviceSequenceShift = sequenceBits;

    /**
     * 时间戳右移位数
     */
    private final long timestampShift = sequenceBits + serviceSequenceBits;

    /**
     * 生成序列的掩码(10位所对应的最大整数值)，这里为1023 (0b1111111111=1023)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public IDGenerator(Long serviceSequence) {
        this.serviceSequence = serviceSequence;
    }

    /**
     * 生成唯一的趋势递增的id
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1L) & sequenceMask;
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - startTimestamp) << timestampShift) | (serviceSequence << serviceSequenceShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis() / 1000L;
    }
}
