package com.brilliance.mda.runtime.mda.driver;

import com.brilliance.mda.runtime.annotation.RelPath;
import com.brilliance.mda.runtime.annotation.Transaction;
import com.brilliance.mda.runtime.mda.*;
import com.brilliance.mda.runtime.mda.impl.AbstractModule;
import com.brilliance.mda.runtime.mda.impl.DatafieldImpl;
import com.brilliance.mda.runtime.mda.impl.ModuleList;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.util.*;

public class MdaDriver {

    @SuppressWarnings("rawtypes")
    public static IBaseObject getBaseObject(IModule root, String target, boolean helpLoad) {
        try {
            if (root == null) {
                root = MdaEnv.getContext().getRoot();
            }

            if (target == null || target.length() == 0)
                return root;
            if (MdaEnv.getContext().getRoot() == root && target.charAt(0) == '\\')
                target = target.substring(1);
            int dotidx = target.indexOf(".");
            if (dotidx < 0)
                dotidx = target.indexOf("\\");
            String fieldName = target;
            String leftPath = null;
            if (dotidx > 0) {
                fieldName = target.substring(0, dotidx);
                leftPath = target.substring(dotidx + 1);
                int begIndex = dotidx + 1;
                while (target.charAt(begIndex) == '.' || target.charAt(begIndex) == '\\')
                    begIndex++;
                leftPath = target.substring(begIndex);
            }

            Class<?> clazz = root.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            if (field.getType() == ModuleHolder.class) {
                IModule module = (IModule) ((ModuleHolder) field.get(root)).get();
                if (dotidx < 0)
                    return module;
                else
                    return getBaseObject(module, leftPath, helpLoad);
            } else if (field.getType() != IModule.class && IModule.class.isAssignableFrom(field.getType()))  //模型
            {
                IModule module = (IModule) field.get(root);
                if (helpLoad && module == null) {
                    String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    try {
                        Method m = clazz.getMethod(getMethodName);
                        module = (IModule) m.invoke(root);
                    } catch (Exception e) {

                    }
                }
                if (module == null)
                    return null;

                if (dotidx < 0)
                    return module;
                else
                    return getBaseObject(module, leftPath, helpLoad);
            } else if (IModuleList.class.isAssignableFrom(field.getType())) {
                IModuleList moduleList = (IModuleList) field.get(root);
                if (helpLoad && moduleList == null) {
                    String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    try {
                        Method m = clazz.getMethod(getMethodName);
                        moduleList = (IModuleList) m.invoke(root);
                    } catch (Exception e) {

                    }
                }
                return moduleList;
            } else  //其他类型默认为字段
            {
                DatafieldImpl holder = new DatafieldImpl(root, field);
                return holder;
            }

        } catch (Exception e) {

        }
        return null;
    }

    public static IBaseObject getBaseObject(IModule root, String target) {
        return getBaseObject(root, target, true);
    }

    @SuppressWarnings("rawtypes")
    public static DatafieldImpl getDatafield(IModule root, String target) {
        return (DatafieldImpl) getBaseObject(root, target);
    }

    public static DatafieldImpl getDatafield(String target) {
        return (DatafieldImpl) getBaseObject(null, target);
    }

    public static IModule getModule(IModule root, String target) {
        return (IModule) getBaseObject(root, target);
    }

    public static IModule getModule(String target) {
        return (IModule) getBaseObject(null, target);
    }

    public static IModule getModule(Object o, IModule root, String target) {
        return (IModule) getBaseObject(root, target);
    }

    @SuppressWarnings("rawtypes")
    public static IModuleList getModuleList(IModule root, String target) {
        return (IModuleList) getBaseObject(root, target);
    }

    public static IModuleList getModuleList(String target) {
        return (IModuleList) getBaseObject(null, target);
    }

    /**
     * 模型拷贝 m2值  深拷贝至m1
     *
     * @param m1
     * @param m2
     */
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void copyValues(Object m1, Object m2) {
        if (m1 == null || m2 == null)
            return;
        if (m1.getClass() != m2.getClass())
            return;
        Class<?> clazz = m1.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            if (Modifier.isStatic(field.getModifiers()))
                continue;
            Class<?> fieldType = field.getType();
            if (AbstractModule.class.isAssignableFrom(fieldType)) //正常模型
            {
                try {
                    Object obj1 = field.get(m1);
                    Object obj2 = field.get(m2);
                    if (obj1 == null && obj2 == null)
                        continue;
                    else if (obj2 == null && obj1 != null) {
                        field.set(m1, null);
                    } else if (obj1 == null && obj2 != null) // 模型类型的字段为null，调用getter
                    {
                        String getterStr = "get" + firstUpper(field.getName());
                        try {
                            Method getter = clazz.getMethod(getterStr);
                            obj1 = getter.invoke(m1);
                        } catch (Exception e) {
                            continue;
                        }
                    }
                    copyValues((IModule) obj1, (IModule) obj2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (ModuleList.class.isAssignableFrom(fieldType)) {
                try {
                    IModuleList obj1 = (IModuleList) field.get(m1);
                    IModuleList obj2 = (IModuleList) field.get(m2);
                    if (obj1 == null && obj2 == null)
                        continue;
                    else if (obj2 == null && obj1 != null) {
                        field.set(m1, null);
                    } else if (obj1 == null && obj2 != null) // 模型类型的字段为null，调用getter
                    {
                        String getterStr = "get" + firstUpper(field.getName());
                        try {
                            Method getter = clazz.getMethod(getterStr);
                            obj1 = (IModuleList) getter.invoke(m1);
                        } catch (Exception e) {
                            continue;
                        }
                    }
                    obj1.clear();
                    obj1.addAll(obj2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } else if (FieldHolder.class.isAssignableFrom(fieldType) || ModuleHolder.class.isAssignableFrom(fieldType)) //引用类型不复制
            {
                continue;
            } else if ("log".equals(field.getName()) || "_parent".equals(field.getName()) || "_path".equals(field.getName())) {
                continue;
            } else //字段类型
            {
                try {
                    Object obj = field.get(m2);
                    field.set(m1, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void clearModule(IModule module) {
        Field[] fields = module.getClass().getDeclaredFields();
        try {
            for (Field field : fields) {
                Class<?> fieldType = field.getType();
                field.setAccessible(true);
                if (Modifier.isStatic(field.getModifiers()))
                    continue;
                if (AbstractModule.class.isAssignableFrom(fieldType)) //正常模型
                {
                    try {
                        Object obj1 = field.get(module);
                        if (obj1 == null)
                            continue;
                        clearModule((IModule) obj1);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else if (ModuleList.class.isAssignableFrom(fieldType)) {
                    try {
                        IModuleList obj1 = (IModuleList) field.get(module);
                        if (obj1 == null)
                            continue;
                        obj1.clear();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                } else if (FieldHolder.class.isAssignableFrom(fieldType) || ModuleHolder.class.isAssignableFrom(fieldType)) //引用类型不复制
                {
                    continue;
                } else if ("log".equals(field.getName()) || "_parent".equals(field.getName()) || "_path".equals(field.getName())) {
                    continue;
                } else //字段类型
                {
                    try {
                        Class<?> zeroClazz = field.getType();
                        Object val = MdaDriver.zero(zeroClazz);
                        field.set(module, val);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }

            }
        } catch (Exception e) {
            e.fillInStackTrace(); //加入警告日志
        }
    }

    public static Object zero(Class<?> clazz) {
        if (clazz == int.class || clazz == Integer.class)
            return 0;
        if (clazz == float.class || clazz == Float.class)
            return 0f;
        if (clazz == long.class || clazz == Long.class)
            return 0l;
        if (clazz == double.class || clazz == Double.class)
            return 0d;
        if (clazz == char.class || clazz == Character.class)
            return (char) 0;
        if (clazz == byte.class || clazz == Byte.class)
            return (byte) 0;
        if (clazz == BigDecimal.class) {
            return new BigDecimal("0");
        }
        return null;

    }

    public static String getModuleType(IModule m) {
        if (m == null) return null;
        return m.getClass().getSimpleName().toLowerCase();
    }

    /**
     * 兼容设置值
     *
     * @param field Class字段
     * @param value 值
     */
    public static void setFieldValue(Field field, Object value) {

    }

    public static <T> void setValueFromVO(IContext ctx, T vo) {
        //TODO 直接基于反射实现，后续用动态直接码优化性能
        Class<?> clazz = vo.getClass();
        String parent = "";
        if (clazz.isAnnotationPresent(RelPath.class)) {
            RelPath relpath = clazz.getAnnotation(RelPath.class);
            parent = relpath.value();
        }
        setValueFromVO(ctx.getRoot(), vo, parent);
    }

    public static void setValueFromVoByClass(IModule root, Object vo, Class clazz, String parent) {
        if (!clazz.getSuperclass().getName().equalsIgnoreCase("java.lang.Object")) {
            //父类定义先赋值，子类覆盖父类的设定
            setValueFromVoByClass(root, vo, clazz.getSuperclass(), parent);
        }
        IModule pntobj = root; //父模型缓存，
        String lastPntPath = null;
        for (Field fieldVo : clazz.getDeclaredFields()) {
            if (fieldVo.getName().equals("")) return;
            if (!fieldVo.isAnnotationPresent(RelPath.class))
                continue;
            RelPath relAno = fieldVo.getAnnotation(RelPath.class);
            if (relAno.dir() == DirType.OUT) // 输出项，不取
                continue;
            String subpath = relAno.value(); // 子路径
//			if ("setmod.docamt".equals(subpath)) {
//				System.out.println("subpath");
//				String a = subpath;
//				String b = a;
//			}
            String path = parent.length() == 0 ? subpath : parent + "." + subpath; //得到路径
            if (relAno.recursion()) //递归取值
            {
                IBaseObject obj = getBaseObject(root, path);
                if (obj == null || !(obj instanceof IModule))
                    continue;
                try {
                    fieldVo.setAccessible(true);
                    Object subVo = fieldVo.get(vo);
                    setValueFromVO((IModule) obj, subVo, path);
                } catch (Exception e) {
                }
                continue;
            }
            int idx = path.lastIndexOf('.');
            Field fieldModule = null;
            if (idx < 0) {
                fieldModule = getParentField(root, path);
                pntobj = root;
                lastPntPath = null;
            } else {
                String templastPntPath = path.substring(0, idx);
                if (!templastPntPath.equals(lastPntPath)) {
                    IBaseObject obj = getBaseObject(root, templastPntPath);
                    if (obj == null || !(obj instanceof IModule)) // 找不到父模型，或者父模型不是IModule类型的
                        continue;
                    else {
                        pntobj = (IModule) obj;
                    }
                } else if (path.startsWith(lastPntPath)) {
                    //基于上次的查找找父模型
                    IBaseObject obj = getBaseObject(pntobj, path.substring(lastPntPath.length(), idx));
                    if (obj == null || !(obj instanceof IModule)) // 找不到父模型，或者父模型不是IModule类型的
                        continue;
                    else {
                        pntobj = (IModule) obj;
                    }
                }
                fieldModule = getParentField(pntobj, path.substring(idx + 1));
                lastPntPath = templastPntPath;
            }
            setFieldValue(fieldVo, vo, fieldModule, pntobj, true);
        }
    }

    public static <T> void setValueFromVO(IModule root, Object vo, String parent) {
        setValueFromVoByClass(root, vo, vo.getClass(), parent);
    }

    private static Field getParentField(Object parent, String fieldstr) {
        try {
            Field field = parent.getClass().getDeclaredField(fieldstr);
            return field;
        } catch (Exception e) {

        }
        return null;
    }

    private static void setFieldValue(Field from, Object fromObj, Field to, Object toObj, boolean voToMod) {
        try {
            if (from == null || fromObj == null || to == null || toObj == null)
                return;
            Class<?> fromClass = from.getType();
            Class<?> toClass = to.getType();
            from.setAccessible(true);
            to.setAccessible(true);
            if (List.class.isAssignableFrom(fromClass)
                    &&
                    List.class.isAssignableFrom(toClass)
                    && ((ParameterizedType) from.getGenericType()).getActualTypeArguments()[0] ==  //判断泛型是否相等
                    ((ParameterizedType) to.getGenericType()).getActualTypeArguments()[0]
            ) {
                if (!voToMod) {
                    to.set(toObj, from.get(fromObj)); //拷贝数据
                } else {
                    //添加数据、添加分页数据
                    List dataList = (List) from.get(fromObj);
                    IModuleList moduleList = (IModuleList) getBaseObject((IModule) toObj, to.getName());
                    if (dataList != null) {
                        HashMap<String, Object> inrs = new HashMap<>();
                        for (Object o : moduleList) {
                            try {
                                Field f = o.getClass().getDeclaredField("inr");
                                f.setAccessible(true);
                                inrs.put((String) f.get(o), o);
                            } catch (NoSuchFieldException ne) {
                                continue;
                            }
                        }
                        for (Object o : dataList) {
                            String inr = getFieldValue(o, "inr");
                            if (inrs.containsKey(inr))
                                moduleList.remove(inrs.get(inr));
                            moduleList.add(o);
                        }
                    }
                }
                return;
            }
            if (!to.getType().equals(from.getType())) //类型不相等
            {
                return;
            }


            to.set(toObj, from.get(fromObj));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static <T> T getFieldValue(Object o, String fieldName) {
        try {
            Field f = o.getClass().getDeclaredField(fieldName);
            f.setAccessible(true);
            return (T) f.get(o);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            return null;
        }
    }

    public static <T> void setValueToVO(IContext ctx, T vo) {
        //TODO 直接基于反射实现，后续用动态直接码优化性能
        Class<?> clazz = vo.getClass();
        String parent = "";
        if (clazz.isAnnotationPresent(RelPath.class)) {
            RelPath relpath = clazz.getAnnotation(RelPath.class);
            parent = relpath.value();
        }
        setValueToVO(ctx.getRoot(), vo, parent);
    }

    public static void setValueToVoByClass(IModule root, Object vo, Class clazz, String parent) {
        if (!clazz.getSuperclass().getName().equalsIgnoreCase("java.lang.Object")) {
            //父类定义先赋值，子类覆盖父类的设定
            setValueToVoByClass(root, vo, clazz.getSuperclass(), parent);
        }
        IModule pntobj = root; //父模型缓存，
        String lastPntPath = null;
        for (Field fieldVo : clazz.getDeclaredFields()) {
            if (!fieldVo.isAnnotationPresent(RelPath.class))
                continue;
            RelPath relAno = fieldVo.getAnnotation(RelPath.class);
            if (relAno.dir() == DirType.IN) // 输入项，不填
                continue;
            String subpath = relAno.value(); // 子路径
            String path = parent.length() == 0 ? subpath : parent + "." + subpath; //得到路径

            if (relAno.recursion()) //递归取值
            {
                IBaseObject obj = getBaseObject(root, path, false);
                if (obj == null || !(obj instanceof IModule))
                    continue;
                try {
                    fieldVo.setAccessible(true);
                    Object subVo = fieldVo.get(vo);
                    setValueToVO((IModule) obj, subVo, path);
                } catch (Exception e) {
                }
                continue;
            }
            int idx = path.lastIndexOf('.');
            Field fieldModule = null;
            if (idx < 0) {
                fieldModule = getParentField(root, path);
                pntobj = root;
                lastPntPath = null;
            } else {
                String templastPntPath = path.substring(0, idx);
                if (!templastPntPath.equals(lastPntPath)) {

                    IBaseObject obj = getBaseObject(root, templastPntPath, false); //不使用get方法获取
                    if (obj == null || !(obj instanceof IModule)) // 找不到父模型，或者父模型不是IModule类型的
                        continue;
                    else {
                        pntobj = (IModule) obj;
                    }
                } else {
                    //基于上次的查找找父模型
                    IBaseObject obj = null;
                    if (idx < lastPntPath.length())
                        obj = pntobj;//即它自己
                    else
                        obj = getBaseObject(pntobj, path.substring(lastPntPath.length(), idx), false); //不使用get方法获取
                    if (obj == null || !(obj instanceof IModule)) // 找不到父模型，或者父模型不是IModule类型的
                        continue;
                    else {
                        pntobj = (IModule) obj;
                    }
                }
                fieldModule = getParentField(pntobj, path.substring(idx + 1));
                lastPntPath = templastPntPath;
            }
            setFieldValue(fieldModule, pntobj, fieldVo, vo, false);
        }
    }

    public static void setValueToVO(IModule root, Object vo, String parent) {
        setValueToVoByClass(root, vo, vo.getClass(), parent);
    }

    public static <T> Map<String, T> translateMapByVoAnotation(String transKey, boolean pathToAlias, Map<String, T> map) {
        Map<String, T> trnsMap = new HashMap<String, T>();
        for (Map.Entry<String, T> entry : map.entrySet()) {
            String key = entry.getKey();
            T obj = entry.getValue();
            String newKey = null;
            if (pathToAlias)
                newKey = getPathAlias(transKey, key);
            else
                newKey = getAliasPath(transKey, key);
            if (newKey == null)
                continue;
            trnsMap.put(newKey, obj);
        }
        return trnsMap;
    }

    public static String getPathAlias(String trnnam, String path) {
        NamesMapper.Alias alias = NamesMapper.getAlias(trnnam);
        Map<String, String> pathAlias = alias.getPathAliasMap();
        String aliasName = pathAlias.get(path);
        if (aliasName != null)
            return aliasName;
        for (Map.Entry<String, String> entry : pathAlias.entrySet()) {
            if (!path.startsWith(entry.getKey()))
                continue;
            aliasName = entry.getValue() + "." + path.substring(entry.getKey().length());
            return aliasName;
        }
        return null;
    }

    public static String getAliasPath(String trnnam, String aliasName) {
        NamesMapper.Alias alias = NamesMapper.getAlias(trnnam);
        Map<String, String> aliasPath = alias.getRevertMap();
        String path = aliasPath.get(aliasName);
        if (path != null)
            return path;
        for (Map.Entry<String, String> entry : aliasPath.entrySet()) {
            if (!aliasName.startsWith(entry.getKey()))
                continue;
            path = entry.getValue() + "." + aliasName.substring(entry.getKey().length());
            return path;
        }
        return null;
    }

    public static void createVoSetterAndGetter(Set<Class<?>> clsSet) {
        for (Class<?> clazz : clsSet) {
            processTransAnotion(clazz);

        }
    }

    /**
     * 根据交易上的注解，生成别名与长路径的映射
     *
     * @param trnCls 交易class
     */
    private static void processTransAnotion(Class<?> trnCls) {
        Transaction trnAno = trnCls.getAnnotation(Transaction.class);
        Class<?> voCls = trnAno.vo();
        if (EmptyVO.class.equals(voCls))
            return;
        String modpntpth = null;
        if (voCls.isAnnotationPresent(RelPath.class)) {
            modpntpth = voCls.getAnnotation(RelPath.class).value();
        }
        String trnname = getTransName(trnCls);
        NamesMapper.genTransAlias(trnname); //生成一个别名映射
        processTransAnotion(trnCls, voCls, modpntpth, null);
    }

    private static void processTransAnotion(Class<?> trnCls, Class<?> voCls, String modPath, String voPath) {
        String trnname = getTransName(trnCls);
        NamesMapper.Alias alias = NamesMapper.getAlias(trnname);
        for (Field fieldVo : voCls.getDeclaredFields()) {
            if (!fieldVo.isAnnotationPresent(RelPath.class)) {
                continue;
            }
            RelPath relAno = voCls.getAnnotation(RelPath.class);
            String voFieldPath = voPath == null ? fieldVo.getName() : voPath + "." + fieldVo.getName();
            String modFieldPath = modPath == null ? relAno.value() : modPath + (
                    relAno.value() == null || relAno.value().length() == 0 ? "" : "." + relAno.value()
            );
            if (!isFieldPathExist(trnCls, modFieldPath))
                continue;
            if (relAno.recursion()) //递归遍历
            {
                processTransAnotion(trnCls, fieldVo.getType(), modFieldPath, voFieldPath);
                continue;
            }
            if (fieldVo.getType().isAssignableFrom(IModule.class)) //模型直接被引用
            {

                //continue;
            }
            //加入名称映射
            alias.getPathAliasMap().put(modFieldPath, voFieldPath);
            alias.getRevertMap().put(voFieldPath, modFieldPath);
        }
    }

    private static boolean isFieldPathExist(Class<?> trnCls, String path) {
        return false;
    }

    private static String getTransName(Class<?> trnCls) {
        Transaction trnAno = trnCls.getAnnotation(Transaction.class);
        if (trnAno.value() != null && trnAno.value().length() != 0)
            return trnAno.value();
        return trnCls.getSimpleName();
    }

    public static String firstUpper(String s) {
        if (s.length() == 1)
            return s.toUpperCase();
        return s.substring(0, 1).toUpperCase() + s.substring(1);
    }

    public static Map<String, String> getErrorMap(IContext ctx, Class voClazz) {
        Map<String, String> errorsMap = new LinkedHashMap<String, String>();
        IRuleEmitter ruleEmitter = ctx.getEmitter();

        while (!voClazz.getName().equalsIgnoreCase("java.lang.Object")
                && !ctx.getFieldErrors().isEmpty()) {
            for (Field fieldVo : voClazz.getDeclaredFields()) {
                if (!fieldVo.isAnnotationPresent(RelPath.class))
                    continue;
                RelPath relAno = fieldVo.getAnnotation(RelPath.class);
                String key = relAno.value();
                key = ruleEmitter.getRealPath(key); //取得真实key
                //使用remove，这样子类的设置优先
                String errorMessage = ctx.getFieldErrors().remove(key);
                if (errorMessage != null)
                    errorsMap.put(key, errorMessage);
            }
            voClazz = voClazz.getSuperclass();
        }
        return errorsMap;
    }
}

