package net.northking.cctp.scriptcase.tools;

import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.LockSupport;

/**
 * 雪花算法生成唯一id工具类
 *
 * @author lwz
 * @since 2023.09.04
 */
public class SnowflakeIdGenerator {
    /**
     * 定义起始时间戳 UTC+8:00 2023-09-04 00:00:00
     */
    private static final long START_TIMESTAMP = 1693756800L;
    /**
     * 定义每个部分的bit数
     */
    /**
     * 序列号占用的bit数  用于表示在同一毫秒内生成的序列号
     */
    private static final long SEQUENCE_BIT = 12;
    /**
     * 工作机器id占用的bit数 表示当前工作机器的唯一ID
     */
    private static final long WORKER_BIT = 10;
    /**
     * 数据中心ID占用的BIT数 表示数据中心的唯一id
     */
    private static final long DATACENTER_BIT = 10;
    // 定义了，每个部分的最大值
    /**
     * 定义了数据中心所能表示的最大值，它是一个位运算的结果
     */
    private static final long MAX_DATACENTER_NUM = ~(-1L << DATACENTER_BIT);
    /**
     * 定义了工作机器id所能表示的最大值
     */
    private static final long MAX_WORKER_NUM = ~(-1L << WORKER_BIT);
    /**
     * 定义了序列号所能表示的最大值
     */
    private static final long MAX_SEQUENCE_NUM = ~(-1L << SEQUENCE_BIT);
    //定义了每个部分向左的位移数
    private static final long WORKER_LEFT = SEQUENCE_BIT;
    private static final long DATACENTER_LEFT = SEQUENCE_BIT + WORKER_BIT;

    private static final long TIMESTAMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;

    //数据中心唯一ID
    private final long DATA_CENTER_ID;
    //当前工作机器唯一ID
    private final long WORKER_ID;
    private final AtomicLong SEQUENCE = new AtomicLong(0L);

    private volatile long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param datacenterId 数据中心id
     * @param workerId     工作机器id
     */
    public SnowflakeIdGenerator(long datacenterId, long workerId) {
        if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
            throw new IllegalArgumentException("数据中心ID不能大于" + MAX_DATACENTER_NUM + "或小于0");
        }
        if (workerId > MAX_WORKER_NUM || workerId < 0) {
            throw new IllegalArgumentException("工作机器ID不能大于" + MAX_WORKER_NUM + "或小于0");
        }
        this.DATA_CENTER_ID = datacenterId;
        this.WORKER_ID = workerId;
        SEQUENCE.set(0L);
    }

    /**
     * 生成ID
     */
    public synchronized long nextId() {
        long currentTimestamp = getTimestamp();
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟向后移动。拒绝为" + (lastTimestamp - currentTimestamp) + "ms生成ID。");
        }
        if (currentTimestamp == lastTimestamp) {
            long seq = SEQUENCE.getAndIncrement() & MAX_SEQUENCE_NUM;
            if (seq == 0) {
                // 当前毫秒的序列号用完，等待下一毫秒
                currentTimestamp = getNextTimestamp(lastTimestamp);
                SEQUENCE.set(0L);
            }
        } else {
            SEQUENCE.set(0L);
        }
        lastTimestamp = currentTimestamp;

        //将各个部分拼接为最终ID
        return ((currentTimestamp - START_TIMESTAMP) << TIMESTAMP_LEFT) |
                (DATA_CENTER_ID << DATACENTER_LEFT) |
                (WORKER_ID << WORKER_LEFT) |
                SEQUENCE.get();

    }

    /**
     * 获取当前时间戳
     *
     * @return long
     */
    private long getTimestamp() {
        long nanoTime = System.nanoTime();
        long timestamp = (nanoTime / 1000000L) + START_TIMESTAMP;

        return Math.max(timestamp, lastTimestamp);
    }

    /**
     * 等待下一毫秒
     * @param lastTimestamp
     * @return long
     */
    private long getNextTimestamp(long lastTimestamp) {
        long currentTimestamp = getTimestamp();
        while (currentTimestamp<=lastTimestamp){
            //等待一纳秒 避免循环空转，导致CPU资源浪费
            LockSupport.parkNanos(1L);
            currentTimestamp=getTimestamp();
        }
        return currentTimestamp;
    }


}
