/**
 * Copyright 2016 benjobs
 * <p>
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.opencron.common.utils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileNotFoundException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 *
 * @ClassName: ReflectUitls
 * @author <a href="mailto:[email protected]">Wanghuajie</a>
 * @date:2012-12-5 下午2:59:49
 * @version V1.0
 * 
 */

@SuppressWarnings({ "rawtypes", "unchecked" })
public abstract class ReflectUitls {

    //获取泛型上的具体类型(第一个)
    public static Class<?> getGenericType(Class<?> clazz) {
        return getGenericType(clazz, 0);
    }

    //获取泛型上的具体类型(指定哪个类型)
    public static Class<?> getGenericType(Class<?> clazz, int i) {
        Type type = clazz.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) type).getActualTypeArguments();
            return (Class<?>) types[i];
        }
        return null;
    }


    /**
	 * 
	 * java反射bean的get方法
	 * 
	 * @param clazz
	 *            javaBean对象类型
	 * @param fieldName
	 *            字段名称
	 * 
	 * @return get方法
	 */
	public static Method getter(Class<?> clazz, String fieldName) throws NoSuchMethodException {
		// get+字段名第一个字母小写,得到get方法名

        // 拿到拷贝源上的属性器数组
        try {
            PropertyDescriptor[] objPds = Introspector.getBeanInfo(clazz).getPropertyDescriptors();

            for (int i = 0; objPds.length > 1 && i < objPds.length; i++) {
                //跳出从object继承的class属性,源上必须有get方法
                if (Class.class == objPds[i].getPropertyType()
                        || objPds[i].getReadMethod() == null) {
                    continue;
                }

                if (objPds[i].getName().equals(fieldName)){
                    return objPds[i].getReadMethod();
                }
            }

        } catch (IntrospectionException e) {
            throw new NoSuchMethodException(e.getMessage());
        }

        return null;

	}

	/**
	 * 
	 * java反射bean的set方法
	 * 
	 * @param clazz
	 *            javaBean对象
	 * @param fieldName
	 *            字段名称
	 * 
	 * @return set方法
	 */
	public static Method setter(Class<?> clazz, String fieldName) {
        AssertUtils.notNull(clazz,fieldName);
        try {
            PropertyDescriptor[] objPds = Introspector.getBeanInfo(clazz).getPropertyDescriptors();

            for (int i = 0; objPds.length > 1 && i < objPds.length; i++) {
                //跳出从object继承的class属性,源上必须有get方法
                if (Class.class == objPds[i].getPropertyType()
                        || objPds[i].getReadMethod() == null) {
                    continue;
                }

                if (objPds[i].getName().equals(fieldName)){
                    return objPds[i].getWriteMethod();
                }
            }

        } catch (IntrospectionException e) {
            throw new RuntimeException(e.getMessage());
        }

        return null;
	}

	/**
	 * 获得目标类中含有某注解的某些个Field
	 * 
	 * @Title: getFieldsByAnnotation
	 * @param objClass
	 *            目标类Class
	 * @param annotationClass
	 *            查询的注解
	 * @param index
	 *            查询多少个
	 * @return Field
	 * @author <a href="mailto:[email protected]">Wanghuajie</a> 2013-1-5下午3:35:07
	 */
    /**
     * 根据注解获取类上有该注解的全部字段
     * @param objClass
     * @param annotationClass
     * @return
     */

    private static List<Field> getFieldsByAnnotation(Class<?> objClass,Class<? extends Annotation> annotationClass) {
        AssertUtils.notNull(objClass,annotationClass);
        List<Field> fields = new ArrayList<Field>(0);
        while (!objClass.getSuperclass().equals(Object.class)){
            Collections.copy(fields, getDeclaredFieldByAnnotation(objClass, annotationClass));
            objClass = objClass.getSuperclass();
        }
        return fields;
    }

    private static List<Field> getDeclaredFieldByAnnotation(Class<?> objClass,Class<? extends Annotation> annotationClass) {
        AssertUtils.notNull(objClass,annotationClass);
        List<Field> fields = new ArrayList<Field>(0);
        for (Field field:objClass.getDeclaredFields()) {
            if (field.getAnnotation(annotationClass) != null) {
                field.setAccessible(true);
                fields.add(field);
            }
        }
        return fields;
    }

    public static List<Field> getAllFields(Class<?> objClass) {
        AssertUtils.notNull(objClass);
        List<Field> fields = new ArrayList<Field>(0);
        while (!objClass.getSuperclass().equals(Object.class)){
            Collections.copy(fields, Arrays.asList(objClass.getDeclaredFields()));
            objClass = objClass.getSuperclass();
        }
        return fields;
    }

    public static List<Method> getMethodsByAnnotation(Class<?> objClass,Class<? extends Annotation> annoClass) throws ClassNotFoundException {
        List<Method> methods = new ArrayList<Method>(0);
        for(Method method:objClass.getDeclaredMethods()){
            if (method.getAnnotation(annoClass)!=null){
                methods.add(method);
            }
        }
        return methods;
    }

    public static List<Class<?>> scanPackageClass(String packageName) throws Exception {
        List<Class<?>> classNames = new ArrayList<Class<?>>(0);

        String resourceName = packageName.replaceAll("\\.", "/");
        URL url = Thread.currentThread().getContextClassLoader().getResource(resourceName);
        if (url == null) {
            throw new FileNotFoundException();
        }
        String kindClass = ".class";
        if ("jar".equals(url.getProtocol())) {
            String jarPath = url.getPath().substring(0, url.getPath().indexOf("!")).replaceFirst("file:/", "").replaceAll("%20", " ");
            JarFile jarFile =  new JarFile(new File(jarPath));
            Enumeration<JarEntry> es = jarFile.entries();
            while (es.hasMoreElements()) {
                String name = null;
                for (int i = 0; i <= packageName.split("\\.").length; i++) {
                    if (es.hasMoreElements()) name = es.nextElement().getName();
                    else break;
                    if (name.contains(kindClass)) break;
                }
                if (name != null && name.endsWith(kindClass)) {
                    String className = name.replaceAll("/", ".").replace(kindClass,"");
                    classNames.add(Class.forName(className));
                }
            }
        } else {
            File urlFile = new File(url.toURI());
            for (File pkgFile : urlFile.listFiles()) {
                if (pkgFile.isFile()) {
                    String className = packageName + "." + pkgFile.getName().replace(kindClass,"");
                    classNames.add(Class.forName(className));
                }
            }
        }

        return classNames;
    }

    /**
     *
     * 执行get方法
     * @param o
     *            执行对象
     * @param fieldName
     *            属性
     * @return 该get方法的返回值
     */
    public static Object invokeGet(Object o, String fieldName) {
        try {
            Method method = getter(o.getClass(), fieldName);
            return method.invoke(o);
        } catch (Exception e) {
            throw new RuntimeException("invoke " + o.getClass().getName()+ "get Method Error,Detail:" + e.getMessage());
        }
    }

    public static boolean methodHasAnnotation(Method method, Class<? extends Annotation> annoClazz) {
        AssertUtils.notNull(method,annoClazz);
        return method.getAnnotation(annoClazz)!=null;
    }

    /**
     * 检查第一个(第二个)参数类型是否为第二个参数个(第一个)类型的包装类
     *
     * @param objType1
     * @param objType2
     * @return
     */
    public static boolean checkPackageType(Class<?> objType1, Class<?> objType2) {
        if (CommonUtils.isEmpty(objType1) || CommonUtils.isEmpty(objType2)
                || (objType1.isPrimitive() && objType2.isPrimitive())
                || (!objType1.isPrimitive() && !objType2.isPrimitive())
                ) {
            return Boolean.FALSE;
        }

        try {
            if (objType1.isPrimitive())
                return objType2.getField("TYPE").get(null) == objType1;
            return objType1.getField("TYPE").get(null) == objType2;
        } catch (Exception e) {
        }
        return Boolean.FALSE;
    }

    //是否为数字或集合
    public static boolean isArrayOrSet(Object object) {
        if (object instanceof Class){
            return ((Class)object).isArray() || Collection.class.isAssignableFrom((Class)object);
        }
        return object.getClass().isArray() || Collection.class.isAssignableFrom(object.getClass());
    }

    //判断类型是否为java 8大基本类型的包装类
    public static boolean isPrimitivePackageType(Class clazz) {
        if (CommonUtils.isEmpty(clazz) || clazz.isPrimitive()) {
            return Boolean.FALSE;
        }
        return Java8Type.getIndexByObjectTypeName(clazz.getName()) > 0;
    }

    enum Java8Type {
        BYTE(1, "byte", "java.lang.Byte"),
        INT(2, "int", "java.lang.Integer"),
        SHORT(3, "short", "java.lang.Short"),
        LONG(4, "long", "java.lang.Long"),
        FLOAT(5, "float", "java.lang.Float"),
        DOUBLE(6, "double", "java.lang.Double"),
        BOOLEAN(7, "boolean", "java.lang.Boolean"),
        CHAR(8, "char", "java.lang.Character");

        private int index;
        private String primitiveTypeName;
        private String objectTypeName;

        Java8Type(int index, String primitiveTypeName, String objectTypeName) {
            this.index = index;
            this.primitiveTypeName = primitiveTypeName;
            this.objectTypeName = objectTypeName;
        }

        public static int getIndexByPrimitiveTypeName(String primitiveTypeName) {
            for (Java8Type javaLang8Type : Java8Type.values()) {
                if (javaLang8Type.getPrimitiveTypeName().equals(primitiveTypeName)) {
                    return javaLang8Type.getIndex();
                }
            }
            return -1;
        }

        public static int getIndexByObjectTypeName(String objectTypeName) {
            for (Java8Type jva8Type : Java8Type.values()) {
                if (jva8Type.getObjectTypeName().equals(objectTypeName)) {
                    return jva8Type.getIndex();
                }
            }
            return 0;
        }

        public int getIndex() {
            return index;
        }

        public void setIndex(int index) {
            this.index = index;
        }

        public String getPrimitiveTypeName() {
            return primitiveTypeName;
        }

        public void setPrimitiveTypeName(String primitiveTypeName) {
            this.primitiveTypeName = primitiveTypeName;
        }

        public String getObjectTypeName() {
            return objectTypeName;
        }

        public void setObjectTypeName(String objectTypeName) {
            this.objectTypeName = objectTypeName;
        }

    }

}