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

import com.brilliance.mda.runtime.mda.IModule;
import com.brilliance.mda.runtime.mda.IModuleList;
import com.brilliance.mda.runtime.mda.IRuleEmitter;
import com.brilliance.mda.runtime.mda.impl.AbstractScopeEmitter;
import com.brilliance.mda.runtime.mda.impl.ModuleList;
import com.brilliance.mda.runtime.mda.util.MdaUtils;
import com.brilliance.mda.support.td.ModuleInfoManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class DCR {
    private static final Logger log = LoggerFactory.getLogger(DCR.class);
    public static final String DCR_SWITCH_KEY = "DCR_SWITCH";

    public static ThreadLocal<LinkedHashMap<String,TaskCombo>> queueTl = new ThreadLocal<>();

    public static ThreadLocal<Boolean> voToTradeStateTl = new ThreadLocal<>();

    public static void emptyDefaultQueue(){
        queueTl.remove();
    }
    public static void notifyDefault(String path){
        if (!isEnable())
            return;
        path = path.replaceAll("\\([0-9]*\\)", "[i]");
        executeDefaultByPath(path);
    }

    private static void executeDefaultByPath(String path){
        String transName = MdaEnv.getContext().getTransName();
        List<String> defaultList = ModuleInfoManager.getDefaultRule(transName, path);
        if (defaultList != null)
            defaultList.forEach(dr -> {
                MdaEnv.getContext().getEmitter().executeDefault(dr);
            });
    }

    public static AbstractScopeEmitter getModuleListEmitter(Class<?> moduleClass){
        String moduleName = moduleClass.getSimpleName().toLowerCase();
        String emitterName = AbstractScopeEmitter.EMITTER_PRE+moduleName;
        AbstractScopeEmitter emitter = (AbstractScopeEmitter)MdaEnv.getBean(emitterName);
        return emitter;
    }

    public static void pushQueue(IModule module, String field, Object srcValue, Object value){
        if (!isEnable())
            return;
        TaskCombo taskCombo = new TaskCombo(module,field);
        //记录当前值和原始值
        taskCombo.srcValue = srcValue;
        taskCombo.value = value;
        //放置记录
        LinkedHashMap<String,TaskCombo> queueList = queueTl.get();
        if(queueList == null){
            queueList = new LinkedHashMap<>();
            queueTl.set(queueList);
//            return;
        }
        //做一个原始状态合并
        //在一次执行过程中，如果某字段的值没有最终修改，default rule 就不要执行了
        String key = taskCombo.toString();
        TaskCombo srcTaskCombo = queueTl.get().get(key);
        if(srcTaskCombo == null){
            queueList.put(key,taskCombo);
            return;
        }
        //发生值回归，清理掉default触发
        if(MdaUtils.compareTo(srcTaskCombo.srcValue,taskCombo.value) == 0){
            queueList.remove(key);
        }else{
            srcTaskCombo.value = taskCombo.value;
        }
    }

    public static void executeQueue() {
        executeQueue(true,MdaEnv.getContext().getEmitter(),0);
    }

    public static void executeQueue(boolean flag, IRuleEmitter ruleEmitter, int countNum) {
        LinkedHashMap<String,TaskCombo> linkedList = queueTl.get();
        if (linkedList == null) {
            return;
        }
        LinkedHashSet<TaskCombo> tempList = new LinkedHashSet<>();
        tempList.addAll(linkedList.values());
        if (flag)
            linkedList.clear();
        Map<String,Object> mergeMap = mergeQueue(tempList,ruleEmitter);
        Stack<String> keyStack = new Stack<>();
        executeMergedQueue(MdaEnv.getContext().getRoot(),ruleEmitter,mergeMap,mergeMap,keyStack);
        // 有些defaultRule在方法内部对自己进行了赋值操作，这样队列还会添加，造成死循环，这里
        // 暂时用次数进行控制，如果执行次数超过5次就退出不再执行
        if(linkedList.size() != 0 && countNum < 5){
            executeQueue(true,ruleEmitter,countNum+1);
        }else{
            linkedList.clear();
        }
    }

    public static Map<String,Object> mergeQueue(LinkedHashSet<TaskCombo> tempList, IRuleEmitter ruleEmitter){
        log.debug("DCR任务数量:{}",tempList.size());
        long begMillis = System.currentTimeMillis();
        List<IModuleList> transModuleList = ruleEmitter.getAllModuleList();
        Map mergeMap = new LinkedHashMap();
        tempList.forEach(taskCombo -> {
            String path = taskCombo.module.getFieldPath(taskCombo.field);
            int begPos = path.indexOf("(");
            if (begPos < 0) {
                collectDefaultByDependency(path,mergeMap,transModuleList);
                return;
            }
            //处理ModuleList的
            int endPos = path.indexOf(")", begPos);
            String moduleListPath = path.substring(0, begPos);
            collectDefaultByDependency(moduleListPath,mergeMap,transModuleList);
            //处理后半段
            //找到其父ModuleList实例
            IModule scopeModule = taskCombo.module.getParent();
            while(!(scopeModule instanceof IModuleList)){
                scopeModule = scopeModule.getParent();
            }
            //查找Emitter
            String moduleName = ((IModuleList)scopeModule).getDataClass().getSimpleName().toLowerCase();
            AbstractScopeEmitter emitter = getModuleListEmitter(((IModuleList)scopeModule).getDataClass());
            if(emitter == null) {
                return;
            }
            //计算
            String lowerModuleListPath = moduleListPath.substring(1).replaceAll("\\\\","\\.").toLowerCase();
            Object moduleListLeaf = mergeMap.get(lowerModuleListPath);
            Map<String, Map<String, Integer>> subMergeQueueTemp = null;
            //有可能moduleList自己也有个default rule，这会导致记录点的数据类型为 Integer
            if(moduleListLeaf != null && moduleListLeaf instanceof  Map) {
                subMergeQueueTemp = (Map<String, Map<String, Integer>>) mergeMap.get(lowerModuleListPath);
            }
            if(subMergeQueueTemp == null){
                subMergeQueueTemp = new LinkedHashMap<>();
                mergeMap.put(lowerModuleListPath,subMergeQueueTemp);
            }
            String substr = path.substring(endPos + 2).toLowerCase().replaceAll("\\\\","\\.");
            //收集外部以[i]表达式的依赖
            String directPath = lowerModuleListPath+"[i]." + substr;
            collectDefaultByDependency(directPath,mergeMap,transModuleList);
            //收集[i]依赖结束
            Map<String,Map<String,Integer>> subMergeQueue = subMergeQueueTemp;

            List<String> defaultList = ModuleInfoManager.getDefaultRule(moduleName, substr);
            if (defaultList != null) {
                defaultList.forEach(dr -> {
                    if (isAbsPath(dr)) {
                        String realPath = MdaEnv.getContext().getEmitter().getRealPath(dr.substring(1));
                        mergeMap.put(realPath,0);
                    } else {
                        String index = path.substring(begPos+1,endPos);
                        Map<String,Integer> subItemQueue = subMergeQueue.get(index);
                        if(subItemQueue == null){
                            subItemQueue = new LinkedHashMap<>();
                            subMergeQueue.put(index,subItemQueue);
                        }
                        String realPath = emitter.getRealPath(dr);
                        subItemQueue.put(realPath,0);
                    }
                });
            }
        });
        log.debug("合并任务耗时：{}",System.currentTimeMillis() - begMillis);
        return mergeMap;
    }
    private static List<IModuleList> getAllModuleList(IRuleEmitter ruleEmitter){
        return ruleEmitter.getAllModuleList();
    }
    private static Map<String,Map<String,Object>> dependencyCache = new ConcurrentHashMap<>();

    private static void collectDefaultByDependencyWithCache(String path,Map<String,Object> mergeMap){

    }
    private static void collectDefaultByDependency(String path,Map<String,Object> mergeMap,List<IModuleList> moduleList){
        String transName = MdaEnv.getContext().getTransName();
        IRuleEmitter ruleEmitter = MdaEnv.getContext().getEmitter();
        List<String> defaultList = ModuleInfoManager.getDefaultRule(transName, path);
        if (defaultList != null) {
            defaultList.forEach(dr -> {
                String realPath = ruleEmitter.getRealPath(dr);
                mergeMap.put(realPath,0);
            });
        }

        //检查涉及到ModuleList中涉及的default rule
        //moduleList = getAllModuleList(ruleEmitter);
        String absPathTemp = path;
        if(absPathTemp.startsWith("\\")){
            absPathTemp = absPathTemp.replaceAll("\\\\","\\.");
        }
        else if(absPathTemp.startsWith(".")){

        }
        else{
            absPathTemp = "."+absPathTemp;
        }
        String absPath = absPathTemp;
        moduleList.forEach(iml->{
            if(iml.size() == 0){
                return;
            }
            IRuleEmitter emitter = getModuleListEmitter(iml.getDataClass());
            if(emitter == null){
                return;
            }
            List<String> subDefaultList = ModuleInfoManager.getDefaultRule(iml.getDataClass().getSimpleName().toLowerCase(), absPath);
            if(subDefaultList == null){
                return;
            }
            String moduleListPath = iml.getPath();
            String lowerModuleListPath = moduleListPath.substring(1).replaceAll("\\\\","\\.").toLowerCase();
            //构造ModuleList的default记录项目
            Map<String,Map<String,Integer>> subMergeQueueTemp = (Map<String,Map<String,Integer>>)mergeMap.get(lowerModuleListPath);
            if(subMergeQueueTemp == null){
                subMergeQueueTemp = new LinkedHashMap<>();
                mergeMap.put(lowerModuleListPath,subMergeQueueTemp);
            }
            //结束构建
            //记录要执行的default rule
            Map<String,Map<String,Integer>> subMergeQueue = subMergeQueueTemp;
            for(int i=1;i<=iml.size();i++){
                String index = String.valueOf(i);
                Map<String,Integer> subItemQueue = subMergeQueue.get(index);
                if(subItemQueue == null){
                    subItemQueue = new LinkedHashMap<>();
                    subMergeQueue.put(index,subItemQueue);
                }
                Map<String,Integer> subItemQueueTemp = subItemQueue;
                subDefaultList.forEach(dr->{
                    String realPath = emitter.getRealPath(dr);
                    subItemQueueTemp.put(realPath,0);
                });
            }

        });

    }
    public static void executeMergedQueue(IModule rootModule, IRuleEmitter emitter,Map<String,Object> mergeMap,Map<String,Object> scopeMergeMap,Stack<String> keyStack){
        scopeMergeMap.entrySet().forEach(entry -> {
            Integer one = new Integer(1);
            if (one.equals(entry.getValue())) {
                return;
            }
            //状态标记为0，或为ModuleList的项，继续执行
            String key = entry.getKey();
            cascadeExecute(key, entry.getValue(), rootModule, emitter, mergeMap, scopeMergeMap, keyStack);

        });
    }
    private static boolean isAbsPath(String path){
        return path.startsWith("\\") || path.startsWith(".");
    }
    private static void cascadeExecute(String path, Object flagOrDps, IModule rootModule, IRuleEmitter emitter,
                                       Map<String, Object> rootMergeMap, Map<String, Object> scopeMergeMap, Stack<String> keyStack){
        //当前Path，如果已在栈中存在，则退出。否则入栈
        String absPath = rootModule.getFieldPath(path);
        if(keyStack.contains(absPath)){
            return;
        }
        keyStack.push(absPath);
        //扫描依赖是否执行
        List<String> dependency = ModuleInfoManager.getDependency(rootModule.getClass().getSimpleName(),path);
        Integer executedFlag = 1;
        if(dependency != null) {
            dependency.forEach(dp -> {
                //执行栈如已包含，不继续执行
                //[i]表达式计算为list的path依赖
                int rowExprIdx = dp.indexOf("[i]");
                if(rowExprIdx > -1){
                  dp = dp.substring(0,rowExprIdx);
                }
                //是否为绝对路径，有不同的判断
                boolean isAbs = isAbsPath(dp);
                Object flag = null;
                if (isAbs) {
                    dp = MdaEnv.getContext().getEmitter().getRealPath(dp);
                    flag = rootMergeMap.get(dp);
                } else {
                    dp = emitter.getRealPath(dp);
                    flag = scopeMergeMap.get(dp);
                }
                if (executedFlag.equals(flag) || flag == null) {
                    return;
                }
                //该依赖还未执行完成，需要执行此依赖，继续递归深入
                if (isAbs) {
                    cascadeExecute(dp, flag, MdaEnv.getContext().getRoot(), MdaEnv.getContext().getEmitter(), rootMergeMap, rootMergeMap, keyStack);
                } else {
                    cascadeExecute(dp, flag, rootModule, emitter, rootMergeMap, scopeMergeMap, keyStack);
                }
            });
        }
        //判断完依赖，执行自己
        //1.标记当前key的状态为已执行
        scopeMergeMap.put(path, executedFlag);
        //2.正式执行Default Rule
        if(flagOrDps instanceof Integer) {
            if(isAbsPath(path)){
                MdaEnv.getContext().getEmitter().executeDefault(path);
            }else {
                emitter.executeDefault(path);
            }
            keyStack.pop();
            return;
        }
        //3.如果依赖moduleList，执行ModuleList里面的Default Rule
        //3.1查找该ModuleList的Emitter
        ModuleList moduleList = (ModuleList)MdaDriver.getBaseObject(null,path);
        AbstractScopeEmitter emitterOfModuleList = getModuleListEmitter(moduleList.getDataClass());
        if(emitter == null){
            keyStack.pop();
            return;
        }
        //3.2深入执行该ModuleList每一项的default rule
        Map<String,Map<String,Object>> subMergeQueue = (Map<String,Map<String,Object>>)flagOrDps;
        subMergeQueue.entrySet().forEach(entry->{
            String idxStr = entry.getKey();
            int num = Integer.parseInt(idxStr);
            if(num <= 0){
                return;
            }
            if(moduleList.size() <= 0){
                return;
            }
            if (moduleList == null || moduleList.isEmpty()){
                return;
            }
            IModule subRootModule = moduleList.getElement(num);
            //3.3设置上下文，以当前数据项为root点，执行Default
            emitterOfModuleList.acceptContext(subRootModule);
            executeMergedQueue(subRootModule,emitterOfModuleList,rootMergeMap,entry.getValue(),keyStack);
            emitterOfModuleList.removeContext();
        });
        //执行一下自己
        //emitter.executeDefault(path);
        keyStack.pop();
    }

    public static void notifyDefault(IModule module, String field) {
        if (!isEnable())
            return;
        String path = module.getFieldPath(field);
        int begPos = path.indexOf("(");
        if (begPos > 0) {
            int endPos = path.indexOf(")", begPos);
            String moduleListPath = path.substring(0, begPos);
            //处理前半段
            executeDefaultByPath(moduleListPath);
            //处理后半段
            //找到其父ModuleList实例
            IModule scopeModule = module.getParent();
            while(!(scopeModule instanceof IModuleList)){
                scopeModule = scopeModule.getParent();
            }
            //查找Emitter
            String moduleName = module.getClass().getSimpleName().toLowerCase();
            AbstractScopeEmitter emitter = getModuleListEmitter(module.getClass());
            if(emitter != null) {
                emitter.acceptContext(module);
                //执行模型内的Rule
                String substr = path.substring(endPos + 1);
                List<String> defaultList = ModuleInfoManager.getDefaultRule(moduleName, substr);
                if (defaultList != null)
                    defaultList.forEach(dr -> {
                        if (dr.startsWith(".")) {
                            MdaEnv.getContext().getEmitter().executeDefault(dr);
                        } else {
                            emitter.executeDefault(dr);
                        }
                    });
                emitter.removeContext();
            }
        }
        notifyDefault(path);
    }

    public static void enNotify(boolean flag) {
        if (flag) {
            MdaEnv.getContext().storeData(DCR_SWITCH_KEY, "0");
        } else {
            MdaEnv.getContext().storeData(DCR_SWITCH_KEY, "1");
        }
    }

    public static boolean getVoToTradeState() {
        Boolean state = voToTradeStateTl.get();
        if (state == null) return false;
        return state;
    }

    public static void setVoToTradeState(boolean state) {
        voToTradeStateTl.set(state);
    }


    public static boolean isEnable() {
        if (MdaEnv.getContext() == null) return false;
        Object val = MdaEnv.getContext().restoreData(DCR_SWITCH_KEY);
        return "0".equals(val);
    }

    public static class TaskCombo{
        public IModule module;
        public String field;
        public Object value;
        public Object srcValue;

        public TaskCombo(IModule module, String field) {
            this.module = module;
            this.field = field;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TaskCombo taskCombo = (TaskCombo) o;
            return Objects.equals(module, taskCombo.module) && Objects.equals(field, taskCombo.field);
        }

        @Override
        public int hashCode() {
            return Objects.hash(module, field);
        }

        @Override
        public String toString() {
            return "TaskCombo{" +
                    "module=" + module +
                    ", field='" + field + '\'' +
                    '}';
        }
    }
}
