package org.xson.tangyuan.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.xson.tangyuan.TangYuanException;
import org.xson.tangyuan.ognl.FieldVo;
import org.xson.tangyuan.ognl.FieldVoWrapper;

public class TypeUtils {

	// private static XsonClassLoader classLoader = new XsonClassLoader();

	public static char ARRAY_MARK = '[';

	public static String toAsmClass(String className) {
		return className.replace('.', '/');
	}

	public static String getLittleClassName(String fullName) {
		int index = fullName.lastIndexOf(".");
		if (index > 0) {
			return fullName.substring(index + 1);
		} else {
			return fullName;
		}
	}

	public static FieldVoWrapper getBeanField(Class<?> clazz) {
		FieldVoWrapper fieldVoWrapper = FieldVo.fieldVoWrapperCache.get(clazz);
		if (null != fieldVoWrapper) {
			return fieldVoWrapper;
		}
		List<FieldVo> fieldVoList = getBeanField0(clazz);
		fieldVoWrapper = new FieldVoWrapper(fieldVoList);
		FieldVo.fieldVoWrapperCache.put(clazz, fieldVoWrapper);
		return fieldVoWrapper;
	}

	private static List<FieldVo> getBeanField0(Class<?> clazz) {
		List<Field> fieldList = getFieldList(clazz);
		Map<String, Method> methodMap = getMethodMap(clazz);
		List<FieldVo> fieldInfoList = new ArrayList<FieldVo>();

		int size = fieldList.size();
		for (int i = 0; i < size; i++) {
			Field field = fieldList.get(i);
			if (checkField(field)) {
				continue;
			}
			String fieldName = field.getName();

			String getName = getFormatMethodName("get", fieldName);
			Method getter = methodMap.get(getName);

			if (null == getter) {
				getName = getFormatMethodName("is", fieldName);
				getter = methodMap.get(getName);
				if (null == getter) {
					continue;
				}
			}

			if (checkGetter(getter)) {
				continue;
			}

			String setName = getFormatMethodName("set", fieldName);
			Method setter = methodMap.get(setName);
			if (null == setter) {
				continue;
			}

			if (checkSetter(setter, field)) {
				continue;
			}

			fieldInfoList.add(new FieldVo(field, getter, setter, clazz));
		}

		Collections.sort(fieldInfoList);
		return fieldInfoList;
	}

	private static String getFormatMethodName(String prefix, String name) {
		if (1 == name.length()) {
			return prefix + name.toUpperCase();
		} else {
			return prefix + name.substring(0, 1).toUpperCase() + name.substring(1);
		}
	}

	private static boolean checkField(Field field) {
		int modifiers = field.getModifiers();
		if (Modifier.isFinal(modifiers)) {
			return true;
		}
		if (Modifier.isTransient(modifiers)) {
			return true;
		}
		if (Modifier.isStatic(modifiers)) {
			return true;
		}
		return false;
	}

	private static boolean checkGetter(Method method) {
		int modifiers = method.getModifiers();
		if (Modifier.isStatic(modifiers)) {
			return true;
		}
		if (Modifier.isNative(modifiers)) {
			return true;
		}
		if (Modifier.isAbstract(modifiers)) {
			return true;
		}
		if (method.getReturnType().equals(Void.TYPE)) {
			return true;
		}
		if (method.getParameterTypes().length != 0) {
			return true;
		}
		if (method.getReturnType() == ClassLoader.class) {
			return true;
		}
		if (method.getName().equals("getClass")) {
			return true;
		}
		return false;
	}

	private static boolean checkSetter(Method method, Field field) {
		int modifiers = method.getModifiers();
		if (Modifier.isStatic(modifiers)) {
			return true;
		}
		if (Modifier.isNative(modifiers)) {
			return true;
		}
		if (Modifier.isAbstract(modifiers)) {
			return true;
		}
		if (!method.getReturnType().equals(Void.TYPE)) {
			return true;
		}
		if (method.getParameterTypes().length != 1) {
			return true;
		}
		if (!method.getParameterTypes()[0].equals(field.getType())) {
			return true;
		}
		return false;
	}

	private static List<Field> getFieldList(Class<?> clazz) {
		List<Field> fieldList = new ArrayList<Field>();
		Class<?> currentClazz = clazz;
		do {
			Field[] fields = currentClazz.getDeclaredFields();
			for (Field item : fields) {
				fieldList.add(item);
			}
			currentClazz = currentClazz.getSuperclass();
		} while (currentClazz != null && currentClazz != Object.class);
		return fieldList;
	}

	public static Map<String, Method> getMethodMap(Class<?> clazz) {
		Map<String, Method> methodMap = new HashMap<String, Method>();
		Method[] methods = clazz.getMethods();
		int length = methods.length;
		for (int i = 0; i < length; i++) {
			methodMap.put(methods[i].getName(), methods[i]);
		}
		return methodMap;
	}

	public static int getArrayDimensions(Class<?> clazz) {
		String var = clazz.getName();
		char[] data = var.toCharArray();
		int count = 0;
		for (int i = 0; i < data.length; i++) {
			if (ARRAY_MARK == data[i]) {
				count++;
			} else {
				break;
			}
		}
		return count;
	}

	public static String[] getExceptionDesc(Method method) {
		Class<?>[] exClasses = method.getExceptionTypes();
		if (null != exClasses && exClasses.length > 0) {
			String[] exClassesName = new String[exClasses.length];
			for (int i = 0; i < exClasses.length; i++) {
				exClassesName[i] = toAsmClass(exClasses[i].getName());
			}
			return exClassesName;
		}
		return null;
	}

	public static String getDesc(Method method) {
		StringBuffer buf = new StringBuffer();
		buf.append("(");
		java.lang.Class<?>[] types = method.getParameterTypes();
		for (int i = 0; i < types.length; ++i) {
			buf.append(getDesc(types[i]));
		}
		buf.append(")");
		buf.append(getDesc(method.getReturnType()));
		return buf.toString();
	}

	public static String getDesc(Class<?> returnType) {
		if (returnType.isPrimitive()) {
			return getPrimitiveLetter(returnType);
		} else if (returnType.isArray()) {
			return "[" + getDesc(returnType.getComponentType());
		} else {
			return "L" + getType(returnType) + ";";
		}
	}

	public static String getType(Class<?> parameterType) {
		if (parameterType.isArray()) {
			return "[" + getDesc(parameterType.getComponentType());
		} else {
			if (!parameterType.isPrimitive()) {
				String clsName = parameterType.getName();
				return clsName.replaceAll("\\.", "/");
			} else {
				return getPrimitiveLetter(parameterType);
			}
		}
	}

	public static String getPrimitiveLetter(Class<?> type) {
		if (Integer.TYPE.equals(type)) {
			return "I";
		} else if (Void.TYPE.equals(type)) {
			return "V";
		} else if (Boolean.TYPE.equals(type)) {
			return "Z";
		} else if (Character.TYPE.equals(type)) {
			return "C";
		} else if (Byte.TYPE.equals(type)) {
			return "B";
		} else if (Short.TYPE.equals(type)) {
			return "S";
		} else if (Float.TYPE.equals(type)) {
			return "F";
		} else if (Long.TYPE.equals(type)) {
			return "J";
		} else if (Double.TYPE.equals(type)) {
			return "D";
		}
		throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
	}

	public static String getComponentDesc(Class<?> type, int dimension) {
		String brackets = "";
		int count = dimension;
		while (count-- > 1) {
			brackets += "[";
		}
		String clsName = null;
		if (type.isPrimitive()) {
			clsName = getPrimitiveLetter(type);
		} else {
			clsName = "L" + type.getName() + ";";
		}
		return brackets + clsName;
	}

	public static Class<?> getArrayType(Class<?> componentType, int dimension) {
		if (1 == dimension) {
			return componentType;
		} else if (dimension > 1) {
			// return loadClass(getComponentDesc(componentType, dimension));
			return findClass(getComponentDesc(componentType, dimension));
		} else {
			throw new TangYuanException("Unlawful dimension:" + dimension);
		}
	}

	public static Class<?> getComponentType(Class<?> type) {
		if (type.isArray()) {
			return getComponentType(type.getComponentType());
		}
		return type;
	}

	public static Class<?> findClass(String className) {
		Throwable prob = null;
		ClassLoader loader = Thread.currentThread().getContextClassLoader();

		if (loader != null) {
			try {
				return Class.forName(className, true, loader);
			} catch (Exception e) {
				prob = e;
			}
		}
		try {
			return Class.forName(className);
		} catch (Exception e) {
			prob = e;
		}

		throw new TangYuanException(prob);
	}

	/**
	 * 是否是JavaBean类型, 不一定准确, 以后可以在每个Bean上增加注解, 如果有的话,可以简单使用
	 */
	public static boolean isBeanType(Object object) {
		Class<?> clazz = object.getClass();
		if (clazz.getName().startsWith("java.")) {
			return false;
		}
		if (clazz.getName().startsWith("javax.")) {
			return false;
		}
		if (clazz.getName().startsWith("sun.")) {
			return false;
		}
		if (clazz.isArray()) {
			return false;
		}
		if (clazz.isPrimitive()) {
			return false;
		}
		return true;
	}

	// public static Object createByteCode(ClassWriter classWriter,
	// String className) throws Exception {
	// byte[] classCode = classWriter.toByteArray();
	//
	// // logger
	// //writeClass(classCode, className);
	// //System.out.println("createByteCode:" + className);
	//
	// Class<?> exampleClass = classLoader.defineClassPublic(className,
	// classCode, 0, classCode.length);
	// Object instance = exampleClass.newInstance();
	// return instance;
	// }
	// public static void writeClass(byte[] code, String className) {
	// String classPath = "C:/Users/david/Desktop/aaadd/code/";
	// try {
	// FileOutputStream fos = new FileOutputStream(classPath + className
	// + ".class");
	// fos.write(code);
	// fos.close();
	// } catch (FileNotFoundException e) {
	// e.printStackTrace();
	// } catch (IOException e) {
	// e.printStackTrace();
	// }
	// }

}