package com.cf.imes.module.executor.util.fileConversion.admin;


import com.cf.imes.module.executor.util.MinuteCounter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDate;

/**
 * @ClassName: SnowflakeIdWorker3rd
 * @Description:snowflake算法改进
 * @author: yonnie
 * @date: 2024/06/18 14:10:47
 * @version V1.0
 *
 *          将产生的Id类型更改为Integer 32bit <br>
 *          把时间戳的单位改为分钟，使用25bit的时间戳
 *          7bit作为自增值即 2^7 = 128
 */
@Slf4j
@Component
public class SnowflakeIdWorker3rd {
    /** 初始时间 (2024-01-01: 1704038400) */
    // private final int twepoch = 28400640;// 1704038400000L/1000/60;
    private final int twepoch = 1704038400;
    /** 序列在id中占位数 */
    private final long sequenceBits = 7L;
    /** 时间截向左移7bit */
    private final long timestampLeftShift = sequenceBits;
    /** 生成序列的MASK (0x7F) */
    private final int sequenceMask = -1 ^ (-1 << sequenceBits);
    /** 分钟内序 (0~127) */
    private int sequence = 0;
    private int laterSequence = 0;
    /** 上次生成ID的时间戳 */
    private int lastTimestamp = -1;
    private final MinuteCounter counter = new MinuteCounter();
    /** 预支时间标志 */
    boolean isAdvance = false;

    // ==============================Constructors=====================================

    public SnowflakeIdWorker3rd() {
    }

    // ==============================Test=============================================

    /** 测试 */
    public static void main(String[] args) {
        SnowflakeIdWorker3rd idWorker = new SnowflakeIdWorker3rd();
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(i + ": " + id + "  " + "2406" + Long.toString(id).substring(2));
        }
        // long id = idWorker.nextId();
        // System.out.println(id);
    }

    // ==============================Methods==========================================
    /**
     * 获取当前年月
     * @return null
     */
    public static int obtainingTime() {
        LocalDate now = LocalDate.now();
        int year = now.getYear();
        String year_last_two_digits = String.valueOf(year).substring(2);
        int month = now.getMonthValue();
        String formatted_date = year_last_two_digits + String.format("%02d", month);
        return Integer.parseInt(formatted_date);
    }

    /**
     * 获得下一个ID (该方法是线程安全)
     * 
     * @return SnowflakeId
     */
    public synchronized int nextId() {
        int timestamp = timeGen();
        // 如果当前时间小于上一次ID生成的时间戳，说明系统时钟修改过
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format(
                    "Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (timestamp > counter.get()) {
            counter.set(timestamp);
            isAdvance = false;
        }

        // 如果是同时间生成的，则进行分钟内序列
        if (lastTimestamp == timestamp || isAdvance) {
            if (!isAdvance) {
                sequence = (sequence + 1) & sequenceMask;
            }

            // 分钟内自增列溢出
            if (sequence == 0) {
                // 预支下一分获得新的时间戳
                isAdvance = true;
                int laterTimestamp = counter.get();
                if (laterSequence == 0) {
                    laterTimestamp = counter.incrementAndGet();
                }
                int nextId = ((laterTimestamp - twepoch) << timestampLeftShift) | laterSequence;
                laterSequence = (laterSequence + 1) & sequenceMask;
                return nextId;
            }
        } else { // 时间戳改变，分钟内序列置0
            sequence = 0;
            laterSequence = 0;
        }
        // 上次生成ID的时间截
        lastTimestamp = timestamp;
        // 移位并或运算拼成32位的ID
        return ((timestamp - twepoch) << timestampLeftShift) | sequence;
    }

    /**
     * 返回以分钟为单位的当前时
     * 
     * @return 当前时间(分钟)
     */
    protected int timeGen() {
        // String timestamp = String.valueOf(System.currentTimeMillis() / 1000 / 60);
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        return Integer.valueOf(timestamp);
    }

}
