package com.liquidnet.commons.lang.util;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;


public class RandomUtil {


	private static Random getRandom() {
		Random random = new Random(System.currentTimeMillis());
		return random;
	}

	/**
	 * 以等概率从指定的对象列表中随机选择一个对象
	 */
	public static <T> T getRandomObject(T... objs) {
		return objs[getRandom().nextInt(objs.length)];
	}

	/**
	 * 以等概率从指定的对象列表中随机选择一个对象
	 */
	public static <T> T getRandomObject(List<T> objs) {
		return objs.get(getRandom().nextInt(objs.size()));
	}


	/**
	 * 从[min,max)中随机一个整数
	 */
	public static int getRandomInt(int min, int max) {
		return getRandom().nextInt(max - min) + min;
	}

	/**
	 * 从[min,max)中随机一个整数
	 */
	public static int getRandomIntTime(int min, int max) {
		Random random = new Random(System.currentTimeMillis()-(new Random().nextInt()));
		return random.nextInt(max - min) + min;
	}

	/**
	 * 获取随机boolean型
	 *
	 * @return
	 */
	public static boolean getRandomBoolean() {
		return getRandom().nextBoolean();
	}

	/**
	 * 得到随即选择器
	 *
	 * @return
	 */
	public static RandomPick getRandomPick() {
		RandomPick randomPick = new RandomPick();
		randomPick.random = getRandom();
		return randomPick;
	}

	/**
	 * 得到百分比选择器
	 *
	 * @return
	 */
	public static PercentPick getPercentPick() {
		PercentPick percentPick = new PercentPick();
		return percentPick;
	}

	public static RangePick getRangePick() {
		RangePick pick = new RangePick();
		pick.random = getRandom();
		return pick;
	}

	/**
	 * 随即从候选对象中选择一个对象出来 下面这个示例演示了加入1，2，3三个数，并且权重分别是1，2，3的随即挑选示例 RandomPick pick
	 * = new RandomPick(); pick.addCandidate(1, 1); pick.addCandidate(2, 2);
	 * pick.addCandidate(3, 3); Map<Object, Integer> map = new HashMap<Object,
	 * Integer>(); for(int i=0;i<10000;i++){ Object o = pick.pick(); Integer
	 * integer = map.get(o); if(integer==null){ map.put(o, 1); }else{ map.put(o,
	 * ++integer); } } System.out.println(map);
	 *
	 * @author <a href="mailto:xin.zhao@happyelements.com">xin.zhao</a>
	 * @version 1.0
	 * @since 1.0
	 */
	public static class RandomPick {
		private RandomPick() {
		}

		private LinkedList<RandomIndex> index = new LinkedList<RandomIndex>();
		private Random random;

		public static RandomPick getInstance() {
			return new RandomPick();
		}

		/**
		 * 添加一个候选对象
		 *
		 * @param object
		 *            候选对象
		 * @param weight
		 *            权重
		 * @return
		 */
		public RandomPick addCandidate(Object object, int weight) {
			int next = 0;
			if (index.peek() != null) {
                next = index.getLast().getStart() + index.getLast().getWeight();
            }
			RandomIndex randomIndex = new RandomIndex();
			randomIndex.setObject(object);
			randomIndex.setStart(next);
			randomIndex.setWeight(weight);
			index.add(randomIndex);
			return this;
		}

		/**
		 * 添加一个候选对象列表， 默认权重都是1
		 *
		 * @param list
		 * @return
		 */
		public RandomPick addCandidateList(Object[] list) {
			if (list == null) {
                return this;
            }
			for (Object o : list) {
				addCandidate(o, 1);
			}
			return this;
		}

		/**
		 * 添加一个候选对象列表， 默认权重都是1
		 *
		 * @param list
		 * @return
		 */
		public RandomPick addCandidateList(List<? extends Object> list) {
			if (list == null) {
                return this;
            }
			for (Object o : list) {
				addCandidate(o, 1);
			}
			return this;
		}

		public RandomPick addCandidateList(List<? extends Object> list,Object except) {
			if (list == null) {
                return this;
            }
			for (Object o : list) {
				if(o != except) {
                    addCandidate(o, 1);
                }
			}
			return this;
		}

		public Object pick() {
			if(index.isEmpty()) {
                return null;
            }
			if(index.size() == 1) {
                return index.getFirst().getObject();
            }

			int rIndex = random.nextInt(index.getLast().getStart() + index.getLast().getWeight());
			for (RandomIndex ri : index) {
				if (rIndex >= ri.getStart() && rIndex < (ri.getStart() + ri.getWeight())) {
                    return ri.getObject();
                }
			}
			return null;
		}

		/**
		 * @param ratio
		 *            在原随机出的数字的基础之上乘以ratio作为新的随机数
		 */
		public Object pick(double ratio) {
			int maxValue = index.getLast().getStart() + index.getLast().getWeight();
			int rIndex = random.nextInt(maxValue);
			rIndex = (int) (rIndex * ratio);
			if (rIndex >= maxValue) {
				rIndex = maxValue - 1;
			}
			for (RandomIndex ri : index) {
				if (rIndex >= ri.getStart() && rIndex < (ri.getStart() + ri.getWeight())) {
                    return ri.getObject();
                }
			}
			return null;
		}

		/**
		 * 清除候选对象
		 *
		 * @return
		 */
		public RandomPick clear() {
			index.clear();
			return this;
		}
	}

	/**
	 * 权重位置记录器
	 *
	 * @author <a href="mailto:xin.zhao@happyelements.com">xin.zhao</a>
	 * @version 1.0
	 * @since 1.0
	 */
	private static class RandomIndex {
		private int start;
		private int weight;
		private Object object;

		public int getStart() {
			return start;
		}

		public void setStart(int start) {
			this.start = start;
		}

		public int getWeight() {
			return weight;
		}

		public void setWeight(int weight) {
			this.weight = weight;
		}

		public Object getObject() {
			return object;
		}

		public void setObject(Object object) {
			this.object = object;
		}
	}

	/**
	 * 按百分比选择概率
	 *
	 * @author <a href="mailto:xin.zhao@happyelements.com">xin.zhao</a>
	 * @version 1.0
	 * @since 1.0
	 */
	public static class PercentPick {
		private PercentPick() {
		}

		/**
		 * 随即返回选择结果
		 *
		 * @param weight
		 *            权重，一般会小于100，权重除以100就是选择的概率
		 * @return
		 */
		public boolean getRandomValue(int weight) {
			if (weight == 0) {
                return false;
            }
			if (weight >= 100) {
                return true;
            }
			RandomPick pick = getRandomPick();
			pick.addCandidate(true, weight);
			pick.addCandidate(false, 100 - weight);
			return (Boolean) pick.pick();
		}
	}

	public static class RangePick {
		private RangePick() {
		}

		private Random random;

		public int getRandomValue(int min, int max) {
			double r = random.nextDouble();
			int r2 = (int) (((max - min) + 1) * r);
			return min + r2;
		}
	}

	// 获得不重复的随机数数组，取值范围[min,max)，个数size
	public static int[] getRandomIntWithoutReduplicate(int min, int max, int size) {
		int[] result = new int[size];// 用于存储结果的数组

		int arraySize = max - min;// 用于放"牌"的数组大小
		int[] intArray = new int[arraySize];// 用于放"牌"的数组
		// 初始化"牌盒"，比如取值范围是[3,10)则"牌盒"里放的"牌"就是3，4，5，6，7，8，9
		for (int i = 0; i < intArray.length; i++) {
			intArray[i] = i + min;
		}
		// 获取不重复的随机数数组
		for (int i = 0; i < size; i++) {
			int c = getRandomInt(min, max - i);// 获取到一个随机数
			int index = c - min;// 这个随机数在"牌盒"里的位置
			swap(intArray, index, arraySize - 1 - i);// 将这张"牌"放到"牌盒"的最后面
			result[i] = intArray[arraySize - 1 - i];// 把这张"牌"的值扔到存储结果的数组里
		}
		return result;
	}


	private static void swap(int[] array, int x, int y) {// 交换数组arry,
		int temp = array[x];
		array[x] = array[y];
		array[y] = temp;
	}

}
