package com.huigou.topsun.util;

import java.util.concurrent.ThreadLocalRandom;

/**
 * |00000000|00000000|00000000|00000000|00000000|00000000|00000000|00000000|
 * |--|xxxxxxxxxxxxxx 41bit 时间戳(毫秒) xxxxxxxxxxxxxxx|--|--------|--------|
 * |---------------------------------------4bit机器id|xxxx|--------|--------|
 * |------------------------------------------16bit自增序列|xxxxxxxx|xxxxxxxx|
 */
public class Snowflake {

    // 开始时间
    private static final long OFFSET = 1686211627000L;
    private static long WORKER_ID = 1L;

    /** 机器ID位  **/
    private static final long WORKER_ID_BITS = 4L;
    /** 自增序列位  **/
    private static final long SEQUENCE_ID_BITS = 16L;


    private static final long WORKER_SHIFT_BITS = SEQUENCE_ID_BITS;
    private static final long OFFSET_SHIFT_BITS = WORKER_ID_BITS + WORKER_SHIFT_BITS;

    private static long SEQUENCE_MAX = (1 << SEQUENCE_ID_BITS) - 1;

    private static long lastTimestamp = 0L;
    private static long sequence = 0L;

//    public Snowflake(long workerId) {
//        WORKER_ID = workerId;
//    }

    public static synchronized long nextId() {
        long timestamp = timeGen();
        if(timestamp < lastTimestamp) {
            System.out.println("时钟回拨");
            // 可以加入 ‘时钟序列’ 位，并缓存。
        }
        if(timestamp == lastTimestamp) {
            sequence = ++sequence & SEQUENCE_MAX;
            // 到达最大序列时等待
            if(sequence == 0L) {
                timestamp = tilNexMillis(lastTimestamp);
            }
            //timestamp = sequence != 0 ?  timestamp : tilNexMillis(lastTimestamp);
        } else {
            // 初始序列 随机 1 - 3
            sequence = ThreadLocalRandom.current().nextLong(1, 3);
        }
        lastTimestamp = timestamp;
        // 时间戳 | 机器id | 序列号
        return (timestamp - OFFSET) << OFFSET_SHIFT_BITS
                | (WORKER_ID << WORKER_SHIFT_BITS)
                | sequence;
    }

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

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

}
