package junkutil.set;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import junkutil.common.StringUtil;

/**
 * Listを集合論的に扱う。
 * @author Hiroyuki Shiota
 */
public class LzList {
	private java.util.List base;

	/**
	 * コンストラクタ
	 */
	public LzList() {
		base = new ArrayList();
	}
	/**
	 * コンストラクタ
	 * @param list
	 */
	public LzList(List list) {
		base = list;
	}
	/**
	 * コンストラクタ
	 * @param list
	 */
	public LzList(Object[] args) {
		base = Arrays.asList(args);
	}
	/**
	 * コンストラクタ
	 * @param list
	 */
	public LzList(Set set) {
		base = Arrays.asList(set.toArray());
	}
	
	/**
	 * コンストラクタ
	 * @param e
	 */
	public LzList(Enumeration e) {
		base = asList(e);
	}

	/**
	 * コンストラクタ
	 * @param i
	 */
	public LzList(Iterator i) {
		base = asList(i);
	}

	/**
	 * リストを取得する。
	 * @return
	 */
	public List list() {
		return base;
	}
	
	/**
	 * orを取得する。
	 * @param arg
	 * @return
	 */
	public LzList or(List arg) {
		List list = new ArrayList(base);
		for (int i = 0; i < arg.size(); i++) {
			Object obj = arg.get(i);
			if (!base.contains(obj)) list.add(obj);
		}
		return new LzList(list);
	}
	
	/**
	 * andを取得する。
	 * @param arg
	 * @return
	 */
	public LzList and(List arg) {
		List list = new ArrayList();
		for (int i = 0; i < arg.size(); i++) {
			Object obj = arg.get(i);
			if (base.contains(obj)) list.add(obj);
		}
		return new LzList(list);
	}
	
	/**
	 * null以外を取得する。
	 * @param arg
	 * @return
	 */
	public LzList notNull() {
		List list = new ArrayList();
		for (int i = 0; i < base.size(); i++) {
			Object obj = base.get(i);
			if (obj != null) list.add(obj);
		}
		return new LzList(list);
	}
	
	/**
	 * 正順Comparator。nullは考慮していない。
	 */
	private static Comparator compStrAsc = new Comparator() {
		public int compare(Object o1, Object o2) {
			return o1.toString().compareTo(o2.toString());
		}
	};
	
	/**
	 * 逆順Comparator。nullは考慮していない。
	 */
	private static Comparator compStrDesc = new Comparator() {
		public int compare(Object o1, Object o2) {
			return -(o1.toString().compareTo(o2.toString()));
		}
	};
	
	/**
	 * 昇順でソートする。
	 * @return
	 */
	public LzList sortAsc() {
		return sort(compStrAsc);
	}
	
	/**
	 * 降順でソートする。
	 * @return
	 */
	public LzList sortDesc() {
		return sort(compStrDesc);
	}
	
	/**
	 * 指定順でソートする。
	 * @return
	 */
	public LzList sort(Comparator c) {
		List list = new ArrayList(base);
		Collections.sort(list, c);
		return new LzList(list);
	}
	
	/**
	 * 絞り込みを行う。nullは除外される。
	 * @param re
	 * @return
	 */
	public LzList grep(String re) {
		List list = new ArrayList();
		Pattern pattern = Pattern.compile(re);
		for (int i = 0; i < base.size(); i++) {
			Object obj = base.get(i);
			if (obj == null) continue;
			if (StringUtil.match(obj.toString(), pattern)) {
				list.add(obj);
			}
		}
		return new LzList(list);
	}
	
	/**
	 * 逆絞り込みを行う。nullは除外される。
	 * @param re
	 * @return
	 */
	public LzList ungrep(String re) {
		List list = new ArrayList();
		Pattern pattern = Pattern.compile(re);
		for (int i = 0; i < base.size(); i++) {
			Object obj = base.get(i);
			if (obj == null) continue;
			if (!StringUtil.match(obj.toString(), pattern)) {
				list.add(obj);
			}
		}
		return new LzList(list);
	}
	
	/**
	 * リストを結合する。
	 * @param arg
	 * @return
	 */
	public LzList join(Collection arg) {
		List list = new ArrayList(base);
		list.addAll(arg);
		return new LzList(list);
	}
	
	/**
	 * ユニークなリストを取得する。
	 * @param arg
	 * @return
	 */
	public LzList uniq() {
		List list = new ArrayList();
		for (int i = 0; i < base.size(); i++) {
			Object obj = base.get(i);
			if (!list.contains(obj)) list.add(obj);
		}
		return new LzList(list);
	}
	
	/**
	 * 反転したリストを取得する。
	 * @return
	 */
	public LzList reverse() {
		List list = new ArrayList();
		int len = base.size();
		for (int i = len-1; i >= 0; i--) {
			list.add(base.get(i));
		}
		return new LzList(list);
	}
	
	/**
	 * クロージャを順に呼び出す。
	 * @param closure
	 * @return
	 */
	public LzList forEach(LzListClosure closure) {
		List list = new ArrayList();
		int len = base.size();
		for (int i = 0; i < len; i++) {
			Object obj = base.get(i);
			if (closure.process(i, len, obj)) {
				list.add(obj);
			}
		}
		return new LzList(list);
	}
	
	/**
	 * 等しいかどうかをテストする。
	 * @param arg
	 * @return
	 */
	public boolean equals(List arg) {
		if (base == arg) return true;
		if (base.size() != arg.size()) return false;
		for (int i = 0; i < base.size(); i++) {
			Object obj1 = base.get(i);
			Object obj2 = arg.get(i);
			if (obj1 == null && obj2 == null) {	
			} else {
				if (obj1 == null || obj2 == null) return false;
				if (!obj1.equals(obj2)) return false;
			}
		}
		return true;
	}

	/**
	 * 順序を揃えたあと、等しいかどうかをテストする。
	 * @param arg
	 * @return
	 */
	public boolean equalsOnSort(List arg) {
		if (base.size() != arg.size()) return false;
		LzList sortedBase = new LzList(base).sortAsc();
		List sortedArg = new LzList(arg).sortAsc().list();
		return sortedBase.equals(sortedArg);
	}
	
	/**
	 * 等しいかどうかをテストする。
	 * @param arg
	 * @return
	 */
	public boolean equals(String arg) {
		return toString().equals(arg);
	}
	
	/**
	 * 文字列化する。
	 * @Override
	 */
	public String toString() {
		return base.toString();
	}
	
	/**
	 * EnumerationをListにする。
	 * @param e
	 * @return
	 */
	public static List asList(Enumeration e) {
		List result = new ArrayList();
		while (e.hasMoreElements()) {
			result.add(e.nextElement());
		}
		return result;
	}
	
	/**
	 * IteratorをListにする。
	 * @param i
	 * @return
	 */
	public static List asList(Iterator i) {
		List result = new ArrayList();
		while (i.hasNext()) {
			result.add(i.next());
		}
		return result;
	}
	
}
