package com.kurukurupapa.tryandroid.fw.util;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import com.kurukurupapa.tryandroid.fw.FwException;

public class ReflectionUtil {
	private static final Field[] ZERO_FIELD_ARR = new Field[] {};

	public static boolean containField(Class<?> cls, String fieldName) {
		try {
			return cls.getDeclaredField(fieldName) != null;
		} catch (SecurityException e) {
			throw new FwException("class=" + cls.getName() + ",fieldName="
					+ fieldName, e);
		} catch (NoSuchFieldException e) {
			return false;
		}
	}

	public static Field getField(Object obj, String fieldName) {
		return getField(obj.getClass(), fieldName);
	}

	public static Field getField(Class<?> cls, String fieldName) {
		try {
			return cls.getDeclaredField(fieldName);
		} catch (SecurityException e) {
			throw new FwException("class=" + cls.getName() + ",fieldName="
					+ fieldName, e);
		} catch (NoSuchFieldException e) {
			throw new FwException("class=" + cls.getName() + ",fieldName="
					+ fieldName, e);
		}
	}

	public static Object getFieldValue(Object target, String fieldName) {
		Field field = getField(target, fieldName);
		return getFieldValue(target, field);
	}

	public static boolean getBooleanFieldValue(Object target, Field field) {
		Object value = getFieldValue(target, field);
		return Boolean.parseBoolean(String.valueOf(value));
	}

	public static boolean getBooleanFieldValue(Object target, String fieldName) {
		Object value = getFieldValue(target, fieldName);
		return Boolean.parseBoolean(String.valueOf(value));
	}

	public static boolean getBooleanFieldValue(Class<?> target, String fieldName) {
		Object value = getFieldValue(target, fieldName);
		return Boolean.parseBoolean(String.valueOf(value));
	}

	public static String getStringFieldValue(Object target, Field field) {
		return (String) getFieldValue(target, field);
	}

	public static String getStringFieldValue(Object target, String fieldName) {
		return (String) getFieldValue(target, fieldName);
	}

	public static String getStringFieldValue(Class<?> target, String fieldName) {
		Object value = getFieldValue(target, fieldName);
		return String.valueOf(value);
	}

	public static Object getFieldValue(Object target, Field field) {
		try {
			return field.get(target);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		}
	}

	public static float getFloatFieldValue(Object obj, Field field) {
		float value = 0;
		try {
			value = field.getFloat(obj);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		}
		return value;
	}

	public static float getFloatFieldValue(Object obj, String fieldName) {
		Field field = getField(obj, fieldName);
		return getFloatFieldValue(obj, field);
	}

	public static int getIntFieldValue(Object obj, Field field) {
		int value = 0;
		try {
			value = field.getInt(obj);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		}
		return value;
	}

	public static int getIntFieldValue(Object obj, String fieldName) {
		Field field = getField(obj, fieldName);
		return getIntFieldValue(obj, field);
	}

	public static long getLongFieldValue(Object obj, Field field) {
		long value = 0;
		try {
			value = field.getLong(obj);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		}
		return value;
	}

	public static long getLongFieldValue(Object obj, String fieldName) {
		Field field = getField(obj, fieldName);
		return getLongFieldValue(obj, field);
	}

	public static Object getFieldValue(Class<?> cls, String fieldName) {
		return getStaticFieldValue(cls, fieldName);
	}

	public static Object getStaticFieldValue(Class<?> cls, String fieldName) {
		Field field = getField(cls, fieldName);
		return getFieldValue(null, field);
	}

	public static void setFieldValue(Object target, String fieldName,
			Object value) {
		Field field = getField(target, fieldName);
		setFieldValue(target, field, value);
	}

	public static void setFieldValue(Object target, Field field, Object value) {
		try {
			field.set(target, value);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		}
	}

	public static List<String> findIntFieldName(Object obj, int value)
			throws IllegalArgumentException, IllegalAccessException {
		List<String> list = new ArrayList<String>();
		Field[] fields = obj.getClass().getFields();
		for (Field e : fields) {
			String name = e.getName();
			if (e.getType() == int.class && e.getInt(obj) == value) {
				list.add(name);
			}
		}
		return list;
	}

	public static Field[] findAllFields(Object obj) {
		if (obj == null) {
			return ZERO_FIELD_ARR;
		}
		return findAllFields(obj.getClass());
	}

	public static Field[] findAllFields(Class<?> cls) {
		return cls.getFields();
	}

	public static List<String> findAllFieldName(Object obj) {
		return findAllFieldName(obj.getClass());
	}

	public static List<String> findAllFieldName(Class<?> cls) {
		List<String> list = new ArrayList<String>();
		Field[] fields = findAllFields(cls);
		for (Field e : fields) {
			list.add(e.getName());
		}
		return list;
	}

	/**
	 * 正規表現に一致するフィールドを取得する。
	 *
	 * @param obj
	 *            対象フィールドを持っているオブジェクト
	 * @param name
	 *            対象フィールドの正規表現文字列名
	 * @return 条件を満たすフィールドの配列
	 */
	public static List<Field> findFields(Object obj, String regex) {
		return findFields(obj.getClass(), regex);
	}

	/**
	 * 正規表現に一致するフィールドを取得する。
	 *
	 * @param cls
	 *            対象フィールドを持っているクラス
	 * @param name
	 *            対象フィールドの正規表現文字列名
	 * @return 条件を満たすフィールドの配列
	 */
	public static List<Field> findFields(Class<?> cls, String regex) {
		List<Field> list = new ArrayList<Field>();
		Pattern p = Pattern.compile(regex);
		Field[] fields = cls.getFields();
		for (Field e : fields) {
			String name = e.getName();
			if (p.matcher(name).matches()) {
				list.add(e);
			}
		}
		return list;
	}

	public static Class<?> loadClass(Object obj, String className) {
		try {
			return obj.getClass().getClassLoader().loadClass(className);
		} catch (ClassNotFoundException e) {
			throw new FwException(e);
		}
	}

	public static Object invokeMethod(Object obj, String methodName) {
		return invokeMethod(obj, methodName, new Object[] { null },
				new Class<?>[] { null });
	}

	public static Object invokeMethod(Object obj, String methodName,
			Object value, Class<?> type) {
		return invokeMethod(obj, methodName, new Object[] { value },
				new Class<?>[] { type });
	}

	public static Object invokeMethod(Object obj, String methodName,
			Object[] argValues, Class<?>[] argTypes) {
		Object result = null;
		try {
			Method method = obj.getClass().getMethod(methodName, argTypes);
			result = method.invoke(obj, argValues);
		} catch (SecurityException e) {
			throw new FwException(e);
		} catch (NoSuchMethodException e) {
			throw new FwException(e);
		} catch (IllegalArgumentException e) {
			throw new FwException(e);
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		} catch (InvocationTargetException e) {
			throw new FwException(e);
		}
		return result;
	}

	public static boolean existMethod(Class<?> cls, String methodName,
			Class<?> argType) {
		return existMethod(cls, methodName, new Class<?>[] { argType });
	}

	public static boolean existMethod(Class<?> cls, String methodName,
			Class<?>[] argTypes) {
		Method method;
		try {
			method = cls.getMethod(methodName, argTypes);
		} catch (SecurityException e) {
			throw new FwException(e);
		} catch (NoSuchMethodException e) {
			method = null;
		}
		return method != null;
	}

	public static <T> T newInstance(Class<T> targetClass) {
		T instance = null;
		try {
			instance = targetClass.newInstance();
		} catch (IllegalAccessException e) {
			throw new FwException(e);
		} catch (InstantiationException e) {
			throw new FwException(e);
		}
		return instance;
	}

	public static String toString(Object obj) {
		if (obj == null || obj instanceof Boolean || obj instanceof Character
				|| obj instanceof Byte || obj instanceof Integer
				|| obj instanceof Long || obj instanceof Float
				|| obj instanceof Double || obj instanceof String) {
			return String.valueOf(obj);
		}

		StringBuilder sb = new StringBuilder();
		for (Field field : findAllFields(obj)) {
			if (sb.length() != 0) {
				sb.append(",");
			}
			sb.append(field.getName() + "=");
			sb.append(toString(getFieldValue(obj, field)));
		}
		return "{" + sb.toString() + "}";
	}

	public static String toString(Object[] objArr) {
		StringBuilder sb = new StringBuilder();
		if (objArr == null) {
			return "{}";
		}
		for (int i = 0; i < objArr.length; i++) {
			if (sb.length() != 0) {
				sb.append(",");
			}
			sb.append(toString(objArr[i]));
		}
		return "{" + sb.toString() + "}";
	}

	public static String toString(List<Object> objList) {
		StringBuilder sb = new StringBuilder();
		if (objList == null) {
			return "{}";
		}
		for (int i = 0; i < objList.size(); i++) {
			if (sb.length() != 0) {
				sb.append(",");
			}
			sb.append(toString(objList.get(i)));
		}
		return "{" + sb.toString() + "}";
	}
}
