package com.pictc.utils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.pictc.exceptions.TFieldException;
import com.pictc.exceptions.TMethodException;



/**
 * @author 张福财
 * @date 2023年8月11日
 * @Description: java对象反射工具类
 */
@SuppressWarnings({"unchecked"})
public class BeanUtils {


	private BeanUtils() {
		super();
	}
    
    
    /**
     * @Description: 获取java类的属性map集合
     * @param @param cls
     * @param @return 参数
     * @return Map<String,Field> 返回类型
     * @throws
     */
    public static Map<String, Field> getMapFields(Class<?> cls) {
        Map<String, Field> fields = CollectionUtils.newHashMap();
        if (cls != null) {
            List<Field> fieldList = new ArrayList<Field>();
            Set<String> names = new HashSet<String>();
            getFields(cls, fieldList,names);
            for (Field field : fieldList) {
                if (hasGetSet(field, cls)) {
                    fields.put(field.getName(), field);
                }
            }
        }
        return fields;
    }
    
    /**
     * @Description: 获取对象非空值的Map集合
     * @param @param obj
     * @param @return 参数
     * @return Map<String,Object> 返回类型
     * @throws
     */
    public static Map<String,Object> getBeanNotNullValue(Object obj){
    	Map<String,Object> rel=CollectionUtils.newHashMap();
    	if(obj==null) {
    		return rel;
    	}
    	Field[] fields= getFields(obj.getClass());
    	for (int i = 0; i < fields.length; i++) {
			Object val=getFieldValue(fields[i], obj);
			if(val!=null) {
				rel.put(fields[i].getName(),val);
			}
		}
    	return rel;
    }


    /**
     * @Description: 获取Class 包含指定注解类型的属性
     * @param @param cls
     * @param @param annotation
     * @param @return 参数
     * @return List<Field> 返回类型
     * @throws
     */
    public static List<Field> getFields(Class<?> cls, Class<? extends Annotation> annotation) {
        List<Field> result = CollectionUtils.newArrayList();
        Field[] fields = getFields(cls);
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            if (field.isAnnotationPresent(annotation)) {
                result.add(field);
            }
        }
        return result;
    }


    /**
     * @Description: 获取Class 属性数组
     * @param @param cls
     * @param @return 参数
     * @return Field[] 返回类型
     * @throws
     */
    public static Field[] getFields(Class<?> cls) {
        List<Field> fields = new ArrayList<Field>();
        Set<String> names = new HashSet<String>();
        getFields(cls, fields,names);
        Field[] result = new Field[fields.size()];
        fields.toArray(result);
        return result;
    }


    private static void getFields(Class<?> cls, List<Field> fields,Set<String> names) {
        if (!cls.equals(Object.class)) {
            Field[] _fields = cls.getDeclaredFields();
            for (int i = 0; i < _fields.length; i++) {
            	Field field = _fields[i];
            	if(names.contains(field.getName())) {
            		continue;
            	}
                fields.add(field);
                names.add(field.getName());
            }
            getFields(cls.getSuperclass(), fields,names);
        }
    }


    /**
     * @Description: 判断class是否有指定属性名的getter、setter方法
     * @param fieldName
     * @param cls
     * @return boolean 返回类型
     */
    public static boolean hasGetSet(String fieldName, Class<?> cls) {
        try {
            return hasGetSet(cls.getDeclaredField(fieldName), cls);
        } catch (Exception e) {
            throw new RuntimeException("Class【"+cls+"】-属性【"+fieldName+"】获取getter、setter方法异常",e);
        }
    }


    public static boolean hasGetSet(Field field, Class<?> cls) {
        Class<?> type = field.getType();
        String name = field.getName();
        String getName = null;
        String uname = getMethodName(name);
        if (type.equals(boolean.class) || type.equals(Boolean.class)) {
            if (name.startsWith("is")) {
                getName = name;
            } else {
                getName = "is" + uname;
            }
        } else {
            getName = "get" + uname;
        }
        String setName = "set" + uname;
        Class<?> beanType = cls;
        do {
            if (findGetSet(beanType, getName, setName, type)) {
                return true;
            }
            beanType = beanType.getSuperclass();
        } while (!Object.class.equals(beanType));

        return false;
    }


    private static boolean findGetSet(Class<?> beanType, String getName, String setName, Class<?> type) {
        Method getMethod = null;
        Method setMethod = null;
        try {
            getMethod = beanType.getDeclaredMethod(getName);
            setMethod = beanType.getDeclaredMethod(setName, type);
        } catch (Exception e) {
        }
        return getMethod != null && setMethod != null;
    }


    private static String getMethodName(String name) {
        return name.substring(0, 1).toUpperCase() + name.substring(1);
    }


    public static void setFieldValue(Field field, Object value, Object bean) {
        try {
        	if(field==null) {
        		throw new TFieldException("setFieldValue时field不能为空");
        	}
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            field.set(bean, value);
        } catch (Exception e) {
            throw new TFieldException("setFieldValue异常", e);
            
        }
    }


    public static void setFieldValue(String fieldName, Object value, Object bean) {
        setFieldValue(getField(fieldName, bean), value, bean);
    }


    public static <T> T getFieldValue(String fieldName, Object bean) {
        return getFieldValue(getField(fieldName, bean), bean);
    }


   
    public static <T> T getFieldValue(Field field, Object bean) {
        Object value = null;
    	if(field==null) {
    		throw new TFieldException("获取属性值时field不能为空");
    	}
        if (!field.isAccessible()) {
            field.setAccessible(true);
        }
        try {
            value = field.get(bean);
        } catch (Exception e) {
            throw new TFieldException("getFieldValue异常", e);
        }
        return value==null?null:(T)value;
    }

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

    public static Field getField(String fieldName, Class<?> cls) {
        try {
            return cls.getDeclaredField(fieldName);
        } catch (Exception e) {
        	throw new TFieldException("获取属性异常", e);
        }
    }
    
    public static <T> T newInstance(Class<?> cls) {
		return newInstance(cls,false);
    }
    
    public static <T> T newInstance(ClassLoader loader,Class<?> cls) {
    	return newInstance(loader,cls,false);
    }

    public static <T> T newInstance(Class<?> cls,boolean ignoreError) {
		return newInstance(null,cls,ignoreError);
    }
    
    public static <T> T newInstance(ClassLoader loader,Class<?> cls,boolean ignoreError) {
    	try {
    		return (T) ClassUtils.loadClass(loader,cls.getName()).getDeclaredConstructor().newInstance();
    	} catch (Exception e) {
    		if(!ignoreError) {
    			throw new RuntimeException("实例化：Class【"+cls.getName()+"】异常",e);
    		}
    	}
    	return null;
    }
    
    public static <T> T newInstance(String className) {
		return newInstance(className, false);
    }

    public static <T> T newInstance(String className,boolean ignoreError) {
        try {
            return (T) ClassUtils.loadClass(className).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
        	if(!ignoreError) {
        		throw new RuntimeException("实例化：Class【"+className+"】异常",e);
        	}
        }
        return null;
    }
    
	public static <T>T deepCopy(Object source){
    	if(source!=null) {
    		Map<String,Object> cache = CollectionUtils.newHashMap();
    		try {
    			return (T) deepCopy(source.getClass(),source,cache);
			} finally {
				cache.clear();
				cache = null;
			}
    	}
		return null;
    }
    
    public static <T>T deepCopy(Type javaType,Object source){
    	if(source!=null) {
    		Map<String,Object> cache = CollectionUtils.newHashMap();
    		try {
    			return (T) deepCopy(javaType,source,cache);
			} finally {
				cache.clear();
				cache = null;
			}
    		
    	}
    	return null;
    }
    
	private static Object deepCopy(Type javaType,Object source,Map<String,Object> cache){
    	if(source==null) {
    		return null;
    	}
    	String addr = Integer.toHexString(source.hashCode());
    	if(cache.containsKey(addr)) {
    		return cache.get(addr);
    	}
    	
    	TypeInfo type = new TypeInfo(javaType);
    	Object target = null;
    	if(type.isPrimitive() || type.isEnum()) {
    		target = source;
		}else if(type.isArray()) {
			int length = Array.getLength(source);
			List<Object> list = new ArrayList<Object>(length);
            for (int j = 0; j < length; j += 1) {
            	Object obj = Array.get(source, j);
            	list.add(deepCopy(type.getArrayType(),obj));
            }
            target = list.toArray();
		}else if(type.isCollection()) {
			Collection<Object> cvals = (Collection<Object>) source;
			Collection<Object>  nvals = type.newCollection();
			for (Object obj : cvals) {
				nvals.add(deepCopy(type.getCollectionType(),obj));
			}
			target = nvals;
		}else if(type.isMap()) {
			Map<Object, Object> mapVal = type.newMap();
			Map<?, ?> valMap = (Map<?, ?>) source;
			for (final Entry<?, ?> e : valMap.entrySet()) {
                final Object value = e.getValue();
                if (value != null) {
                	mapVal.put(e.getKey(),deepCopy(value));
                }
            }
			target = mapVal;
		}else {
			Class<?> klass = type.getKlass();
			target = newInstance(klass);
			cache.put(addr,target);
	    	Field[] fields = getFields(klass);
	    	Class<?> sklazz = source.getClass();
	    	Map<String, Field> mapFields = getMapFields(sklazz);
	    	for (int i = 0; i < fields.length; i++) {
	    		Field field = fields[i];
	    		Field sf = mapFields.get(field.getName());
	    		if(sf==null || !sf.getType().equals(field.getType())) {
	    			continue;
	    		}
	    		Object val = getFieldValue(sf, source);
	    		if(val!=null) {
	    			setFieldValue(field,deepCopy(field.getGenericType(),val, cache),target);
	    		}
	    	}
		}
		return target;
    }

    /**
     * 调用方法
     * */
    public static <T>T callMethod(Method method, Object obj, Object... values) throws InstantiationException,
            IllegalAccessException, NumberFormatException, IllegalArgumentException,
            InvocationTargetException {
		return (T) method.invoke(obj, values);
    }


    /**
     * 判断类是否含义该属性
     * 
     * @param klass
     * @param field
     * @return
     */
    public static boolean hasField(Class<?> klass, String field) {
        Class<?> btype = klass;
        while (!Object.class.equals(btype)) {
            try {
                btype.getDeclaredField(field);
                return true;
            } catch (Exception e) {
            }
            btype = btype.getSuperclass();
        }
        return false;
    }
    
    public static Method getMethod(Class<?> cls, String name, Class<?>... agrs) {
        Method method = null;
        try {
            if (agrs == null || agrs.length == 0) {
                method = cls.getDeclaredMethod(name);
            } else {
                method = cls.getDeclaredMethod(name, agrs);
            }
        } catch (NoSuchMethodException | SecurityException e) {
            throw new TMethodException(e);
        }
        return method;
    }
    
    public static Method getHasMethod(Class<?> cls, String name, Class<?>... agrs) {
        Method method = null;
        try {
            if (agrs == null || agrs.length == 0) {
                method = cls.getDeclaredMethod(name);
            } else {
                method = cls.getDeclaredMethod(name, agrs);
            }
        } catch (Exception e) {}
		return method;
    }
    
    public static <T>T copyBean(Object source,T target){
    	if(source!=null) {
    		Field[] ffields = getFields(target.getClass());
    		Map<String,Field> tarNames = CollectionUtils.newHashMap();
    		for (int i = 0; i < ffields.length; i++) {
    			tarNames.put(ffields[i].getName(),ffields[i]);
			}
    		Field[] fields = getFields(source.getClass());
    		for (Field field : fields) {
    			if(tarNames.containsKey(field.getName())) {
    				setFieldValue(tarNames.get(field.getName()),getFieldValue(field,source),target);
    			}
			}
    	}
		return target;
    }

}
