package net.northking.cctp.element.core.reflect;

import com.alibaba.fastjson.JSON;
import net.northking.cctp.element.core.exception.KeywordArgumentConvertException;

import java.util.List;
import java.util.Map;

/**
 * Parameter type converter
 *
 * @author fengxiang.zhang
 * @since 1.0
 */
public class ArgumentConverter implements IArgumentConverter {

    private final Class<?>[] parameterTypes;

    public ArgumentConverter(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    @Override
    public Object[] convertArguments(Object[] args) throws KeywordArgumentConvertException {
        try {
            if (!shouldConvert(args)) {
                return args;
            }
            return internalConvert(args);
        } catch (Exception e) {
            throw new KeywordArgumentConvertException("参数类型转换失败", e);
        }
    }

    private Object[] internalConvert(Object[] args) {
        Object[] convertedArgs = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            if (!isArrayArgument(args[i])) {
                convertedArgs[i] = convertToType(parameterTypes[i], args[i]);
            } else {
                convertedArgs[i] = args[i];
            }
        }
        return convertedArgs;
    }

    private boolean shouldConvert(Object[] args) {
        return args != null;
    }

    private boolean isArrayArgument(Object object) {
        return object == null ? false : object.getClass().isArray();
    }

    /**
     * keyword argument convert to java method argument type
     *
     * @param type
     * @param object
     * @return
     */
    private static Object convertToType(Class<?> type, Object object) {
        /**1、支持null类型参数传入(对象传递)**/
        if (object == null) {
            return null;
        }
        /**1、支持对象类型参数传入(对象传递)**/
        if (type.isAssignableFrom(object.getClass())) {
            return object;
        }
        /**3、支持通过String类型能转换成组件期望参数类型的参数传入(兼容页面输入及String对象传递)**/
        Object result = convert(type, object.toString());
        if (result != null) {
            /** 不允许String类型转换结果为null **/
            return result;
        }
        throw new IllegalArgumentException("组件参数类型错误, 参数类型应为" + type.getCanonicalName());
    }

    /**
     * <p>参数格式</p>
     * <pre>
     * '5'                  =>  可转数字类型
     * '[1, 2, 3]'          =>  可转List集合、数组、JSONArray
     * '{"a": 1, "b": 2}'   =>  可转JSONObject、Map集合
     * </pre>
     *
     * @param type  组件期望参数类型
     * @param value 实际参数值
     * @return 转换成组件期望类型值
     */
    private static Object convert(Class<?> type, String value) {
        try {
            switch (type.getName()) {

                /** byte **/
                case "byte":
                    return Byte.parseByte(value);
                case "java.lang.Byte":
                    return Byte.valueOf(value);
                case "[B":
                    return JSON.parseObject(value, byte[].class);
                case "[Ljava.lang.Byte;":
                    return JSON.parseObject(value, Byte[].class);
                /** short **/
                case "short":
                    return Short.parseShort(value);
                case "java.lang.Short":
                    return Short.valueOf(value);
                case "[S":
                    return JSON.parseObject(value, short[].class);
                case "[Ljava.lang.Short;":
                    return JSON.parseObject(value, Short[].class);
                /** int **/
                case "int":
                    return Integer.parseInt(value);
                case "java.lang.Integer":
                    return Integer.valueOf(value);
                case "[I":
                    return JSON.parseObject(value, int[].class);
                case "[Ljava.lang.Integer;":
                    return JSON.parseObject(value, Integer[].class);
                /** long **/
                case "long":
                    return Long.parseLong(value);
                case "java.lang.Long":
                    return Long.valueOf(value);
                case "[J":
                    return JSON.parseObject(value, long[].class);
                case "[Ljava.lang.Long;":
                    return JSON.parseObject(value, Long[].class);
                /** double **/
                case "double":
                    return Double.parseDouble(value);
                case "java.lang.Double":
                    return Double.valueOf(value);
                case "[D":
                    return JSON.parseObject(value, double[].class);
                case "[Ljava.lang.Double;":
                    return JSON.parseObject(value, Double[].class);
                /** float **/
                case "float":
                    return Float.parseFloat(value);
                case "java.lang.Float":
                    return Float.valueOf(value);
                case "[F":
                    return JSON.parseObject(value, float[].class);
                case "[Ljava.lang.Float;":
                    return JSON.parseObject(value, Float[].class);
                /** boolean **/
                case "boolean":
                    return Boolean.parseBoolean(value);
                case "java.lang.Boolean":
                    return Boolean.valueOf(value);
                case "[Z":
                    return JSON.parseObject(value, boolean[].class);
                case "[Ljava.lang.Boolean;":
                    return JSON.parseObject(value, Boolean[].class);
                /** String **/
                case "java.lang.String":
                    return value;
                case "[Ljava.lang.String;":
                    return JSON.parseObject(value, String[].class);
                /** JSON&集合 **/
                case "com.alibaba.fastjson.JSONArray":
                    return JSON.parseArray(value);
                case "com.alibaba.fastjson.JSONObject":
                    return JSON.parseObject(value);
                case "java.util.List":
                    return JSON.parseObject(value, List.class);
                case "java.util.Map":
                    return JSON.parseObject(value, Map.class);
                default:
                    return null;
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("组件参数语法错误, 参数类型应为" + type.getCanonicalName());
        }
    }

}