/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 com.alipay.test.acts.util;

import au.com.bytecode.opencsv.CSVWriter;
import com.alipay.test.acts.log.ActsLogUtil;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.azeckoski.reflectutils.ClassFields;
import org.azeckoski.reflectutils.ReflectUtils;
import org.azeckoski.reflectutils.ClassData;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.alipay.test.acts.db.enums.CSVColEnum;
import com.alipay.test.acts.log.ActsLogUtil;
import com.alipay.test.acts.object.manager.ObjectTypeManager;
import com.google.common.collect.ImmutableList;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.Parameter;
import com.google.common.reflect.TypeToken;

/**
 * external method for acts ide
 *
 * @author mahongliang
 * @version $Id: CSVApisUtil.java, v 0.1 2015年11月20日 下午12:48:57  Exp $
 */
public class CSVApisUtil {
    private static final Log LOG = LogFactory.getLog(CSVApisUtil.class);

    /**
     * External method. Parse Class
     *
     * @param clsName  class
     * @param classLoade  clsLoad
     * @param genRootPath csv root path
     * @throws ClassNotFoundException
     */
    public static Set<String> paraClassToCscFile(String clsName, ClassLoader classLoade,
                                                 String genRootPath) throws ClassNotFoundException {
        Set<String> sbfWarnError = new HashSet<String>();
        Class<?> specifyClass = classLoade.loadClass(clsName);
        if (specifyClass.isInterface()) {
            processInterface(specifyClass, genRootPath, sbfWarnError);
        } else {
            doProcess(specifyClass, null, new HashSet<String>(),
                mkCsvFolderForCls(specifyClass, null, genRootPath), sbfWarnError);
        }

        return sbfWarnError;
    }

    /**
     * External method. Parse the input and return parameters of the specified method
     *
     * @param clsName  class
     * @param classLoade  clsLoad
     * @param genRootPath csv root path
     * @throws ClassNotFoundException
     */
    public static Set<String> paraClassSpeciMethodToCscFile(String clsName, ClassLoader classLoade,
                                                            String genRootPath, String methodName,
                                                            boolean isResultOnly)
                                                                                 throws ClassNotFoundException {
        Set<String> sbfWarnError = new HashSet<String>();
        Class<?> specifyClass = classLoade.loadClass(clsName);
        processInterfaceMethod(specifyClass, genRootPath, sbfWarnError, methodName, isResultOnly);

        return sbfWarnError;

    }

    /**
     * create a CSV file based on the class
     *
     * @param clsMain
     * @param genRootPath
     * @return created csv file
     */
    public static String mkCsvFolderForCls(Class<?> clsMain, Type clsType, String genRootPath) {
        if (isWrapClass(clsMain)) {
            LOG.warn("do nothing when simple type");
            return null;
        }
        if (clsMain.isArray() && isWrapClass(clsMain.getComponentType())) {
            LOG.warn("do nothing  when the element'class in array is simple type");
            return null;
        }

        Class<?> clsAdd = clsMain.isArray() ? clsMain.getComponentType() : clsMain;

        String csvRootPath;
        String csvFolder;
        File objModel = FileUtil.getTestResourceFileByRootPath(genRootPath);
        if (!objModel.exists()) {
            objModel.mkdir();
        }
        if (clsType == null) {
            csvFolder = genRootPath + clsAdd.getSimpleName();
            csvRootPath = csvFolder + "/" + clsAdd.getSimpleName() + ".csv";
        } else {
            if (clsType instanceof ParameterizedType) {
                Class<?> clsSub = getParameRawCls(clsType);
                Type typeInner = HandMutiParameType(clsType, 0);
                Class<?> typeSub = getClass(typeInner, 0);
                //csv name with a generic name
                csvFolder = genRootPath + clsAdd.getSimpleName() + "_" + clsSub.getSimpleName()
                            + "_" + typeSub.getSimpleName();
                csvRootPath = csvFolder + "/" + clsAdd.getSimpleName() + "_"
                              + clsSub.getSimpleName() + "_" + typeSub.getSimpleName() + ".csv";
            } else {
                Class<?> clsSub = getClass(clsType, 0);
                //csv name with a generic name
                csvFolder = genRootPath + clsAdd.getSimpleName() + "_" + clsSub.getSimpleName();
                csvRootPath = csvFolder + "/" + clsAdd.getSimpleName() + "_"
                              + clsSub.getSimpleName() + ".csv";
            }

        }
        File file = FileUtil.getTestResourceFileByRootPath(csvFolder);
        if (!file.exists()) {
            file.mkdir();
        }
        return csvRootPath;
    }

    /**
     * get .csv file path based on the class
     *
     * @param objClass
     * @param csvPath
     * @return
     */
    private static String getCsvFileName(Class<?> objClass, String csvPath) {

        if (isWrapClass(objClass)) {
            LOG.warn("do nothing when simple type");
            return null;
        }
        String[] paths = csvPath.split("/");
        ArrayUtils.reverse(paths);

        String className = objClass.getSimpleName() + ".csv";

        if (!StringUtils.equals(className, paths[0])) {
            csvPath = StringUtils.replace(csvPath, paths[0], className);
        }

        return csvPath;
    }

    public static String getGenericCsvFileName(Class<?> sueperClass, Class<?> subClass,
                                               String csvPath) {

        if (isWrapClass(sueperClass)) {
            LOG.warn("do nothing when simple type");
            return null;
        }

        if (null == subClass) {
            return getCsvFileName(sueperClass, csvPath);
        }
        String[] paths = csvPath.split("/");
        ArrayUtils.reverse(paths);

        String className = sueperClass.getSimpleName() + "_" + subClass.getSimpleName() + ".csv";

        if (!StringUtils.equals(className, paths[0])) {
            csvPath = StringUtils.replace(csvPath, paths[0], className);
        }

        return csvPath;
    }

    /**
     *
     * @param
     * @param
     */
    private static void processInterface(Class<?> specifyClass, String genRootPath,
                                         Set<String> sbfWarnError) {
        ReflectUtils refUtil = ReflectUtils.getInstance();
        ClassFields<?> clsFiled = refUtil.analyzeClass(specifyClass);
        ClassData<?> getPro = clsFiled.getClassData();
        List<Method> listMetchod = getPro.getMethods();
        for (Method method : listMetchod) {
            Invokable invoke = Invokable.from(method);
            ImmutableList<Parameter> mParameters = invoke.getParameters();
            //input parameters
            for (Parameter parameter : mParameters) {

                try {
                    Class<?> paraCls = parameter.getType().getRawType();
                    TypeToken<?> preToken = parameter.getType();
                    TypeToken<?> genericTypeToken = preToken.resolveType(preToken.getType());
                    String csvFile = null;
                    if (!(genericTypeToken.getType() instanceof ParameterizedType)) {
                        doProcess(paraCls, null, new HashSet<String>(),
                            mkCsvFolderForCls(paraCls, null, genRootPath), sbfWarnError);
                    } else {
                        if (Map.class.isAssignableFrom(paraCls)) {
                            //MAP
                            csvFile = mkCsvFolderForCls(paraCls,
                                HandMutiParameType(genericTypeToken.getType(), 1), genRootPath);
                        } else {
                            csvFile = mkCsvFolderForCls(paraCls,
                                HandMutiParameType(genericTypeToken.getType(), 0), genRootPath);
                        }
                        ContainerUtils.handContainer(paraCls,
                            HandMutiParameType(genericTypeToken.getType(), 0),
                            HandMutiParameType(genericTypeToken.getType(), 1), csvFile,
                            new HashSet<String>(), sbfWarnError, true);
                    }

                } catch (Throwable e) {
                    //catch ex, then parsing other parameters
                    String genModelMsg = "translate class [" + parameter.getClass().getName()
                                         + "] to CSV failed:" + e.getMessage()
                                         + ", recommended to generate templates using class!"
                                         + "\n";
                    sbfWarnError.add(genModelMsg);
                }
            }
            try {
                //return parameters
                retClsToMkCsv(genRootPath, method, sbfWarnError);
            } catch (Throwable e) {
                //catch ex, then parsing other parameters
                String genModelMsg = "translate class" + method.getGenericReturnType().toString()
                                     + "] to CSV failed:" + e.getMessage()
                                     + ", recommended to generate templates using class!" + "\n";
                sbfWarnError.add(genModelMsg);
            }
        }
    }

    /**
     * generate the input and return parameters of the specified method in the interface
     *
     * @param specifyClass
     * @param genRootPath
     * @param sbfWarnError
     * @param methodName
     * @param isResultOnly
     */
    private static void processInterfaceMethod(Class<?> specifyClass, String genRootPath,
                                               Set<String> sbfWarnError, String methodName,
                                               boolean isResultOnly) {
        ReflectUtils refUtil = ReflectUtils.getInstance();
        ClassFields<?> clsFiled = refUtil.analyzeClass(specifyClass);
        ClassData<?> getPro = clsFiled.getClassData();
        List<Method> listMetchod = getPro.getMethods();
        for (Method method : listMetchod) {
            if (StringUtils.equals(method.getName(), methodName)) {
                Invokable invoke = Invokable.from(method);
                ImmutableList<Parameter> mParameters = invoke.getParameters();
                //input parameter
                if (!isResultOnly) {
                    for (Parameter parameter : mParameters) {

                        try {
                            Class<?> paraCls = parameter.getType().getRawType();
                            TypeToken<?> preToken = parameter.getType();
                            TypeToken<?> genericTypeToken = preToken
                                .resolveType(preToken.getType());
                            String csvFile = null;
                            if (!(genericTypeToken.getType() instanceof ParameterizedType)) {
                                doProcess(paraCls, null, new HashSet<String>(),
                                    mkCsvFolderForCls(paraCls, null, genRootPath), sbfWarnError);
                            } else {
                                if (Map.class.isAssignableFrom(paraCls)) {
                                    //MAP
                                    csvFile = mkCsvFolderForCls(paraCls,
                                        HandMutiParameType(genericTypeToken.getType(), 1),
                                        genRootPath);
                                } else {
                                    csvFile = mkCsvFolderForCls(paraCls,
                                        HandMutiParameType(genericTypeToken.getType(), 0),
                                        genRootPath);
                                }
                                ContainerUtils.handContainer(paraCls,
                                    HandMutiParameType(genericTypeToken.getType(), 0),
                                    HandMutiParameType(genericTypeToken.getType(), 1), csvFile,
                                    new HashSet<String>(), sbfWarnError, true);
                            }

                        } catch (Throwable e) {
                            //catch ex, then parsing other parameters
                            String genModelMsg = "class ["
                                                 + parameter.getClass().getName()
                                                 + "] to CSV failed:"
                                                 + e.getMessage()
                                                 + ", recommended to generate templates using class!"
                                                 + "\n";
                            sbfWarnError.add(genModelMsg);
                        }
                    }
                }
                try {
                    //return parameter
                    retClsToMkCsv(genRootPath, method, sbfWarnError);
                } catch (Throwable e) {
                    //catch ex, then parsing other parameters
                    String genModelMsg = "class [" + method.getGenericReturnType().toString()
                                         + "] to CSV failed:" + e.getMessage()
                                         + ", recommended to generate templates using class!"
                                         + "\n";
                    sbfWarnError.add(genModelMsg);
                }
            }

        }
    }

    /**
     * parse return parameter
     *
     * @param genRootPath
     * @param method
     */
    private static void retClsToMkCsv(String genRootPath, Method method, Set<String> sbfWarnError) {
        TypeToken<?> preToken = TypeToken.of(method.getGenericReturnType());
        TypeToken<?> genericTypeToken = preToken.resolveType(method.getGenericReturnType());
        Class<?> rawClss = genericTypeToken.getRawType();

        if (!(method.getGenericReturnType() instanceof ParameterizedType)) {
            doProcess(genericTypeToken.getRawType(), null, new HashSet<String>(),
                mkCsvFolderForCls(rawClss, null, genRootPath), sbfWarnError);
        } else {
            if (Map.class.isAssignableFrom(rawClss)) {
                //MAP
                String csvFile = mkCsvFolderForCls(rawClss,
                    HandMutiParameType(genericTypeToken.getType(), 1), genRootPath);
                ContainerUtils.handContainer(rawClss,
                    HandMutiParameType(genericTypeToken.getType(), 0),
                    HandMutiParameType(genericTypeToken.getType(), 1), csvFile,
                    new HashSet<String>(), sbfWarnError, true);
            } else {
                //other
                String csvFile = mkCsvFolderForCls(rawClss,
                    HandMutiParameType(genericTypeToken.getType(), 0), genRootPath);
                ContainerUtils.handContainer(rawClss,
                    HandMutiParameType(genericTypeToken.getType(), 0), null, csvFile,
                    new HashSet<String>(), sbfWarnError, true);
            }
        }
    }

    private static final ObjectTypeManager objectTypeManager             = new ObjectTypeManager();

    /** multiple elements:"1;2;3" */
    private static final String            LIST_CONTENT_TEMPLATE         = "1";
    /** multiple elements:"FILE@1;2" */
    private static final String            COMPLEX_LIST_CONTENT_TEMPLATE = "FILE@1";
    private static final String            COMPLEX_TYPE_CONTENT_TEMPLATE = "FILE@1";
    private static final String            FILE_WORDS                    = "FILE";

    /**
     *
     * @param
     * @param
     */
    public static void doProcess(Class<?> classTopara, Class<?> subCls, Set<String> setCalue,
                                 String csvRoot, Set<String> sbfWarn) {
        if (StringUtils.isBlank(csvRoot)) {
            ActsLogUtil.warn(LOG, "The path is empty and the CSV file cannot be generated.");
            return;
        }
        if (null == classTopara) {
            ActsLogUtil.warn(LOG, "The class name is empty and cannot generate a CSV file");
            return;
        }

        if (classTopara.isArray()) {
            classTopara = classTopara.getComponentType();
        }

        if (isWrapClass(classTopara) || setCalue.contains(classTopara.getName())) {
            return;
        }

        File file = FileUtil.getTestResourceFileByRootPath(csvRoot);
        if (file.exists()) {
            ActsLogUtil.warn(LOG, "file [" + csvRoot + "] already exists,skip directly");
            return;
        }

        Map<String, Field> getPro = findTargetClsFields(classTopara);

        //prevent loops
        avoidDeedLoop(setCalue, classTopara, subCls);

        List<String[]> outputValues = new ArrayList<String[]>();
        //assemble the first line of the CSV file:the header line
        List<String> header = new ArrayList<String>();
        header.add(CSVColEnum.CLASS.getCode());
        header.add(CSVColEnum.PROPERTY.getCode());
        header.add(CSVColEnum.TYPE.getCode());
        header.add(CSVColEnum.RULE.getCode());
        header.add(CSVColEnum.FLAG.getCode());
        header.add("value");
        outputValues.add(header.toArray(new String[header.size()]));

        int i = 1;

        for (Entry<String, Field> proValue : getPro.entrySet()) {
            if (null == proValue.getValue()) {
                continue;
            }

            List<String> value = new ArrayList<String>();
            if (1 == i) {
                //If it is the first generated, the content needs to contain the class name.
                value.add(classTopara.getName());
            } else {
                value.add("");
            }
            //attribute's name
            value.add(proValue.getKey());
            //attribute's type name
            value.add(proValue.getValue().getType().getName());

            //data rule
            value.add("");

            //class type of this attribute
            Class<?> filedCls = proValue.getValue().getType();
            Boolean isHandle = false;
            if (setCalue.contains(filedCls.getName())) {
                //prevent infinite loops
                value.add("N");
                value.add("null");
                isHandle = true;
            } else if (isWrapClass(filedCls)) {

                addSimpleValue(proValue.getKey(), filedCls, value);
                isHandle = true;
            }

            if (null != proValue.getValue()) {
                if (!isHandle && Map.class.isAssignableFrom(filedCls)) {

                    try {
                        value.add("M");
                        Class<?> rawClass = getParameRawCls(proValue.getValue().getGenericType());

                        Type mapKeycls = HandMutiParameType(proValue.getValue().getGenericType(), 0);
                        Type mapValueCls = HandMutiParameType(proValue.getValue().getGenericType(),
                            1);

                        String mapPath;
                        if (mapValueCls instanceof ParameterizedType) {
                            mapPath = getGenericCsvFileName(rawClass,
                                (Class) ((ParameterizedType) mapValueCls).getRawType(), csvRoot);
                        } else {
                            mapPath = getGenericCsvFileName(rawClass, getClass(mapValueCls, 0),
                                csvRoot);
                        }

                        ContainerUtils.handContainer(rawClass, mapKeycls, mapValueCls, mapPath,
                            setCalue, sbfWarn, false);
                        value.add(cutCsvName(mapPath) + "@1");
                    } catch (Throwable e) {
                        //default value when failed
                        value.set(4, "N");
                        value.add("null");
                        LOG.warn("", e);
                    }
                } else if (!isHandle) {

                    Class<?> norMalCls = getClass(proValue.getValue().getGenericType(), 0);
                    //Array
                    if (filedCls.isArray()) {
                        norMalCls = filedCls.getComponentType();
                    }

                    value.add("Y");
                    if (objectTypeManager.isCollectionType(filedCls)) {
                        if (objectTypeManager.isSimpleType(norMalCls)) {
                            value.add(LIST_CONTENT_TEMPLATE);
                        } else if (Map.class.isAssignableFrom(norMalCls)) {
                            //List<Map<>>
                            TypeToken<?> preToken = TypeToken.of(proValue.getValue()
                                .getGenericType());
                            TypeToken<?> genericTypeToken = preToken.resolveType(proValue
                                .getValue().getGenericType());

                            Type mapType = HandMutiParameType(genericTypeToken.getType(), 0);
                            Class<?> rawClass = CSVApisUtil.getParameRawCls(mapType);

                            Type mapKeycls = CSVApisUtil.HandMutiParameType(mapType, 0);
                            Type mapValueCls = CSVApisUtil.HandMutiParameType(mapType, 1);

                            String mapPath;
                            if (mapValueCls instanceof ParameterizedType) {
                                mapPath = getGenericCsvFileName(rawClass,
                                    (Class) ((ParameterizedType) mapValueCls).getRawType(), csvRoot);
                            } else {
                                mapPath = getGenericCsvFileName(rawClass, getClass(mapValueCls, 0),
                                    csvRoot);
                            }

                            ContainerUtils.handContainer(rawClass, mapKeycls, mapValueCls, mapPath,
                                setCalue, sbfWarn, false);
                            value.add(cutCsvName(mapPath) + "@1");

                        } else {

                            //Set<Object> is not supported
                            if (Set.class.isAssignableFrom(filedCls)) {
                                value.set(4, "N");
                                value.add("null");
                            } else {
                                //List<Object>:element is a complex object

                                //prevent nested list elements
                                if (setCalue.contains(norMalCls.getName())) {
                                    value.set(4, "N");
                                    value.add("null");
                                } else {
                                    value.add(COMPLEX_LIST_CONTENT_TEMPLATE.replace(FILE_WORDS,
                                        norMalCls.getSimpleName() + ".csv"));
                                }

                                try {
                                    doProcess(norMalCls, null, setCalue,
                                        getCsvFileName(norMalCls, csvRoot), sbfWarn);
                                } catch (Throwable e) {
                                    //default value when failed
                                    value.set(4, "N");
                                    value.set(5, "null");
                                    LOG.warn("process complex classes in list failed", e);
                                }
                            }
                        }
                    } else if (proValue.getValue().getGenericType() instanceof ParameterizedType) {
                        value.add(COMPLEX_LIST_CONTENT_TEMPLATE.replace(FILE_WORDS,
                            filedCls.getSimpleName() + "_" + norMalCls.getSimpleName() + ".csv"));
                        try {
                            doProcess(filedCls, norMalCls, setCalue,
                                getGenericCsvFileName(filedCls, norMalCls, csvRoot), sbfWarn);
                        } catch (Throwable e) {
                            //default value when failed
                            value.set(4, "N");
                            value.set(5, "null");
                            LOG.warn("process generic classes failed", e);
                        }
                    } else if (filedCls.isInterface()
                               || Modifier.isAbstract(filedCls.getModifiers())) {
                        //stop generating templates when interface or abstraction
                        value.set(4, "N");
                        value.add("null");
                    } else {
                        value.add(COMPLEX_TYPE_CONTENT_TEMPLATE.replace(FILE_WORDS,
                            filedCls.getSimpleName() + ".csv"));
                    }
                    if (!isWrapClass(norMalCls) && !setCalue.contains(norMalCls.getName())
                        && !Map.class.isAssignableFrom(norMalCls)) {
                        //deal with internal complex classes
                        try {
                            doProcess(norMalCls, null, setCalue,
                                getCsvFileName(norMalCls, csvRoot), sbfWarn);
                        } catch (Throwable e) {
                            //default value when failed
                            value.set(4, "N");
                            value.set(5, "null");
                            LOG.warn("process internal complex classes failed", e);
                        }
                    }
                }
            }
            outputValues.add(value.toArray(new String[value.size()]));
            i++;
        }

        //no field of complicated objects
        if (getPro.size() == 0) {
            sbfWarn.add(cutCsvName(csvRoot));
            List<String> value = new ArrayList<String>();
            value.add(classTopara.getName());
            value.add("");
            value.add("");
            value.add("");
            value.add("");
            value.add("");
            outputValues.add(value.toArray(new String[value.size()]));
        }
        writeToCsv(file, outputValues);
        setCalue.remove(classTopara.getName());
    }

    public static String cutCsvName(final String csvRoot) {
        return StringUtils.substringAfterLast(csvRoot, "/");
    }

    public static Map<String, Field> findTargetClsFields(Class<?> cls) {
        if (isWrapClass(cls)) {
            return null;
        }
        ReflectUtils refUtil = ReflectUtils.getInstance();
        ClassFields<?> clsFiled = refUtil.analyzeClass(cls);
        List<Field> getPro = clsFiled.getClassData().getFields();
        Map<String, Field> reClsProp = new HashMap<String, Field>();
        for (Field proValue : getPro) {
            if (Modifier.isFinal(proValue.getModifiers())
                || Modifier.isStatic(proValue.getModifiers())) {
                continue;
            }
            String propName = proValue.getName();
            reClsProp.put(propName, proValue);
        }

        return reClsProp;
    }

    public static void writeToCsv(File file, List<String[]> outputValues) {

        OutputStream outputStream = null;
        try {
            outputStream = new FileOutputStream(file);
        } catch (Exception e) {
            ActsLogUtil.warn(LOG, "file [" + file.getName() + "] not fund." + e);
            throw new RuntimeException(e);
        }
        //write the generated content to a CSV file
        try {
            OutputStreamWriter osw = null;
            osw = new OutputStreamWriter(outputStream);
            CSVWriter csvWriter = new CSVWriter(osw);
            csvWriter.writeAll(outputValues);
            csvWriter.close();
            ActsLogUtil.warn(LOG, file.getName() + "generated successfully");
        } catch (Exception e) {
            ActsLogUtil.warn(LOG, "write to CSV file failed:" + file.getName() + e);
            throw new RuntimeException(e);
        }
    }

    public static void addSimpleValue(String fieldName, Class<?> fieldType, List<String> value) {
        if (StringUtils.equals(java.util.Date.class.getName(), fieldType.getName())) {
            value.add("D");
            value.add("today");
        } else if (StringUtils.equals(java.util.Currency.class.getName(), fieldType.getName())) {

            value.add("Y");
            value.add("CNY");

        } else if (StringUtils.equals(fieldName, "currencyValue")) {
            //default value
            value.add("Y");
            value.add("156");

        } else if (StringUtils.equals(fieldType.getName(), "int")
                   || StringUtils.equals(fieldType.getName(), "java.lang.Integer")
                   || StringUtils.equals(fieldType.getName(), "long")
                   || StringUtils.equals(fieldType.getName(), "java.lang.Long")
                   || StringUtils.equals(fieldType.getName(), "short")) {
            value.add("Y");
            value.add("0");
        } else if (StringUtils.equals(fieldType.getName(), "float")
                   || StringUtils.equals(fieldType.getName(), "double")) {
            value.add("Y");
            value.add("0.0");
        } else if (StringUtils.equals(fieldType.getName(), "boolean")) {
            value.add("Y");
            value.add("false");
        } else if (StringUtils.equals(fieldType.getName(), "char")) {
            value.add("Y");
            value.add("A");
        } else if (StringUtils.equals(fieldType.getName(), "java.math.BigDecimal")) {
            value.add("Y");
            value.add("0.001");
        } else if (StringUtils.equals(fieldType.getName(), "java.lang.Object")) {
            value.set(2, "java.lang.Object");
            value.add("N");
            value.add("");
        } else if (StringUtils.equals(fieldType.getName(), "java.lang.Void")) {
            value.set(2, "java.lang.Void");
            value.add("N");
            value.add("null");
        } else {
            value.add("Y");
            value.add("null");
        }
    }

    /**
     * get the type of generic
     * @param type
     * @param i
     * @return
     */
    public static Class<?> getClass(Type type, int i) {
        if (type instanceof ParameterizedType) {

            return getGenericClass((ParameterizedType) type, i);
        } else if (type instanceof GenericArrayType) {
            return (Class) ((GenericArrayType) type).getGenericComponentType();
        } else if (type instanceof TypeVariable) {
            return getClass(((TypeVariable) type).getBounds()[0], 0);
        } else if (type instanceof WildcardType) {
            WildcardType wuleType = (WildcardType) type;
            if (null != wuleType.getUpperBounds()[0]) {
                return getClass(wuleType.getUpperBounds()[0], 0);
            } else {
                return getClass(wuleType.getLowerBounds()[0], 0);
            }
        } else if (type instanceof Class) {
            return (Class<?>) type;
        } else {
            return (Class<?>) type;
        }
    }

    /**
     *
     * @param type
     * @param i
     * @return
     */
    public static Type HandMutiParameType(Type type, int i) {
        if (type instanceof ParameterizedType) {
            try {
                ParameterizedType outType = (ParameterizedType) type;
                return outType.getActualTypeArguments()[i];
            } catch (Exception e) {
                return null;
            }
        } else if (type instanceof Class) {
            if (0 == i) {
                return type;
            }
            return null;
        } else {
            if (0 == i) {
                getClass(type, 0);
            } else {
                return null;
            }
        }
        return null;

    }

    /**
     *
     * @param type
     *
     * @return
     */
    public static Class<?> getParameRawCls(Type type) {
        if (type instanceof ParameterizedType) {
            try {
                ParameterizedType outType = (ParameterizedType) type;
                return (Class) outType.getRawType();
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }

    /**
     *
     * @param type
     *
     * @return
     */
    public static int getTypeCount(Type type) {
        if (type instanceof ParameterizedType) {
            try {
                ParameterizedType outType = (ParameterizedType) type;
                return outType.getActualTypeArguments().length;
            } catch (Exception e) {
                return 0;
            }
        } else if (type instanceof Class) {
            return 0;
        }
        return 1;
    }

    private static Class<?> getGenericClass(ParameterizedType parameterizedType, int i) {
        Type genericClass = null;
        try {
            genericClass = parameterizedType.getActualTypeArguments()[i];
        } catch (Exception e) {
            return null;
        }

        if (genericClass instanceof ParameterizedType) {
            return (Class) ((ParameterizedType) genericClass).getRawType();
        } else if (genericClass instanceof GenericArrayType) {
            return (Class<?>) ((GenericArrayType) genericClass).getGenericComponentType();
        } else if (genericClass instanceof TypeVariable) {
            return getClass(((TypeVariable) genericClass).getBounds()[0], 0);
        } else if (genericClass instanceof WildcardType) {
            WildcardType wuleType = (WildcardType) genericClass;
            if (null != wuleType.getUpperBounds()[0]) {
                return getClass(wuleType.getUpperBounds()[0], 0);
            } else {
                return getClass(wuleType.getLowerBounds()[0], 0);
            }

        } else {
            return (Class<?>) genericClass;
        }
    }

    private static void avoidDeedLoop(Set<String> mapLoop, Class<?> clsFullName, Class<?> subClass) {

        String clsName = clsFullName.getName();
        //filter out simple type
        if (isWrapClass(clsFullName)) {
            return;
        }

        if (null != subClass) {
            mapLoop.remove(clsFullName);
            clsName += "_" + subClass.getSimpleName();

        }
        if (!mapLoop.contains(clsName)) {
            mapLoop.add(clsName);
        }
    }

    private final static Set<String> simpleClassType = new HashSet<String>();

    static {
        simpleClassType.add("boolean");
        simpleClassType.add("java.lang.Integer");
        simpleClassType.add("java.lang.Float");
        simpleClassType.add("java.lang.Double");
        simpleClassType.add("java.lang.Long");
        simpleClassType.add("java.lang.Short");
        simpleClassType.add("java.lang.Byte");
        simpleClassType.add("java.lang.Boolean");
        simpleClassType.add("java.lang.Character");
        simpleClassType.add("java.util.Properties");
        simpleClassType.add("java.lang.String");
        simpleClassType.add("java.util.Date");

        simpleClassType.add("java.util.Currency");
        simpleClassType.add("java.math.BigDecimal");
        simpleClassType.add("java.io.Serializable");
        simpleClassType.add("java.lang.Object");
        simpleClassType.add("java.lang.Void");

    }

    /**
     *
     * @param clsToJude
     * @return
     */
    public static boolean isWrapClass(Class<?> clsToJude) {

        if (clsToJude.isPrimitive() || clsToJude.isEnum()
            || clsToJude.getName().toLowerCase().contains("enum")
            || simpleClassType.contains(clsToJude.getName())) {
            return true;
        }

        return false;

    }
}