package com.wecloud.dispatch.general.impl;

import cn.hutool.core.bean.copier.BeanCopier;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.wecloud.dispatch.ActionContext;
import com.wecloud.dispatch.extend.ActionRequest;
import com.wecloud.dispatch.extend.ArgumentBox;
import com.wecloud.dispatch.extend.MethodArgumentResolver;
import com.wecloud.utils.JsonUtils;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author lixiaozhong
 */
public class GeneralMethodArgumentResolver implements MethodArgumentResolver {

	protected final DefaultConversionService conversionService = new DefaultConversionService();

	Map<Class<?>, Boolean> canNotInstanceMap = new HashMap<>();

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
//		return parameter.hasParameterAnnotation(RequestParam.class);
		return true;
	}

	@Override
	public Object resolveArgument(
			ActionContext actionContext,
			MethodParameter parameter,
			ActionRequest request,
			ArgumentBox argumentBox) {
		Object object = null;

		RequestParam define = parameter.getParameterAnnotation(RequestParam.class);
		// 获取属定义的名称
		String name = null == define ? parameter.getParameterName() : define.value();

		Type t = parameter.getGenericParameterType();
		Class<?> clazz = parameter.getParameterType();

		Object value = request.getData().get(name);

		if(ActionRequest.class.isAssignableFrom(clazz)) {
			object = request;
		} else if (ArgumentBox.class.isAssignableFrom(clazz)) {
			object = argumentBox;
		} else {
			object = JsonUtils.beanCopyDeep(value, clazz);
		}
		if(object == null) {
			object = getDefaultValue(clazz);
		}
		if (null == object && isCanInstance(clazz) && !canNotInstanceMap.containsKey(clazz)) {
			object = getObject(clazz);
		}
		return object;
	}

	/**
	 *
	 * 是否可以被实例化
	 *
	 * @param classType
	 * @return
	 */
	private boolean isCanInstance(Class<?> classType) {
		boolean isAbstract = Modifier.isAbstract(classType.getModifiers());
		boolean can = true;
		if (classType.isAnnotation()) {
			can = false;
		} else if (classType.isArray()) {
			can = false;
		} else if (classType.isEnum()) {
			can = false;
		} else if (classType.isInterface()) {
			can = false;
		} else if (isAbstract) {
			can = false;
		}
		return can;
	}

	private <T> Object getObject(Class<T> clazz) {
		Object object = null;
		if (isCanInstance(clazz) && !canNotInstanceMap.containsKey(clazz)) {
			try {
				object = clazz.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				canNotInstanceMap.put(clazz, false);
			}
		}
		return object;
	}

	/**
	 * @param clazz
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T> T getDefaultValue(Class<T> clazz) {
		Object object = null;
		if (List.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new ArrayList<Object>(0);
			}
		} else if (Map.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new HashMap<Object, Object>(0);
			}
		} else if (Set.class.isAssignableFrom(clazz)) {
			if (isCanInstance(clazz)) {
				object = getObject(clazz);
			} else {
				object = new HashSet<Object>(0);
			}
		} else if (clazz.isArray()) {
			Class<?> componentType = clazz.getComponentType();
			object = Array.newInstance(componentType, 0);
		} else if (clazz == int.class) {
			object = 0;
		} else if (clazz == long.class) {
			object = 0L;
		} else if (clazz == float.class) {
			object = 0.0F;
		} else if (clazz == double.class) {
			object = 0.00D;
		} else if (clazz == byte.class) {
			object = (byte) 0;
		} else if (clazz == char.class) {
			object = '\u0000';
		} else if (clazz == short.class) {
			object = 0;
		} else if (clazz == boolean.class) {
			object = false;
		}
		return ((T) object);
	}

	/**
	 * 是否自定义实现Map
	 *
	 * @param clazz
	 * @return
	 */
	boolean isCustomMap(Class<?> clazz) {
		boolean isMap = Map.class.isAssignableFrom(clazz);
		boolean isSubMap = java.util.HashMap.class == clazz
				|| java.util.Hashtable.class == clazz
				|| java.util.TreeMap.class == clazz
				|| java.util.IdentityHashMap.class == clazz
				|| java.util.WeakHashMap.class == clazz
				|| java.util.LinkedHashMap.class == clazz;
		return isMap && !isSubMap;
	}

	boolean isCollection(Class<?> clazz) {
		return Collection.class.isAssignableFrom(clazz);
	}
}
