/*
 * Copyright [2020] [MaxKey of copyright http://www.maxkey.top]
 * 
 * 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 org.maxkey.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.LogFactory;

public class BeanUtil {
	
	public static void copyBean(Object  origin,Object target) {
		if( origin == null || target == null) return;
		try {				
			BeanUtils.copyProperties( origin, target);
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}
		
	public static Object cloneSupper(Object origin) {			
		Object target = null;
		if(origin == null) return target;
		try {				
			target = origin.getClass().getSuperclass().newInstance();
			BeanUtils.copyProperties(target,origin);
		} catch (Exception e) {
			e.printStackTrace();
		}		
		return target;
	}
		
	public static String getValue(Object bean,String  field ) {
		if(bean == null) return null;
		String retVal = "";
		try {
			retVal = BeanUtils.getProperty(bean, field);
		} catch(Exception e) {
			e.printStackTrace();
		}
		return retVal;
	}
	
	@SuppressWarnings("rawtypes")
	public static boolean isNotNull(Collection collection) {
		if(collection != null && collection.size() > 0) {
			return true;
		}
		return false;
	}
	
	@SuppressWarnings("rawtypes")
	public static boolean isNotNull(Map map) {
		if(map != null && map.size() > 0) {
			return true;
		}
		return false;
	}
	
	public static Object get(Object bean, String fieldName) {
		try {
			return invokeMethod(bean,getMethodByProperty("get",fieldName));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static Object set(Object bean, String fieldName,Object value) {
		try {
			return invokeMethod(bean,getMethodByProperty("set",fieldName),new Object[]{value});
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static Object getPublicProperty(Object bean, String fieldName) {
		try {
			Field field = bean.getClass().getField(fieldName);
			return field.get(bean);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static boolean setPublicProperty(Object bean, String fieldName,Object value) {
		try {
			Field field = bean.getClass().getField(fieldName);
			 field.set(bean,value);
			 return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	public static Boolean isPublicProperty(Class<? extends Object> cls, String fieldName) {
		if (isSetProperty(cls, fieldName)&& isGetProperty(cls, fieldName)) {
			return true;
		} else {
			return false;
		}
	}

	public static Boolean isPublicProperty(Object bean, String fieldName) {
		if (isSetProperty(bean.getClass(), fieldName)
				&& isGetProperty(bean.getClass(), fieldName)) {
			return true;
		} else {
			return false;
		}
	}

	public static <T> Boolean isSetProperty(Class<T> cls, String fieldName) {
		Method[] method = cls.getDeclaredMethods();
		for (int i = 0; i < method.length; i++) {
			if (method[i].getModifiers() == Modifier.PUBLIC
					&& method[i].getName().equals(getMethodByProperty("set",fieldName))) {
				return true;
			}
		}
		return false;
	}

	public static <T> Boolean isGetProperty(Class<T> cls, String fieldName) {
		Method[] method = cls.getDeclaredMethods();
		for (int i = 0; i < method.length; i++) {
			if (method[i].getModifiers() == Modifier.PUBLIC
					&& method[i].getName().equals(getMethodByProperty("get",fieldName))) {
				return true;
			}
		}
		return false;
	}

	public static Object newInstance(String className) {
		return Instance.newInstance(className);
	}
	
	public static <T> Object newInstance(Class<T> cls) {
		return Instance.newInstance(cls);
	}
	
	public static Object newInstance(String className, Object[] args) {
		return Instance.newInstance(className, args);
	}
	
	public static <T> T newInstance(Class<T> cls, Object[] args) {
		return (T) Instance.newInstance(cls, args);
	}
	public static Object invokeMethod(Object bean, String methodName, Object[] args)
	throws Exception {
		return MethodInvoke.invokeMethod(bean, methodName, args);
	}
	
	public static Object invokeMethod(Object bean, String methodName)
	throws Exception {
		return MethodInvoke.invokeMethod(bean, methodName);
	}

	public static Object invokeStaticMethod(Class<?> beanClass, String methodName,
			Object[] args) throws Exception {
		return MethodInvoke.invokeMethod(beanClass, methodName, args);
	}
	
	public static Object invokeStaticMethod(Class<?> beanClass, String methodName) throws Exception {
		return MethodInvoke.invokeStaticMethod(beanClass, methodName);
	}
	
	public static Map<?, ?> toMap(Object bean){
		return BeanConvert.bean2Map(bean);
	}
	
	public static Object fillBean(Object bean,HashMap<?, ?> valueMap){
		return BeanConvert.map2Bean(bean, valueMap);
	}
	public static Map<String, String> getFields(Class<? extends Object> cls) {
		Field[] flds = cls.getDeclaredFields();
		Map<String, String> map = new HashMap<String, String>();
		for (int i = 0; i < flds.length; i++) {
			String name = flds[i].getName();
			map.put(flds[i].getName(), flds[i].getType().getName());
		}
		return map;
	}

	public static Map<String, String> getFields(Object bean)  {
		return getFields(bean.getClass());
	}

	public static Map<String, String> getPropertyFields(Class<? extends Object> cls){
		Field[] flds = cls.getDeclaredFields();
		Map<String, String> map = new HashMap<String, String>();
		for (int i = 0; i < flds.length; i++) {
			String fieldName = flds[i].getName();
			if (isPublicProperty(cls, fieldName))
				map.put(flds[i].getName(), flds[i].getType().getName());
		}
		return map;
	}

	public static Map<String, String> getPropertyFields(Object bean) {
		return getPropertyFields(bean.getClass());
	}
	
	public static <T> boolean isEmpty(T entity,Field field){
		return ! isNotEmpty(entity,field);
	}
	
	public static <T> boolean isNotEmpty(T entity,Field field){
		boolean isFieldNotEmpty=true;
		String fieldType=field.getType().getName();
		Object value=null;
		String fillValue=null;
		try {
			if(BeanUtil.get(entity, field.getName())==null){
				return false;
			}else{
				fillValue = BeanUtil.get(entity, field.getName()).toString();
			}
		} catch (IllegalArgumentException e1) {
			e1.printStackTrace();
		} 
		if(fieldType.equals("java.lang.String")){
			if(String.valueOf(fillValue)==null)isFieldNotEmpty= false;
        }else if(fieldType.equals("int")){
        	if(Integer.parseInt(fillValue)==0)isFieldNotEmpty= false;
        }else if(fieldType.equals("long")){
        	if(Long.parseLong(fillValue)==0)isFieldNotEmpty= false;
        }else if(fieldType.equals("java.lang.Long")){
        	if(Long.parseLong(fillValue)==0)isFieldNotEmpty= false;
        }else if(fieldType.equals("double")){
        	if(Double.valueOf(fillValue)==0.0d)isFieldNotEmpty= false;
        }else if(fieldType.equals("float")){
        	if(Float.parseFloat(fillValue)==0.0f)isFieldNotEmpty= false;
        }else if(fieldType.equals("java.util.Date")){ 
        	try {
        		value=BeanUtil.get(entity, field.getName());
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} 
			if(value==null)isFieldNotEmpty= false;
        }else if(fieldType.equals("java.lang.Object")){
        	try {
				value=BeanUtil.get(entity, field.getName());
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} 
			if(value==null)isFieldNotEmpty= false;
        }else if(fieldType.equals("char")){
        	if(Character.valueOf(fillValue.charAt(0))=='\u0000')isFieldNotEmpty= false;
        }else if(fieldType.equals("boolean")){
        	value=Boolean.parseBoolean(fillValue);
        }else if(fieldType.equals("short")){
        	if(Short.parseShort(fillValue)==0)isFieldNotEmpty= false;
        }else if(fieldType.equals("byte")){
        	if(Byte.parseByte(fillValue)==0)isFieldNotEmpty= false;
        }
		
		LogFactory.getLog(BeanUtil.class).debug("isFieldNotEmpty() fieldName : "+field.getName()+", fieldType : "+fieldType+", Value : "+fillValue+", isFieldNotEmpty : "+isFieldNotEmpty);
		
		return isFieldNotEmpty;
	}
	
	public static void displayValues(Object bean) {
		Field[] flds = bean.getClass().getDeclaredFields();
		LogFactory.getLog(BeanUtil.class).debug("displayValues() *******************************************");
		LogFactory.getLog(BeanUtil.class).debug("displayValues() "+bean.getClass().getName());
		for (int i = 0; i < flds.length; i++) {
			String name = flds[i].getName();
			if(isGetProperty(bean.getClass(),name)){
				LogFactory.getLog(BeanUtil.class).debug("displayValues() Field "+(i+1)+" : "+name+" = "+BeanUtil.get(bean, name));
			}
		}
		
		LogFactory.getLog(BeanUtils.class).debug("displayValues() *******************************************");
		
	}
	
	public static <T> void beanClone(T target,T origin){
		Field[] flds = target.getClass().getDeclaredFields();
		for (int i = 0; i < flds.length; i++) {
			String name = flds[i].getName();
			if(isPublicProperty(origin,name)){
				if(get(origin,name)!=null){
					set(target,name,get(origin,name));
				}
			}
		}
	}
	
	public static Class[] getMethodParameterTypes(Class<?> c,String methodName){
		Method []methods=c.getMethods();
		for (Method method : methods) {
		  Class[] parameterTypes = method.getParameterTypes();
		  if(method.getName().equals(methodName)){
			  return parameterTypes;
		  }
		}
		return null;
	}
	
	public static String getMethodByProperty(String getOrSet ,String property){
		String methodName=getOrSet+( 
				property.length() == 1 ? 
						(Character.toUpperCase(property.charAt(0)) + "") : 
						 Character.toUpperCase(
								property.charAt(0))+ property.substring(1));
		//LogFactory.getLog(BeanUtils.class).debug("getMethodByProperty() methodName : "+methodName);
		return methodName;
	}
	
}
