package com.artfess.activiti.cache;

import com.artfess.base.cache.annotation.CacheEvict;
import com.artfess.base.cache.annotation.CachePut;
import com.artfess.base.cache.annotation.Cacheable;
import com.artfess.base.constants.CacheKeyConst;
import com.artfess.base.exception.WorkFlowException;
import com.artfess.base.util.AppUtil;
import com.artfess.base.util.ContextThread;
import com.artfess.base.util.FileUtil;
import org.activiti.engine.impl.persistence.deploy.DeploymentCache;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.apache.commons.lang.exception.ExceptionUtils;

import java.util.HashMap;
import java.util.Map;

/**
 * 流程引擎的自定义缓存接口，可以配置到activiti的配置文件中
 *
 * @author heyifan
 * @company 广州宏天软件股份有限公司
 * @email heyf@jee-soft.cn
 * @date 2020年6月17日
 */
public class ActivitiDefCache implements DeploymentCache<ProcessDefinitionEntity>, ContextThread {
    @Override
    public ProcessDefinitionEntity get(String id) {
        //首先从线程变量中获取
        ProcessDefinitionEntity p = getThreadLocalDef(id);
        if (p == null) {
            ActivitiDefCache bean = AppUtil.getBean(getClass());
            // 获取缓存数据时，必须通过Spring容器中获取到的bean来调用（否则缓存方法上的注解不会生效），而且缓存方法只能是protected后public方法
            ProcessDefinitionEntity ent = bean.getFromCache(id);
            if (ent == null) {
                return null;
            }
            setThreadLocalDef(ent);
            p = getThreadLocalDef(id);
        }
        return p;
    }

    @Override
    public void add(String id, ProcessDefinitionEntity object) {
        ActivitiDefCache bean = AppUtil.getBean(getClass());
        // 获取缓存数据时，必须通过Spring容器中获取到的bean来调用（否则缓存方法上的注解不会生效），而且缓存方法只能是protected后public方法
        bean.putInCache(id, object);
        setThreadLocalDef(object);
    }

    @Cacheable(value = CacheKeyConst.EIP_BPM_PROCESS_ENTITY, key = "#id", ignoreException = false)
    public ProcessDefinitionEntity getFromCache(String id) {
        return null;
    }

    @CachePut(value = CacheKeyConst.EIP_BPM_PROCESS_ENTITY, key = "#id", ignoreException = false)
    public ProcessDefinitionEntity putInCache(String id, ProcessDefinitionEntity object) {
        return object;
    }

    @Override
    @CacheEvict(value = CacheKeyConst.EIP_BPM_PROCESS_ENTITY, key = "#id", ignoreException = false)
    public void remove(String id) {
        clearProcessDefinitionEntity(id);
    }

    @Override
    @CacheEvict(value = CacheKeyConst.EIP_BPM_PROCESS_ENTITY, allEntries = true, ignoreException = false)
    public void clear() {
        clearProcessCache();
    }

    public void cleanAll() {
        clearProcessCache();
    }

    private ThreadLocal<Map<String, ProcessDefinitionEntity>> processDefinitionCacheLocal = new ThreadLocal<Map<String, ProcessDefinitionEntity>>();

    private void clearProcessDefinitionEntity(String defId) {
        processDefinitionCacheLocal.remove();
    }

    private void clearProcessCache() {
        processDefinitionCacheLocal.remove();
    }

    /**
     * 把传入的对象克隆一份，放入线程变量中。驳回/指定节点跳转会用到
     *
     * @param process
     */
    private void setThreadLocalDef(ProcessDefinitionEntity process) {
        ProcessDefinitionEntity cloneEnt = null;
        try {
            //克隆流程定义。
            cloneEnt = (ProcessDefinitionEntity) FileUtil.cloneObject(process);
        } catch (Exception e) {
            throw new WorkFlowException(ExceptionUtils.getRootCauseMessage(e));
        }
        if (processDefinitionCacheLocal.get() == null) {
            Map<String, ProcessDefinitionEntity> map = new HashMap<String, ProcessDefinitionEntity>();
            map.put(cloneEnt.getId(), cloneEnt);
            processDefinitionCacheLocal.set(map);
        } else {
            Map<String, ProcessDefinitionEntity> map = processDefinitionCacheLocal.get();
            map.put(cloneEnt.getId(), cloneEnt);
        }
    }

    private ProcessDefinitionEntity getThreadLocalDef(String processDefinitionId) {
        if (processDefinitionCacheLocal.get() == null) {
            return null;
        }
        Map<String, ProcessDefinitionEntity> map = processDefinitionCacheLocal.get();
        if (!map.containsKey(processDefinitionId)) {
            return null;
        }
        return map.get(processDefinitionId);
    }
}
