package com.goafanti.core.mybatis;

import com.goafanti.common.utils.LoggerUtils;
import com.goafanti.common.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

import java.text.MessageFormat;
import java.util.Calendar;

/**
 *
 * @author xiaolong
 *
 */
public class JDBCIdGenerator {

	private static final long	JDBC_EPOCH;

	private static final long	SEQUENCE_BITS				= 12L;

	private static final long	WORKER_ID_BITS				= 10L;

	private static final long	SEQUENCE_MASK				= (1 << SEQUENCE_BITS) - 1;

	private static final long	WORKER_ID_LEFT_SHIFT_BITS	= SEQUENCE_BITS;

	private static final long	TIMESTAMP_LEFT_SHIFT_BITS	= WORKER_ID_LEFT_SHIFT_BITS + WORKER_ID_BITS;

	private static final long	WORKER_ID_MAX_VALUE			= 1L << WORKER_ID_BITS;

	private static final Logger	logger						= LoggerFactory.getLogger(JDBCIdGenerator.class);

	private static long			workerId;

	static {
		Calendar calendar = Calendar.getInstance();
		calendar.set(2016, Calendar.NOVEMBER, 1);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		JDBC_EPOCH = calendar.getTimeInMillis();
		initWorkerId();
	}

	private long	sequence;

	private long	lastTime;

	static void initWorkerId() {
		String workerId = System.getProperty("jdbc.worker.id");
		if (workerId == null)workerId="1001";
		if (StringUtils.isNoneBlank(workerId)) {
			setWorkerId(Long.valueOf(workerId));
			return;
		}
		workerId = System.getenv("JDBC_WORKER_ID");
		if (StringUtils.isNoneBlank(workerId)) {
			setWorkerId(Long.valueOf(workerId));
			return;
		}
		setWorkerId(0l);
	}

	/**
	 * 设置工作进程Id.
	 *
	 * @param workerId
	 *            工作进程Id
	 */
	public static void setWorkerId(final Long workerId) {
		Assert.isTrue(workerId > 0L && workerId < WORKER_ID_MAX_VALUE, "工作进程ID不能为空，且小于1024");
		JDBCIdGenerator.workerId = workerId;
	}

	/**
	 * 生成Id.
	 *
	 * @return 返回@{@link Long}类型的Id
	 */
	public synchronized Long generateId() {
		long time = System.currentTimeMillis();
		Assert.isTrue(lastTime <= time,
				MessageFormat.format(
						"Clock is moving backwards, last time is {0} milliseconds, current time is {1} milliseconds",
						lastTime, time));
		if (lastTime == time) {
			if (0L == (sequence = ++sequence & SEQUENCE_MASK)) {
				time = waitUntilNextTime(time);
			}
		} else {
			sequence = 0;
		}
		lastTime = time;
		Long res = ((time - JDBC_EPOCH) << TIMESTAMP_LEFT_SHIFT_BITS) | (workerId << WORKER_ID_LEFT_SHIFT_BITS)
				| sequence;
		LoggerUtils.debug(logger, "生成JDBCID：[%s] -- [%s] -- [%s] -- [%s]", lastTime, workerId, sequence, res);
		return res;
	}

	private long waitUntilNextTime(final long lastTime) {
		long time = System.currentTimeMillis();
		while (time <= lastTime) {
			time = System.currentTimeMillis();
		}
		return time;
	}

	public String getNOgenerateId(){
		StringBuffer str=new StringBuffer("NO");
		str=str.append(generateId());
		return str.toString();
	}
}
