package cn.freemud.management.util;

import com.thoughtworks.xstream.core.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Slf4j
public  class GlobalSnowflakeIdWorker {


    /**
     * 起始时间 2015/1/1 0:0:0
     */
    private static long TIMESTAMP_START = 1420041600000L;

    /**
     * 时间戳向左位移位数
     */
    private static int timestampShift;

    /**
     * 机器号
     */
    private static long machineNo;

    /**
     * 机器Id向左位移位数
     */
    private static int machineNoShift;

    /**
     * 每毫秒能产生的最大序列号
     */
    private static int maxSequence;

    /**
     * 当前自增序列
     */
    private static AtomicInteger sequence;

    /**
     * 初始化方法
     *
     * @param machineId 机器ID
     */
    public static synchronized void init(int machineId) {
        timestampShift = 20;
        machineNoShift = 8;
        machineNo = (long) (machineId << machineNoShift);
        maxSequence = 128;   //7位序列号
        sequence = new AtomicInteger(0);
    }

    public static long getDatetimeId(Date time) {
        long timestamp = time.getTime() - TIMESTAMP_START;
        timestamp <<= timestampShift;
        return timestamp + machineNo;
    }

    /**
     * 获取Id产生的时间
     *
     * @param id
     * @return ID产生时间
     */
    public static Date getDatetime(Long id) {
        id = id >> timestampShift;
        id = id + TIMESTAMP_START;
        return new Date(id);
    }

    /**
     * 专门提代给shardingjdbc使用
     * @param id
     * @return
     */
    public static Date getDatetimeByShardingJdbc(Long id) {
        id = id >> 20;
        id = id + TIMESTAMP_START;
        return new Date(id);
    }


    /**
     * 获取时间戳
     * @param id
     * @return
     */
    public static Long getTimeStamp(Long id) {
        id = id >> timestampShift;
        id = id + TIMESTAMP_START;
        return id;
    }

    /**
     * 生成ID
     * 此处实现存在部分问题，但影响甚微。运行的逻辑是sequence用完之后会睡眠1ms，然后开始
     *
     * @return id
     */
    public static long generateId() throws Exception {
        while (true) {
            int sec = sequence.incrementAndGet();
            try {
                if (sec == maxSequence) {
                    Thread.sleep(1L);
                    sequence.set(0);
                } else {
                    if (sec <= maxSequence) {
                        long timestamp = System.currentTimeMillis() - TIMESTAMP_START;
                        timestamp <<= timestampShift;
                        return timestamp + machineNo + (long) sec;
                    }

                    Thread.sleep(1L);
                }
            }catch (InterruptedException e){
                log.error("!!!!Snowflake InterruptedException", e);
                Thread.currentThread().interrupt();
                throw new Exception(e.getMessage());
            }
        }
    }

}
