package hiro.yoshioka.util;

import java.nio.charset.Charset;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtil {
	public static final String NULL_STRING = "NULL";
	public static final String EMPTY_STRING = "";
	public static final String HALF_SPACE__STRING = " ";
	public static final String TAB_STRING = "\t";
	public static final String YEN_STRING = "\\";
	public static final String DOUBLE_QUOTE_STRING = "\"";
	public static final String SINGLE_QUOTE_STRING = "'";
	public static final char TAB_CHAR = TAB_STRING.charAt(0);
	public static final char YEN_CHAR = YEN_STRING.charAt(0);
	public static final char DOUBLE_QUOTE_CHAR = DOUBLE_QUOTE_STRING.charAt(0);
	public static final char SINGLE_QUOTE_CHAR = SINGLE_QUOTE_STRING.charAt(0);
	public static final char[] EMPTY_CHARACTOR_ARRAY = new char[0];
	public static final String[] EMPTY_STRING_ARRAY = new String[0];
	public static final String[][] EMPTY_STRING_ARRAY2 = new String[0][0];
	public static final Pattern P_SEMICORON_WHITE = Pattern
			.compile(";\\p{Space}");
	public static final Pattern P_SQL_COMMENT = Pattern.compile(
			"(--(.*)|/[*][^*]*?[*]/)", Pattern.MULTILINE);
	public static final String[] ENCODINGS = { "ASCII", "ISO8859_1", "UTF-8",
			"UTF-16", "UTF-16BE", "UTF-16LE", "EUC_JP", "SJIS", "MS932",
			"ISO2022JP" };

	static Pattern P = Pattern.compile("(['][^']*?['])|([ ]+)");

	public static final String LINE_SEPARATOR;
	public static final String DEFAULT_ENCODING;
	static {
		LINE_SEPARATOR = System.getProperties().getProperty("line.separator");
		DEFAULT_ENCODING = System.getProperties().getProperty("file.encoding");
	}

	public static void main(String[] args) {
		char[] dic = { 'a', 'b', 'c', 'd', '0', '/' };
		for (int i = 0; i < 500; i++) {
			System.out.println("[" + i + "]=" + convertRadix(i, dic));
		}
	}

	/**
	 * charactors = { 'a', 'b', 'c', 'd', '0', '/' };<BR>
	 * l = {0,16}<BR>
	 * [0]=a [1]=b [2]=c [3]=d [4]=0 [5]=aa [6]=ab [7]=ac<BR>
	 * [9]=a0 [10]=a/ [11]=ba [12]=bb [13]=bc [14]=bd [15]=b0 [16]=b/
	 * 
	 * @param l
	 * @param charactors
	 * @return
	 */
	public static String convertRadix(int l, char[] charactors) {
		l++;
		StringBuilder ret = new StringBuilder();
		while (l > 0) {
			int di = l % charactors.length;
			if (l < charactors.length && di > 0) {
				ret.insert(0, charactors[di - 1]);
			} else {
				ret.insert(0, charactors[di]);
			}
			l /= charactors.length;
		}
		return ret.toString();
	}

	public static void append(StringBuilder buf, String text) {
		buf.append(text);
	}

	public static void appendln(StringBuilder buf, String text) {
		appendln(buf, 0, text);
	}

	public static void appendln(StringBuilder buf, int depth, String text) {
		if (depth > 0) {
			for (int i = 0; i < depth; i++) {
				buf.append("  ");
			}
		}
		buf.append(text).append(LINE_SEPARATOR);
	}

	public static boolean isIgnoreCaseStartsWith(String left, String right) {
		return nvl(left).toUpperCase().startsWith(nvl(right).toUpperCase());
	}

	public static List<String> split(Object target) {
		if (target == null) {
			return Collections.EMPTY_LIST;
		}

		List<String> retList = new ArrayList<String>();
		String[] targets = null;
		if (target instanceof String) {
			targets = ((String) target).split(",");
		} else if (target instanceof String[]) {
			targets = (String[]) target;
		} else {
			retList.add(target.toString());
		}
		if (targets != null) {
			for (String str : targets) {
				if (!StringUtil.isEmpty(str)) {
					retList.add(str.trim());
				}
			}
		}

		return retList;
	}

	public static List<Long> parseLongListNoneException(Object num) {
		if (num == null) {
			return Collections.EMPTY_LIST;
		}
		List<Long> retList = new ArrayList<Long>();
		Object[] nums = null;
		if (num instanceof String) {
			nums = ((String) num).split(",");
		} else if (num instanceof Object[]) {
			nums = (Object[]) num;
		}
		if (nums == null) {
			Long l = toLong(num);
			if (l != -1L) {
				retList.add(l);
			}
		} else {
			for (Object n : nums) {
				Long l = toLong(n);
				if (l != -1L) {
					retList.add(l);
				}
			}
		}
		return retList;
	}

	private static final Long toLong(Object o) {
		if (o instanceof Long) {
			return (Long) o;
		} else {
			try {
				return Long.valueOf(o.toString().trim());
			} catch (NumberFormatException e) {
				return -1L;
			}
		}
	}

	public static int parseIntNoneException(String num) {
		return parseIntNoneException(num, 0);
	}

	public static int parseIntNoneException(String num, int defaultNum) {
		if (num == null) {
			return defaultNum;
		}
		try {
			return Integer.parseInt(num);
		} catch (Exception e) {
			return defaultNum;
		}
	}

	/**
	 * SQL文出力用に次の置換を行う ' -> '' \ -> \\
	 * 
	 * @param input
	 *            置換対象の文字列
	 * @return 置換処理後の文字列
	 */
	static public String cnv2SQLEscape(String input) {
		input = input.replaceAll("'", "''");
		input = input.replaceAll("\\\\", "\\\\\\\\");
		return input;
	}

	/**
	 * DUMP WITH WHITESPACE
	 */
	public static void dumpWhiteSpace(String target) {
		if (isEmpty(target)) {
			return;
		}
		int index = 0;
		int lineIndex = 0;
		DecimalFormat df = new DecimalFormat("00000");
		System.out.println("     \t1\t2\t3\t4\t5\t6\t7\t8\t9\t0");
		out: while (true) {
			System.out.print(df.format(lineIndex * 10) + "\t");
			for (int i = 0; i < 10; i++) {
				if (index >= target.length()) {
					break out;
				}
				char c = (char) target.charAt(index++);
				if (Character.isISOControl(c) || Character.isWhitespace(c)) {
					if (c == '\t') {
						System.out.print("TB");
					} else if (c == ' ') {
						System.out.print("SP");
					} else if (c == '　') {
						System.out.print("ZP");
					} else if (c == '\r') {
						System.out.print("CR");
					} else if (c == '\n') {
						System.out.print("LF");
					} else {
						System.out.print("*");
						System.out.println("char=[" + c + "] type= "
								+ Character.getType(c));
					}
				} else {
					System.out.print(String.valueOf(c));
				}

				System.out.print("\t");
			}
			lineIndex++;
			System.out.println();
		}
		System.out.println();
	}

	public static String cutByBytes(CharSequence input, int cutSize,
			Charset charset) {

		StringBuilder builder = new StringBuilder(cutSize);
		int workLength = 0;
		for (int index = 0; index < input.length(); index++) {
			char c = input.charAt(index);
			int length = Character.toString(c).getBytes(charset).length;

			if (workLength + length > cutSize) {
				return builder.toString();
			}
			builder.append(c);
			workLength += length;
		}
		return builder.toString();
	}

	public static List<String> splitBytes(CharSequence input, int splitSize,
			Charset charset) {
		List<String> result = new ArrayList<String>();

		StringBuilder builder = new StringBuilder(splitSize);
		int workLength = 0;
		for (int index = 0; index < input.length(); index++) {
			char c = input.charAt(index);
			int length = Character.toString(c).getBytes(charset).length;

			if (workLength + length > splitSize) {
				result.add(builder.toString());
				builder.setLength(0);
				workLength = 0;
			}
			builder.append(c);
			workLength += length;
		}
		if (builder.length() > 0) {
			result.add(builder.toString());
		}
		return result;
	}

	public static String cnvWithoutComment(String target) {
		Matcher m = P_SQL_COMMENT.matcher(target);
		return m.replaceAll("");
	}

	public static String continuingBlank2aBlank(String target) {
		Matcher m = P.matcher(target);
		StringBuffer rel = new StringBuffer();

		while (m.find()) {
			if (m.group(2) == null) {
				// rel.append(m.group(1));
			} else {
				m.appendReplacement(rel, HALF_SPACE__STRING);
			}
		}
		m.appendTail(rel);
		return rel.toString();
	}

	public static String nvl(Object string) {
		return string == null ? EMPTY_STRING : string.toString();
	}

	public static String getShortenDate(java.util.Date date) {
		Calendar cal = GregorianCalendar.getInstance();
		Calendar targetCal = GregorianCalendar.getInstance();
		targetCal.setTime(date);
		if (cal.get(Calendar.YEAR) == targetCal.get(Calendar.YEAR)) {
			return String.format("%tm/%1$td %1$tH:%1$tM", date);
		} else {
			// return String.format("%tY/%1$tm/%1$td(%1$ta)", date);
			return String.format("%tY/%1$tm/%1$td", date);
		}
	}

	public static boolean isEmpty(Object o) {
		if (o == null) {
			return true;
		}
		if (o.toString().trim().length() == 0) {
			return true;
		}
		return false;
	}

	public static String join(Collection collection, String separator) {
		if (collection == null || collection.size() == 0) {
			return StringUtil.EMPTY_STRING;
		}
		if (collection.size() == 1) {
			return StringUtil.nvl(collection.iterator().next());
		}
		if (separator == null) {
			separator = StringUtil.EMPTY_STRING;
		}
		StringBuilder buf = new StringBuilder();
		Iterator ite = collection.iterator();
		buf.append(StringUtil.nvl(ite.next()));
		while (ite.hasNext()) {
			buf.append(separator);
			buf.append(StringUtil.nvl(ite.next()));
		}
		return buf.toString();
	}

	public static boolean isCRorLF(char target) {
		return isCRorLFor(target, EMPTY_CHARACTOR_ARRAY);
	}

	public static boolean isCRorLFor(char target, char[] cs) {
		if ('\r' == target || '\n' == target) {
			return true;
		}
		if (cs == null || cs.length == 0) {
			return false;
		}
		for (char c : cs) {
			if (target == c) {
				return true;
			}
		}
		return false;
	}

	public static String[][] deleteColumn(String[][] target, int deleteIndex) {
		List<String[]> retList = new ArrayList<String[]>();
		for (String[] t : target) {
			String[] result = new String[t.length - 1];
			System.arraycopy(t, 0, result, 0, deleteIndex);
			if (t.length != deleteIndex) {
				System.arraycopy(t, deleteIndex + 1, result, deleteIndex,
						t.length - deleteIndex - 1);
			}
			retList.add(result);
		}
		return retList.toArray(new String[retList.size()][]);
	}

	public static String resetCRLF(String target, boolean many2oneCrLl) {
		return resetCRLF(target, many2oneCrLl, LineSeparatorEnum.CRLF);
	}

	public static String resetCRLF(String target, boolean many2oneCrLl,
			LineSeparatorEnum lineSeparator) {
		String t = target.replaceAll(LineSeparatorEnum.CRLF.getLineSeparator(),
				LineSeparatorEnum.LF.getLineSeparator());
		t = t.replaceAll(LineSeparatorEnum.CR.getLineSeparator(),
				LineSeparatorEnum.LF.getLineSeparator());
		if (many2oneCrLl) {
			t = t.replaceAll("[\n]+", LineSeparatorEnum.LF.getLineSeparator());
		}
		return t.replaceAll(LineSeparatorEnum.LF.getLineSeparator(),
				lineSeparator.getLineSeparator());
	}

	public static String getStackTraceString(Throwable e) {
		return getStackTraceString(e, Integer.MAX_VALUE);
	}

	public static String getStackTraceString(Throwable e, int maxCount) {
		StringBuilder buf = new StringBuilder();
		StackTraceElement[] elm = e.getStackTrace();
		if (e.getMessage() == null) {
			buf.append("Message is null. ");
		} else {
			buf.append(e.getMessage());
		}
		for (int i = 0; i < elm.length && i < maxCount; i++) {
			buf.append(elm[i] + LINE_SEPARATOR);
		}
		return resetCRLF(buf.toString(), true);
	}

	public static String escName(String str) {
		// ●「<」→「&lt;」
		// ●「&」→「&amp;」
		// ●「>」→「&gt;」
		// ●「"」→「&quot;」
		// ●「'」→「&apos;」
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			switch (c) {
			case '(':
			case '（':
				buf.append("_");
				break;
			case ')':
			case '）':
				break;
			case '%':
				break;
			case '％':
				break;
			case '<':
				buf.append("&lt;");
				break;
			case '&':
				buf.append("&amp;");
				break;
			case '>':
				buf.append("&gt;");
				break;
			case '"':
				buf.append("&quot;");
				break;
			case '\'':
				buf.append("&apos;");
				break;

			default:
				buf.append(c);
				break;
			}
		}
		return buf.toString();
	}

	public static String esc(String str) {
		// ●「<」→「&lt;」
		// ●「&」→「&amp;」
		// ●「>」→「&gt;」
		// ●「"」→「&quot;」
		// ●「'」→「&apos;」
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < str.length(); i++) {
			char c = str.charAt(i);
			switch (c) {
			case '<':
				buf.append("&lt;");
				break;
			case '&':
				buf.append("&amp;");
				break;
			case '>':
				buf.append("&gt;");
				break;
			case '"':
				buf.append("&quot;");
				break;
			case '\'':
				buf.append("&apos;");
				break;

			default:
				buf.append(c);
				break;
			}
		}
		return buf.toString();
	}

	public static String getThisLineSeparator(String text) {
		return LineSeparatorEnum.getThisLineSeparator(text).getLineSeparator();
	}

	public static String[] getLines(String text) {
		return text.split("\r\n|\r|\n");
	}

	public static List<String> getLinesRangeOf(String text, int startLine,
			int endLine, boolean withResetCrLf) {
		if (startLine > endLine) {
			System.err
					.println("Illegal Argument endLine must be gretor-equals than startLine. start["
							+ startLine + "] end[" + endLine + "]");
			return Collections.EMPTY_LIST;
		}
		if (withResetCrLf) {
			text = StringUtil.resetCRLF(text, false);
		}
		List<String> retList = new ArrayList<String>();
		int iNum = 1;
		String[] lines = getLines(text);
		if (lines.length < startLine) {
			return Collections.EMPTY_LIST;
		}
		for (String line : lines) {
			if (iNum > endLine) {
				break;
			}
			if (startLine <= iNum) {
				retList.add(line);
			}
			iNum++;
		}
		return retList;
	}

	public static String cnvMilliToTimeString(long millisec) {
		long s = TimeUnit.SECONDS.convert(millisec, TimeUnit.MILLISECONDS);
		long m = TimeUnit.MINUTES.convert(millisec, TimeUnit.MILLISECONDS);
		long h = TimeUnit.HOURS.convert(millisec, TimeUnit.MILLISECONDS);
		return String.format("%dh:%dm:%ds", h, m, s);
	}
}