package com.jeeplus.common.utils.time;

import java.util.Date;

/**
 * 日期提供者, 使用它而不是直接取得系统时间, 方便测试.
 * 
 * 平时使用DEFAULT，测试时替换为DummyClock，可准确控制时间变化而不用Thread.sleep()等待时间流逝.
 * 
 * @author calvin
 */
public class ClockUtil {

	private static Clock INSTANCE = new DefaultClock();

	/**
	 * 计算流逝的时间
	 */
	public static long elapsedTime(long beginTime) {
		return currentTimeMillis() - beginTime;
	}

	/**
	 * 切换为DummyClock，使用系统时间为初始时间, 单个测试完成后需要调用useDefaultClock()切换回去.
	 */
	public static synchronized DummyClock useDummyClock() {
		INSTANCE = new DummyClock();
		return (DummyClock) INSTANCE;
	}

	/**
	 * 切换为DummyClock，单个测试完成后需要调用useDefaultClock()切换回去.
	 */
	public static synchronized DummyClock useDummyClock(long timeStampMills) {
		INSTANCE = new DummyClock(timeStampMills);
		return (DummyClock) INSTANCE;
	}

	/**
	 * 切换为DummyClock，单个测试完成后需要调用useDefaultClock()切换回去.
	 */
	public static synchronized DummyClock useDummyClock(Date date) {
		INSTANCE = new DummyClock(date);
		return (DummyClock) INSTANCE;
	}

	/**
	 * 重置为默认Clock
	 */
	public static synchronized void useDefaultClock() {
		INSTANCE = new DefaultClock();
	}

	/**
	 * 系统当前时间
	 */
	public static Date currentDate() {
		return INSTANCE.currentDate();
	}

	/**
	 * 系统当前时间戳
	 */
	public static long currentTimeMillis() {
		return INSTANCE.currentTimeMillis();
	}

	/**
	 * 操作系统启动到现在的纳秒数，与系统时间是完全独立的两个时间体系
	 */
	public static long nanoTime() {
		return INSTANCE.nanoTime();
	}

	public interface Clock {

		/**
		 * 系统当前时间
		 */
		Date currentDate();

		/**
		 * 系统当前时间戳
		 */
		long currentTimeMillis();

		/**
		 * 操作系统启动到现在的纳秒数，与系统时间是完全独立的两个时间体系
		 */
		long nanoTime();
	}

	/**
	 * 默认时间提供者，返回当前的时间，线程安全。
	 */
	public static class DefaultClock implements Clock {

		@Override
		public Date currentDate() {
			return new Date();
		}

		@Override
		public long currentTimeMillis() {
			return System.currentTimeMillis();
		}

		@Override
		public long nanoTime() {
			return System.nanoTime();
		}
	}

	/**
	 * 可配置的时间提供者，用于测试.
	 */
	public static class DummyClock implements Clock {

		private long time;
		private long nanoTme;

		public DummyClock() {
			this(System.currentTimeMillis());
		}

		public DummyClock(Date date) {
			this(date.getTime());
		}

		public DummyClock(long time) {
			this.time = time;
			this.nanoTme = System.nanoTime();
		}

		@Override
		public Date currentDate() {
			return new Date(time);
		}

		@Override
		public long currentTimeMillis() {
			return time;
		}

		/**
		 * 获取nanotime
		 */
		@Override
		public long nanoTime() {
			return nanoTme;
		}

		/**
		 * 重新设置日期.
		 */
		public void updateNow(Date newDate) {
			time = newDate.getTime();
		}

		/**
		 * 重新设置时间.
		 */
		public void updateNow(long newTime) {
			this.time = newTime;
		}

		/**
		 * 滚动时间.
		 */
		public void increaseTime(int millis) {
			time += millis;
		}

		/**
		 * 滚动时间.
		 */
		public void decreaseTime(int millis) {
			time -= millis;
		}

		/**
		 * 设置nanotime.
		 */
		public void setNanoTime(long nanoTime) {
			this.nanoTme = nanoTime;
		}
	}
}
