/*
 * Copyright 2008-2009 the original author or authors.
 *
 * Licensed 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
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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 net.hasor.utils;
import net.hasor.utils.convert.ConverterUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 *
 * @version : 2011-6-3
 * @author 赵永春 (zyc@hasor.net)
 */
public class BeanUtils {
    /**获取指定类型的默认值。*/
    public static Object getDefaultValue(final Class<?> returnType) {
        if (returnType == null || !returnType.isPrimitive()) {
            return null;
        }
        //
        if (returnType == int.class) {
            return 0;
        } else if (returnType == byte.class) {
            return (byte) 0;
        } else if (returnType == char.class) {
            return '\0';
        } else if (returnType == double.class) {
            return 0d;
        } else if (returnType == float.class) {
            return 0f;
        } else if (returnType == long.class) {
            return 0L;
        } else if (returnType == short.class) {
            return (short) 0;
        } else if (returnType == boolean.class) {
            return false;
        } else if (returnType == void.class) {
            return null;
        } else if (returnType.isArray()) {
            return null;
        }
        return null;
    }

    public static Object[] getDefaultValue(Class<?>[] paramArray) {
        if (paramArray == null) {
            return null;
        }
        Object[] objs = new Object[paramArray.length];
        for (int i = 0; i < paramArray.length; i++) {
            objs[i] = getDefaultValue(paramArray[i]);
        }
        return objs;
    }

    /**
     * 该方法的作用是反射的形式调用目标的方法。
     * @param target 被调用的对象
     * @param methodName 要调用的反射方法名。
     * @param objects 参数列表
     */
    public static Object invokeMethod(final Object target, final String methodName, final Object... objects) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
        if (target == null) {
            return null;
        }
        Class<?> targetType = target.getClass();
        Method invokeMethod = null;
        //反射调用方法
        Method[] ms = targetType.getMethods();
        for (Method m : ms) {
            //1.名字不相等的忽略
            if (!m.getName().equals(methodName)) {
                continue;
            }
            //2.目标方法参数列表个数与types字段中存放的个数不一样的忽略。
            Class<?>[] paramTypes = m.getParameterTypes();
            if (paramTypes.length != objects.length) {
                continue;
            }
            //3.如果有参数类型不一样的也忽略---1
            boolean isFind = true;
            for (int i = 0; i < paramTypes.length; i++) {
                Object param_object = objects[i];
                if (param_object == null) {
                    continue;
                }
                //
                if (!paramTypes[i].isAssignableFrom(param_object.getClass())) {
                    isFind = false;
                    break;
                }
            }
            //5.如果有参数类型不一样的也忽略---2
            if (!isFind) {
                continue;
            }
            //符合条件执行调用
            invokeMethod = m;
        }
        if (invokeMethod == null) {
            throw new NullPointerException(methodName + " invokeMethod is null.");
        } else {
            return invokeMethod.invoke(target, objects);
        }
    }
    /*----------------------------------------------------------------------------------------*/

    /**获取类定义的字段和继承父类中定义的字段以及父类的父类（子类重新定义同名字段也会被列入集合）。*/
    public static List<Field> findALLFields(final Class<?> target) {
        if (target == null) {
            return null;
        }
        ArrayList<Field> fList = new ArrayList<>();
        BeanUtils.findALLFields(target, fList);
        return fList;
    }

    private static void findALLFields(final Class<?> target, final ArrayList<Field> fList) {
        if (target == null) {
            return;
        }
        for (Field field : target.getDeclaredFields()) {
            if (!fList.contains(field)) {
                fList.add(field);
            }
        }
        for (Field field : target.getFields()) {
            if (!fList.contains(field)) {
                fList.add(field);
            }
        }
        Class<?> superType = target.getSuperclass();
        if (superType == null || superType == target) {
            return;
        }
        BeanUtils.findALLFields(superType, fList);
    }

    /**获取类定义的方法和继承父类中定义的方法以及父类的父类（子类的重写方法也会被返回）。*/
    public static List<Method> findALLMethods(final Class<?> target) {
        if (target == null) {
            return null;
        }
        ArrayList<Method> mList = new ArrayList<>();
        BeanUtils.findALLMethods(target, mList);
        return mList;
    }

    private static void findALLMethods(final Class<?> target, final ArrayList<Method> mList) {
        if (target == null) {
            return;
        }
        for (Method method : target.getDeclaredMethods()) {
            if (!mList.contains(method)) {
                mList.add(method);
            }
        }
        for (Method method : target.getMethods()) {
            if (!mList.contains(method)) {
                mList.add(method);
            }
        }
        Class<?> superType = target.getSuperclass();
        if (superType == null || superType == target) {
            return;
        }
        BeanUtils.findALLMethods(superType, mList);
    }
    /*----------------------------------------------------------------------------------------*/

    /**查找一个可操作的字段列表。*/
    public static List<Field> getFields(final Class<?> type) {
        return Arrays.asList(type.getFields());
    }

    /**查找一个可操作的方法列表。*/
    public static List<Method> getMethods(final Class<?> type) {
        return Arrays.asList(type.getMethods());
    }

    /**查找一个可操作的字段。*/
    public static Field getField(final String fieldName, final Class<?> type) {
        if (fieldName == null || type == null) {
            return null;
        }
        for (Field f : type.getFields()) {
            if (f.getName().equals(fieldName)) {
                return f;
            }
        }
        for (Field f : type.getDeclaredFields()) {
            if (f.getName().equals(fieldName)) {
                return f;
            }
        }
        return null;
    }

    /**查找一个可操作的方法。*/
    public static Method getMethod(final Class<?> atClass, final String name, final Class<?>[] paramType) {
        try {
            return atClass.getMethod(name, paramType);
        } catch (Exception e) {
            try {
                return atClass.getDeclaredMethod(name, paramType);
            } catch (Exception e1) {
                return null;
            }
        }
    }

    /**获取属性名集合，该方法是{@link #getPropertys(Class)}方法的升级版，通过该方法还可以同时返回可访问的字段作为属性。*/
    public static List<String> getPropertysAndFields(final Class<?> target) {
        List<String> mnames = BeanUtils.getPropertys(target);
        List<Field> fnames = BeanUtils.getFields(target);
        for (Field f : fnames) {
            String fName = f.getName();
            if (!mnames.contains(fName)) {
                mnames.add(fName);
            }
        }
        return mnames;
    }

    /**获取属性名集合，被包含的属性可能有些只是只读属性，有些是只写属性。也有读写属性。*/
    public static List<String> getPropertys(final Class<?> target) {
        List<String> mnames = new ArrayList<>();
        List<Method> ms = BeanUtils.getMethods(target);
        for (Method m : ms) {
            String name = m.getName();
            if (name.startsWith("get") || name.startsWith("set")) {
                name = name.substring(3);
            } else if (name.startsWith("is")) {
                name = name.substring(2);
            } else {
                continue;
            }
            if (!name.equals("")) {
                name = StringUtils.firstCharToLowerCase(name);
                if (!mnames.contains(name)) {
                    mnames.add(name);
                }
            }
        }
        return mnames;
    }

    /**获取属性名集合，被包含的属性可能有些只是只读属性，有些是只写属性。也有读写属性。*/
    public static PropertyDescriptor[] getPropertyDescriptors(final Class<?> defineType) {
        List<PropertyDescriptor> mnames = new ArrayList<>();
        List<String> ms = BeanUtils.getPropertys(defineType);
        for (String m : ms) {
            try {
                mnames.add(new PropertyDescriptor(m, defineType));
            } catch (Exception e) {
            }
        }
        return mnames.toArray(new PropertyDescriptor[0]);
    }

    /**获取一个属性的读取方法。*/
    public static Method getReadMethod(final String property, final Class<?> target) {
        if (property == null || target == null) {
            return null;
        }
        String methodName_1 = "get" + StringUtils.firstCharToUpperCase(property);
        String methodName_2 = "is" + StringUtils.firstCharToUpperCase(property);
        //
        for (Method m : target.getMethods()) {
            if (m.getParameterTypes().length == 0) {
                String methodName = m.getName();
                if (methodName.equals(methodName_1)) {
                    return m;
                }
                /*是否是布尔*/
                if (methodName.equals(methodName_2)) {
                    Class<?> t = m.getReturnType();
                    if (t == Boolean.class || t == boolean.class) {
                        return m;
                    }
                }
            }
        }
        return null;
    }

    /**获取一个属性的写入方法。*/
    public static Method getWriteMethod(final String property, final Class<?> target) {
        if (property == null || target == null) {
            return null;
        }
        String methodName = "set" + StringUtils.firstCharToUpperCase(property);
        for (Method m : target.getMethods()) {
            if (m.getName().equals(methodName)) {
                if (m.getParameterTypes().length == 1) {
                    return m;
                }
            }
        }
        return null;
    }

    /**测试是否具有propertyName所表示的属性，无论是读或写方法只要存在一个就表示存在该属性。*/
    public static boolean hasProperty(final String propertyName, final Class<?> target) {
        //get、set方法
        if (BeanUtils.getReadMethod(propertyName, target) == null) {
            return BeanUtils.getWriteMethod(propertyName, target) != null;
        }
        return true;
    }

    /**测试是否具有fieldName所表示的字段，无论是读或写方法只要存在一个就表示存在该属性。*/
    public static boolean hasField(final String propertyName, final Class<?> target) {
        return BeanUtils.getField(propertyName, target) != null;
    }

    /**测试是否具有name所表示的属性，hasProperty或hasField有一个返回为true则返回true。*/
    public static boolean hasPropertyOrField(final String name, final Class<?> target) {
        if (!BeanUtils.hasProperty(name, target)) {
            return BeanUtils.hasField(name, target);
        }
        return true;
    }

    /**测试是否支持readProperty方法。返回true表示可以进行读取操作。*/
    public static boolean canReadProperty(final String propertyName, final Class<?> target) {
        Method readMethod = BeanUtils.getReadMethod(propertyName, target);
        return readMethod != null;
    }

    /**测试是否支持readPropertyOrField方法。*/
    public static boolean canReadPropertyOrField(final String propertyName, final Class<?> target) {
        if (!BeanUtils.canReadProperty(propertyName, target)) {
            return BeanUtils.hasField(propertyName, target);
        }
        return true;
    }

    /**测试是否支持writeProperty方法。返回true表示可以进行写入操作。*/
    public static boolean canWriteProperty(final String propertyName, final Class<?> target) {
        Method writeMethod = BeanUtils.getWriteMethod(propertyName, target);
        return writeMethod != null;
    }

    /**测试是否支持Field方法写。*/
    public static boolean canWriteField(final String propertyName, final Class<?> target) {
        Field field = getField(propertyName, target);
        return field != null && !Modifier.isFinal(field.getModifiers());
    }

    /**测试是否支持writePropertyOrField方法。*/
    public static boolean canWritePropertyOrField(final String propertyName, final Class<?> target) {
        if (!BeanUtils.canWriteProperty(propertyName, target)) {
            return BeanUtils.canWriteField(propertyName, target);
        }
        return true;
    }
    /*----------------------------------------------------------------------------------------*/

    /**执行属性注入，除了注入int,short,long,等基本类型之外该方法还支持注入枚举类型。返回值表示执行是否成功。注意：该方法会根据属性类型进行尝试类型转换。*/
    public static boolean writeProperty(final Object object, final String attName, final Object value) {
        if (object == null || attName == null) {
            return false;
        }
        //1.查找方法
        Class<?> defineType = object.getClass();
        Method writeMethod = BeanUtils.getWriteMethod(attName, defineType);
        if (writeMethod == null) {
            return false;
        }
        //2.执行属性转换
        Class<?> toType = writeMethod.getParameterTypes()[0];
        Object attValueObject = ConverterUtils.convert(toType, value);
        //3.执行属性注入
        try {
            writeMethod.invoke(object, attValueObject);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**执行字段注入，除了注入int,short,long,等基本类型之外该方法还支持注入枚举类型。注意：该方法会根据属性类型进行尝试类型转换。*/
    public static boolean writeField(final Object object, final String fieldName, final Object value) {
        if (object == null || fieldName == null) {
            return false;
        }
        //1.查找方法
        Class<?> defineType = object.getClass();
        Field writeField = BeanUtils.getField(fieldName, defineType);
        if (writeField == null) {
            return false;
        }
        //2.执行属性转换
        Class<?> toType = writeField.getType();
        Object attValueObject = ConverterUtils.convert(toType, value);
        //3.执行属性注入
        try {
            writeField.setAccessible(true);
            writeField.set(object, attValueObject);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**执行注入，该方法首先会试图执行属性方法注入。如果失败则执行字段注入。注意：该方法会根据属性类型进行尝试类型转换。*/
    public static boolean writePropertyOrField(final Object object, final String attName, final Object value) {
        Class<?> defineType = object.getClass();
        if (BeanUtils.canWriteProperty(attName, defineType)) {
            return BeanUtils.writeProperty(object, attName, value);//支持方法写入
        }
        if (BeanUtils.hasField(attName, defineType)) {
            return BeanUtils.writeField(object, attName, value);//支持字段写入
        }
        return false;
    }

    /**执行属性读取。*/
    public static Object readProperty(final Object object, final String attName) {
        if (object == null || attName == null) {
            return false;
        }
        //1.查找方法
        Class<?> defineType = object.getClass();
        Method readMethod = BeanUtils.getReadMethod(attName, defineType);
        if (readMethod == null) {
            return null;
        }
        //2.执行属性读取
        try {
            return readMethod.invoke(object);
        } catch (Exception e) {
            return null;
        }
    }

    /**执行字段读取。*/
    public static Object readField(final Object object, final String fieldName) {
        if (object == null || fieldName == null) {
            return null;
        }
        //1.查找方法
        Class<?> defineType = object.getClass();
        Field readField = BeanUtils.getField(fieldName, defineType);
        if (readField == null) {
            return null;
        }
        //2.执行字段读取
        try {
            readField.setAccessible(true);
            return readField.get(object);
        } catch (Exception e) {
            return null;
        }
    }

    /**执行注入，该方法首先会试图执行属性方法注入。如果失败则执行字段注入。注意：该方法会根据属性类型进行尝试类型转换。*/
    public static Object readPropertyOrField(final Object object, final String attName) {
        Class<?> defineType = object.getClass();
        if (BeanUtils.canReadProperty(attName, defineType)) {
            return BeanUtils.readProperty(object, attName);//支持方法读取
        }
        if (BeanUtils.hasField(attName, defineType)) {
            return BeanUtils.readField(object, attName);//支持字段读取
        }
        return null;
    }

    /***/
    public static Class<?> getPropertyType(final Class<?> defineType, final String attName) {
        try {
            PropertyDescriptor pd = new PropertyDescriptor(attName, defineType);
            return pd.getPropertyType();
        } catch (Exception e) {
            return null;
        }
    }

    /***/
    public static Class<?> getFieldType(final Class<?> defineType, final String attName) {
        Field readField = BeanUtils.getField(attName, defineType);
        if (readField != null) {
            return readField.getType();
        }
        return null;
    }

    /***/
    public static Class<?> getPropertyOrFieldType(final Class<?> defineType, final String attName) {
        Class<?> propType = null;
        //
        propType = BeanUtils.getPropertyType(defineType, attName);
        if (propType != null) {
            return propType;
        }
        propType = BeanUtils.getFieldType(defineType, attName);
        return propType;
    }

    /***/
    public static void copyProperties(final Object dest, final Object orig) {
        if (dest == null) {
            throw new IllegalArgumentException("dest is null");
        }
        if (orig == null) {
            throw new IllegalArgumentException("orig is null");
        }
        //
        List<String> propNames = new ArrayList<>();
        if (orig instanceof Map) {
            for (Object key : ((Map) orig).keySet()) {
                propNames.add(key.toString());
            }
        } else {
            propNames = BeanUtils.getPropertys(orig.getClass());
        }
        for (String prop : propNames) {
            BeanUtils.copyProperty(dest, orig, prop);
        }
    }

    /***/
    public static void copyProperty(final Object dest, final Object orig, final String propertyName) {
        if (dest == null) {
            throw new IllegalArgumentException("dest is null");
        }
        if (orig == null) {
            throw new IllegalArgumentException("orig is null");
        }
        if (StringUtils.isBlank(propertyName)) {
            throw new IllegalArgumentException("propertyName is null");
        }
        //
        if (!(orig instanceof Map)) {
            if (!BeanUtils.canReadPropertyOrField(propertyName, orig.getClass())) {
                return;
            }
        }
        if (!(dest instanceof Map)) {
            if (!BeanUtils.canWritePropertyOrField(propertyName, dest.getClass())) {
                return;
            }
        }
        //
        Object val = null;
        if (!(orig instanceof Map)) {
            val = BeanUtils.readPropertyOrField(orig, propertyName);
        } else {
            val = ((Map) orig).get(propertyName);
        }
        //
        if (!(dest instanceof Map)) {
            BeanUtils.writePropertyOrField(dest, propertyName, val);
        } else {
            ((Map) dest).put(propertyName, val);
        }
    }
}