package thyyxxk.webserver.utils;

import java.util.UUID;

/**
 * @author dj
 */
public class SnowFlakeId {
    private static SnowFlakeId INSTANCE;
    //下面两个每个5位，加起来就是10位的工作机器id
    private final long workerId;
    private final long datacenterId;
    //长度为5位
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    //12位的序列号
    private long sequence;
    //上次时间戳，初始值为负数
    private long lastTimestamp = -1L;

    private SnowFlakeId(long workerId, long datacenterId, long sequence) {
        //最大值
        long maxWorkerId = ~(-1L << workerIdBits);
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        long maxDatacenterId = ~(-1L << datacenterIdBits);
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    public static SnowFlakeId instance() {
        synchronized (SnowFlakeId.class) {
            if (null == INSTANCE) {
                INSTANCE = new SnowFlakeId(1, 1, 2011140217L);
            }
        }
        return INSTANCE;
    }

    /**
     * 下一个ID生成算法
     *
     * @return 返回 18 位字符串
     */
    public synchronized String nextId() {
        long timestamp = System.currentTimeMillis();

        //获取当前时间戳如果小于上次时间戳，则表示时间戳获取出现异常
        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));
        }

        //获取当前时间戳如果等于上次时间戳（同一毫秒内），则在序列号加1；否则序列号赋值为0，从0开始。
        //序列号id长度
        long sequenceBits = 11L;
        if (lastTimestamp == timestamp) {
            //序列号最大值
            long sequenceMask = ~(-1L << sequenceBits);
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        //将上次时间戳值刷新
        lastTimestamp = timestamp;

        //初始时间戳
        long twepoch = 1288834974657L;
        //工作id需要左移的位数，12位
        //数据id需要左移位数 12+5=17位
        long datacenterIdShift = sequenceBits + workerIdBits;
        //时间戳需要左移位数 12+5+5=22位
        long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        long id = ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << sequenceBits) |
                sequence;
        return String.valueOf(id);
    }

    //获取时间戳，并与上次时间戳比较
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }

    public String nextUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }
}
