package com.xxfc.platform.universal.weixin.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 主键id生成器
 * 
 * @author linlihua
 *
 */
public class Snowflake {

	private static final Logger logger = LoggerFactory.getLogger(Snowflake.class);
	private long workerId;// 工作id 5位 0-31
	private long datacenterId;// 数据中心id 5位 0-31
	private long sequence;// 2的12次方减1

	/**
	 * 生成系统时间戳毫秒
	 * 
	 * @return
	 */
	private long genTime() {
		return System.currentTimeMillis();
	}

	// 再定义工作id位数
	private long workerIdBits = 5L;
	private long datacenterIdBits = 5L;
	private long sequenceBits = 12L;

	// 位运算求得工作id范围值
	private long maxWorkerId = -1 ^ (-1 << workerIdBits);
	private long maxDatacenterId = -1 ^ (-1 << datacenterIdBits);
	private long sequenceMask = -1 ^ (-1 << sequenceBits);

	private long twepoch = 1522145570000L;

	private long workerIdShift = sequenceBits;
	private long datacenterIdShift = sequenceBits + workerIdBits;
	private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

	private Snowflake(long workerId, long datacenterId, long sequance) {
		if (workerId < 0 || workerId > maxWorkerId) {
			logger.error("创建Snowflake实例失败workerId过大");
			return;
		}
		if (datacenterId < 0 || datacenterId > maxDatacenterId) {
			logger.error("创建Snowflake实例失败datacenterId过大");
			return;
		}
		this.workerId = workerId;
		this.datacenterId = datacenterId;
		this.sequence = sequance;

	}

	private long lastTimestamp = -1L;

	// 因为此类是单例的，所以这个方法就不需要synchronized
	private synchronized long nextId() {
		long timestamp = genTime();
		if (lastTimestamp == timestamp) {// 在同一毫秒内，执行了多次
			// 如果sequence超过了4095，那么和4095进行与运算则会变为0,当同一毫秒内，生成了4095个序列，那么必须在下一毫秒内，并且序列再次从0开始，防止溢出4095
			sequence = (sequence + 1) & sequenceMask;
			if (sequence == 0) {
				timestamp = tilTimestamp(lastTimestamp);
			}
		} else {
			sequence = 0;
		}
		lastTimestamp = timestamp;

		return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
				| (workerId << workerIdShift) | sequence;
	}

	private long tilTimestamp(long lastTimestamp) {
		long timestamp = genTime();
		while (lastTimestamp >= timestamp)
			timestamp = genTime();
		return timestamp;
	}

	public static long build() {
		return Holder.getSingleton().nextId();
	}

	// 静态内部类加载实现单例
	private static class Holder {
		private static Snowflake snowflake = new Snowflake(1, 1, 1);

		private static Snowflake getSingleton() {
			return Holder.snowflake;
		}
	}
}

