package com.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.lang3.StringUtils;



/**
 * 反射相关方法
 * @author gexuejia
 *
 */
public class ClassUtils {
	/*
	 * 取得某一类所在包的所有类名 不含迭代
	 */
	public static String[] getPackageAllClassName(String classLocation, String packageName) {
		//将packageName分解
		String[] packagePathSplit = packageName.split("[.]");
//		String realClassLocation = classLocation + "\\src";
		String realClassLocation = "";
		int packageLength = packagePathSplit.length;
		for (int i = 0; i < packageLength; i++) {
			realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
		}
		File packeageDir = new File(realClassLocation);
		if (packeageDir.isDirectory()) {
			String[] allClassName = packeageDir.list();
			return allClassName;
		}
		return null;
	}

	public static List<String> getPackageAllClassNameByPathAndName(String classLocation, String packageName,boolean containSubDir) {
		//将packageName分解
		String[] packagePathSplit = packageName.split("[.]");
		List<String> classes = new ArrayList<String>();
//		String realClassLocation = classLocation + "\\src";
		String realClassLocation = "";
		int packageLength = packagePathSplit.length;
		for (int i = 0; i < packageLength; i++) {
			realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
		}
		File packeageDir = new File(classLocation+realClassLocation);
		if (packeageDir.isDirectory()) {
			String[] allClassName = packeageDir.list();
			for(String className : allClassName){
				if(className.endsWith(".class")){
					classes.add(packageName+"."+StringUtils.split(className, "[.]")[0]);
				}
				if(containSubDir){
					File fi = new File(classLocation+"\\"+className);
					if(fi.isDirectory()){
						classes.addAll(getPackageAllClassNameByPathAndName(classLocation+"\\"+className,packageName+"."+className,containSubDir));
					}
				}
			}
		}
		return classes;
	}



	/**
	 * 从包package中获取所有的Class
	 * @param pack
	 * @return
	 */
	public static Set<Class<?>> getClasses(Package pack) {

		//第一个class类的集合
		Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
		//是否循环迭代
		boolean recursive = false;
		//获取包的名字 并进行替换
		String packageName = pack.getName();
		String packageDirName = packageName.replace('.', '/');
		//定义一个枚举的集合 并进行循环来处理这个目录下的things
		Enumeration<URL> dirs;
		try {
			dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
			//循环迭代下去
			while (dirs.hasMoreElements()) {
				//获取下一个元素
				URL url = dirs.nextElement();
				//得到协议的名称
				String protocol = url.getProtocol();
				//如果是以文件的形式保存在服务器上
				if ("file".equals(protocol)) {
					//获取包的物理路径
					String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
					//以文件的方式扫描整个包下的文件 并添加到集合中
					findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
				}
//				else if ("jar".equals(protocol)) {
//					//如果是jar包文件
//					//定义一个JarFile
//					JarFile jar;
//					try {
//						//获取jar
//						jar = ((JarURLConnection) url.openConnection()).getJarFile();
//						//从此jar包 得到一个枚举类
//						Enumeration<JarEntry> entries = jar.entries();
//						//同样的进行循环迭代
//						while (entries.hasMoreElements()) {
//							//获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
//							JarEntry entry = entries.nextElement();
//							String name = entry.getName();
//							//如果是以/开头的
//							if (name.charAt(0) == '/') {
//								//获取后面的字符串
//								name = name.substring(1);
//							}
//							//如果前半部分和定义的包名相同
//							if (name.startsWith(packageDirName)) {
//								int idx = name.lastIndexOf('/');
//								//如果以"/"结尾 是一个包
//								if (idx != -1) {
//									//获取包名 把"/"替换成"."
//									packageName = name.substring(0, idx).replace('/', '.');
//								}
//								//如果可以迭代下去 并且是一个包
//								if ((idx != -1) || recursive) {
//									//如果是一个.class文件 而且不是目录
//									if (name.endsWith(".class") && !entry.isDirectory()) {
//										//去掉后面的".class" 获取真正的类名
//										String className = name.substring(packageName.length() + 1, name.length() - 6);
//										try {
//											//添加到classes
//											classes.add(Class.forName(packageName + '.' + className));
//										} catch (ClassNotFoundException e) {
//											e.printStackTrace();
//										}
//									}
//								}
//							}
//						}
//					} catch (IOException e) {
//						e.printStackTrace();
//					}
//				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return classes;
	}

	/**
	 * 以文件的形式来获取包下的所有Class
	 * @param packageName
	 * @param packagePath
	 * @param recursive
	 * @param classes
	 */
	public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) {
		//获取此包的目录 建立一个File
		File dir = new File(packagePath);
		//如果不存在或者 也不是目录就直接返回
		if (!dir.exists() || !dir.isDirectory()) {
			return;
		}
		//如果存在 就获取包下的所有文件 包括目录
		File[] dirfiles = dir.listFiles(new FileFilter() {
			//自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
			public boolean accept(File file) {
				return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
			}
		});
		//循环所有文件
		for (File file : dirfiles) {
			//如果是目录 则继续扫描
			if (file.isDirectory()) {
				findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
			} else {
				//如果是java类文件 去掉后面的.class 只留下类名
				String className = file.getName().substring(0, file.getName().length() - 6);
				try {
					//添加到集合中去
					classes.add(Class.forName(packageName + '.' + className));
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static boolean isBelongInterface(Class<?> srcClass, Class<?> dstInterface) {
		System.out.println(srcClass.getName()+":"+dstInterface.getName());
		for (Class<?> localClass : srcClass.getInterfaces()) {
			if (localClass.equals(dstInterface))
				return true;
			if (isBelongInterface(localClass, dstInterface))
				return true;
		}
		return (srcClass.getSuperclass() != null) && (isBelongInterface(srcClass.getSuperclass(), dstInterface));
	}

	/**
	 * 获取该类成员变量的值
	 * @param instance
	 * @param fieldName
	 * @return 字段值
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static String getValue(Object instance, String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException,
			IllegalAccessException {
		Field field = getField(instance.getClass(), fieldName);
		field.setAccessible(true);
		Object value = field.get(instance);

		return String.valueOf(value);

	}


	/**
	 * 获取该类及其父类的成员变量
	 * @param clazz
	 * @param fieldName
	 * @return
	 */
	private static Field getField(Class cl, String fieldName) {
		Field f = null;
		Class clazz = cl;
		while (clazz != null && f == null) {
			try {
				f = clazz.getDeclaredField(fieldName);
			} catch (Exception e) {
				clazz = clazz.getSuperclass();
			}
		}
		return f;
	}

	public static void setObjectValue(Class cl, Object o, String key, String value) throws Exception {
		Field f = null;
		Class clazz = cl;
		while (clazz != null && f == null) {
			try {
				f = clazz.getDeclaredField(key);
			} catch (Exception e) {
				clazz = clazz.getSuperclass();
			}
		}
		if (f == null) {
			throw new Exception("类[" + cl.getName() + "]没有属性[" + key + "]，请检查！");
		}

		Class t = f.getType();
		Method m = null;
		try {
			m = cl.getMethod("set" + Character.toUpperCase(key.charAt(0)) + key.substring(1), new Class[] { t });
		} catch (Exception e) {
			throw new Exception("类[" + cl.getName() + "]属性[" + key + "]的设置方法[" + ("set" + Character.toUpperCase(key.charAt(0)) + key.substring(1) + "(" + t.getName() + ")") + "]，请检查！");
		}
		Object v = null;
		if (t == Byte.class || t == Byte.TYPE) {
			v = Byte.parseByte(value);
		} else if (t == Short.class || t == Short.TYPE) {
			v = Short.parseShort(value);
		} else if (t == Integer.class || t == Integer.TYPE) {
			v = Integer.parseInt(value);
		} else if (t == Long.class || t == Long.TYPE) {
			v = Long.parseLong(value);
		} else if (t == Boolean.class || t == Boolean.TYPE) {
			v = Boolean.parseBoolean(value);
		} else if (t == String.class) {
			v = value;
		} else if (t.isArray()) {
			if (value.trim().length() == 0) {
				v = Array.newInstance(t, 0);
			} else {
				String ss[] = value.split(",");
				t = t.getComponentType();
				v = Array.newInstance(t, ss.length);
				for (int i = 0; i < ss.length; i++) {
					Object v2 = null;
					if (t == Byte.class || t == Byte.TYPE) {
						v2 = Byte.parseByte(ss[i]);
					} else if (t == Short.class || t == Short.TYPE) {
						v2 = Short.parseShort(ss[i]);
					} else if (t == Integer.class || t == Integer.TYPE) {
						v2 = Integer.parseInt(ss[i]);
					} else if (t == Long.class || t == Long.TYPE) {
						v2 = Long.parseLong(ss[i]);
					} else if (t == Boolean.class || t == Boolean.TYPE) {
						v2 = Boolean.parseBoolean(ss[i]);
					} else if (t == String.class) {
						v2 = ss[i];
					} else {
						throw new Exception("类[" + cl.getName() + "]属性[" + key + "]为复杂的数据类型【" + t.getName() + "的数组】，暂时不支持！");
					}
					Array.set(v, i, v2);
				}
			}
		} else {
			throw new Exception("类[" + cl.getName() + "]属性[" + key + "]为复杂的数据类型【" + t.getName() + "】，暂时不支持！");
		}
		m.invoke(o, new Object[] { v });
	}


	/**
	 * 对传入的对象进行深度克隆
	 * @param src
	 * @return object
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static Object deepClone(Object src) throws IOException, ClassNotFoundException {
		ByteArrayOutputStream betyArrayOutputStream = new ByteArrayOutputStream();
		ObjectOutputStream objectOutputStream = new ObjectOutputStream(betyArrayOutputStream);
		objectOutputStream.writeObject(src);
		ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(betyArrayOutputStream.toByteArray());
		ObjectInputStream objInputStream = new ObjectInputStream(byteArrayInputStream);
		return objInputStream.readObject();
	}

}
