package com.mortals.xhx.common.utils;

/**
 * @author wxy
 */
public class SnowFlakeUtil {

    private long workerId;
    private long dataCenterId;
    private long sequence = 0L;
    private long workerIdBits = 5L;
    private long dataCenterIdBits = 5L;
    private long sequenceBits = 12L;
    private long workerIdShift = sequenceBits;
    private long dataCenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;
    private long sequenceMask = ~(-1L << sequenceBits);
    private long lastTimestamp = -1L;
    private static SnowFlakeUtil snowFlakeInstance=null;

    public SnowFlakeUtil() {
        this(0L, 0L);
    }

    public SnowFlakeUtil(long workerId, long dataCenterId) {

        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;
    }
//    private static class IdGenHolder {
//        private static final SnowFlakeUtil INSTANCE = new SnowFlakeUtil();
//    }
    public static SnowFlakeUtil get() {
//        //避免单例模式在懒汉模式使用，造成ID重复
        if(snowFlakeInstance == null){
            synchronized (SnowFlakeUtil.class){
                if(snowFlakeInstance == null){
                    snowFlakeInstance = new SnowFlakeUtil();
                }
            }
        }
        return snowFlakeInstance;
    }
    /**
     * 获取全局唯一编码
     */
    public static String getId() {
        long id = SnowFlakeUtil.get().nextId();
        return Long.toString(id);
    }

    //线程安全的id生成方法
    @SuppressWarnings("all")
    public synchronized long nextId() {
        //获取当前毫秒数
        long timestamp = timeGen();
        //如果服务器时间有问题(时钟后退) 报错。
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }
        //如果上次生成时间和当前时间相同,在同一毫秒内
        if (lastTimestamp == timestamp) {
            //sequence自增，因为sequence只有12bit，所以和sequenceMask相与一下，去掉高位
            sequence = (sequence + 1) & sequenceMask;
            //判断是否溢出,也就是每毫秒内超过4095，当为4096时，与sequenceMask相与，sequence就等于0
            if (sequence == 0) {
                //自旋等待到下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        long twepoch = 1111166465631L;
        return ((timestamp - twepoch) << timestampLeftShift)
                | (dataCenterId << dataCenterIdShift)
                | (workerId << workerIdShift)
                | sequence;
    }

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

    protected long timeGen() {
        return System.currentTimeMillis();
    }


}
