package com.jeeplus.common.utils.collection;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import com.jeeplus.common.utils.base.annotation.Nullable;
import com.jeeplus.common.utils.collection.type.ConcurrentHashSet;

import com.google.common.collect.Sets;

/**
 * 关于Set的工具集合.
 * 
 * 1. 各种Set的创建函数, 包括ConcurrentHashSet
 * 
 * 2. 集合运算函数(交集，并集等,from guava)
 */
public class SetUtil {

	/**
	 * 根据等号左边的类型，构造类型正确的HashSet.
	 * 
	 * @see Sets#newHashSet()
	 */
	public static <T> HashSet<T> newHashSet() {
		return new HashSet<T>();
	}

	/**
	 * 根据等号左边的类型，构造类型正确的HashSet, 并初始化元素.
	 * 
	 * @see Sets#newHashSet(Object...)
	 */
	@SuppressWarnings("unchecked")
	public static <T> HashSet<T> newHashSet(T... elements) {
		return Sets.newHashSet(elements);
	}

	/**
	 * HashSet涉及HashMap大小，因此建议在构造时传入需要初始的集合，其他如TreeSet不需要.
	 * 
	 * @see Sets#newHashSet(Iterable)
	 */
	public static <T> HashSet<T> newHashSet(Iterable<? extends T> elements) {
		return Sets.newHashSet(elements);
	}

	/**
	 * 创建HashSet并设置初始大小，因为HashSet内部是HashMap，会计算LoadFactor后的真实大小.
	 * 
	 * @see Sets#newHashSetWithExpectedSize(int)
	 */
	public static <T> HashSet<T> newHashSetWithCapacity(int expectedSize) {
		return Sets.newHashSetWithExpectedSize(expectedSize);
	}

	/**
	 * 根据等号左边的类型，构造类型正确的TreeSet, 通过实现了Comparable的元素自身进行排序.
	 * 
	 * @see Sets#newTreeSet()
	 */
	@SuppressWarnings("rawtypes")
	public static <T extends Comparable> TreeSet<T> newSortedSet() {
		return new TreeSet<T>();
	}

	/**
	 * 根据等号左边的类型，构造类型正确的TreeSet, 并设置comparator.
	 * 
	 * @see Sets#newTreeSet(Comparator)
	 */
	public static <T> TreeSet<T> newSortedSet(@Nullable Comparator<? super T> comparator) {
		return Sets.newTreeSet(comparator);
	}

	/**
	 * 根据等号左边的类型，构造类型正确的ConcurrentHashSet
	 */
	public static <T> ConcurrentHashSet<T> newConcurrentHashSet() {
		return new ConcurrentHashSet<T>();
	}

	///////////////// from JDK Collections的常用构造函数 ///////////////////

	/**
	 * 返回一个空的结构特殊的Set，节约空间.
	 * 
	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
	 * 
	 * @see Collections#emptySet()
	 */
	public static final <T> Set<T> emptySet() {
		return Collections.emptySet();
	}

	/**
	 * 如果set为null，转化为一个安全的空Set.
	 * 
	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
	 * 
	 * @see Collections#emptySet()
	 */
	public static <T> Set<T> emptySetIfNull(final Set<T> set) {
		return set == null ? (Set<T>) Collections.EMPTY_SET : set;
	}

	/**
	 * 返回只含一个元素但结构特殊的Set，节约空间.
	 * 
	 * 注意返回的Set不可写, 写入会抛出UnsupportedOperationException.
	 * 
	 * @see Collections#singleton(Object)
	 */
	public static final <T> Set<T> singletonSet(T o) {
		return Collections.singleton(o);
	}

	/**
	 * 返回包装后不可修改的Set.
	 * 
	 * 如果尝试修改，会抛出UnsupportedOperationException
	 * 
	 * @see Collections#unmodifiableSet(Set)
	 */
	public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
		return Collections.unmodifiableSet(s);
	}

	/**
	 * 从Map构造Set的大杀器, 可以用来制造各种Set
	 * 
	 * @see Collections#newSetFromMap(Map)
	 */
	public static <T> Set<T> newSetFromMap(Map<T, Boolean> map) {
		return Collections.newSetFromMap(map);
	}

	//////////////// from guava的集合运算函数/////////////
	/**
	 * set1, set2的并集（在set1或set2的对象）的只读view，不复制产生新的Set对象.
	 * 
	 * 如果尝试写入该View会抛出UnsupportedOperationException
	 */
	public static <E> Set<E> unionView(final Set<? extends E> set1, final Set<? extends E> set2) {
		return Sets.union(set1, set2);
	}

	/**
	 * set1, set2的交集（同时在set1和set2的对象）的只读view，不复制产生新的Set对象.
	 * 
	 * 如果尝试写入该View会抛出UnsupportedOperationException
	 */
	public static <E> Set<E> intersectionView(final Set<E> set1, final Set<?> set2) {
		return Sets.intersection(set1, set2);
	}

	/**
	 * set1, set2的差集（在set1，不在set2中的对象）的只读view，不复制产生新的Set对象.
	 * 
	 * 如果尝试写入该View会抛出UnsupportedOperationException
	 */
	public static <E> Set<E> differenceView(final Set<E> set1, final Set<?> set2) {
		return Sets.difference(set1, set2);
	}

	/**
	 * set1, set2的补集（在set1或set2中，但不在交集中的对象，又叫反交集）的只读view，不复制产生新的Set对象.
	 * 
	 * 如果尝试写入该View会抛出UnsupportedOperationException
	 */
	public static <E> Set<E> disjointView(final Set<? extends E> set1, final Set<? extends E> set2) {
		return Sets.symmetricDifference(set1, set2);
	}
}
