package com.jf.activiti.service.impl;

import com.jf.activiti.service.ProcessExtensionService;

import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;
import org.activiti.engine.impl.pvm.process.TransitionImpl;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.spring.ProcessEngineFactoryBean;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: wangbin
 * Date: 13-11-12
 * Time: 下午5:30
 * To change this template use File | Settings | File Templates.
 */
@Service("processExtensionService")
public class ProcessExtensionServiceImpl implements ProcessExtensionService {

    @Autowired
    protected RepositoryService repositoryService;

    @Autowired
    protected RuntimeService runtimeService;

    @Autowired
    protected TaskService taskService;

    @Autowired
    protected HistoryService historyService;

    @Autowired
    protected IdentityService identityService;

    @Autowired
    ProcessEngineFactoryBean processEngine;


    /**
     * 迭代循环流程树结构,查询当前节点可驳回的任务节点
     * @param taskId 当前任务ID
     * @param currActivity 当前活动节点
     * @param rtnList 存储回退节点集合
     * @param tempList 临时存储节点集合(存储一次迭代过程中的同级userTask节点)
     * @return 回退节点集合
     */
    public List<ActivityImpl> iteratorBackActivity(String taskId,ActivityImpl currActivity,
                                                   List<ActivityImpl> rtnList,List<ActivityImpl> tempList)throws Exception{
        //获取流程实例
        ProcessInstance processInstance =  findProcessInstanceByTaskId(taskId);

        //当前节点的流入来源
        List<PvmTransition> incomingTransitions = currActivity.getIncomingTransitions();
        //条件分支节点集合,userTask节点遍历完毕,迭代遍历此集合,查询条件分支对应的userTask节点
        List<ActivityImpl> exclusiveGateways = new ArrayList<ActivityImpl>();
        //并行节点集合,userTask节点遍历完毕,迭代遍历此集合,查询并行节点对应的userTask节点
        List<ActivityImpl> parallelGateways = new ArrayList<ActivityImpl>();

        //遍历当前节点所流入的路径
        for(PvmTransition pvmTransition : incomingTransitions){
            TransitionImpl transitionImpl = (TransitionImpl)pvmTransition;
            ActivityImpl activityImpl = transitionImpl.getSource();
            String type = (String)activityImpl.getProperty("type");

            if("parallelGateway".equals(type)){
                String gatewayId = activityImpl.getId();
                String gatewayType =gatewayId.substring(gatewayId.lastIndexOf("_")+1);
                if("START".equals(gatewayType.toUpperCase())){ //并行起点,停止递归
                    return rtnList;
                }
                else{
                    parallelGateways.add(activityImpl);
                }
            }
            else if("startEvent".equals(type)){
                return rtnList;
            } else if ("userTask".equals(type)) {// 用户任务
                tempList.add(activityImpl);
            }else if ("exclusiveGateway".equals(type)) {// 分支路线,临时存储此节点,本次循环结束,迭代集合,查询对应的userTask节点
                currActivity = transitionImpl.getSource();
                exclusiveGateways.add(currActivity);
            }
        }
        /**
         * 迭代条件分支集合,查询对应的userTask节点
         */
        for(ActivityImpl activityImpl : exclusiveGateways ){
            iteratorBackActivity(taskId,activityImpl,rtnList,tempList);
        }

        /**
         * 迭代并行集合,查询对应的userTask节点
         */
        for(ActivityImpl activityImpl:parallelGateways){
            iteratorBackActivity(taskId, currActivity, rtnList, tempList);
        }

        /**
         * 根据同级userTask集合,过滤最近发生的节点
         */
        currActivity = filterNewestActivity(processInstance, tempList);
        if(currActivity!=null){
           //查询当前节点的流向是否为并行终点,并获取并行起点ID
           String id = findParallelGatewayId(currActivity);
           if(StringUtils.isEmpty(id)){
               rtnList.add(currActivity);
           }
           else {
               currActivity = findActivitiImpl(taskId,id);
           }
           tempList.clear();
            iteratorBackActivity(taskId, currActivity, rtnList, tempList);
        }

        return rtnList;
    }


    /**
     * 驳回流程
     * @param taskId 当前任务ID
     * @param activityId 驳回节点ID
     * @param variables 流程存储参数
     * @throws Exception
     */
    public void backProcess(String taskId,String activityId,
                            Map<String,Object> variables)throws Exception{
       if(StringUtils.isEmpty(activityId)){
           throw new Exception("驳回目标节点ID为空!");
       }

       ProcessInstance processInstance =  findProcessInstanceByTaskId(taskId);

       String taskDefinitionKey = findTaskById(taskId).getTaskDefinitionKey();

       List<Task> taskList = findTaskListByKey(processInstance.getId(),taskDefinitionKey);

       for(Task task : taskList){
            commitProcess(task.getId(),variables,activityId);
       }

    }

    /**
     * @param taskId 当前任务ID
     * @param variables 流程变量
     * @param activityId 流程转向执行任务节点ID<br> 此参数为空,默认为提交操作
     * @throws Exception
     */
    private void commitProcess(String taskId,Map<String, Object> variables,
                               String activityId)throws Exception{
         if(variables==null){
             variables = new HashMap<String,Object>();
         }

         if(StringUtils.isEmpty(activityId)){
             taskService.complete(taskId);
         }
         else{
             turnTransition(taskId,activityId,variables);
         }

    }

    /**
     * 流程转向操作
     * @param taskId 当前任务ID
     * @param activityId 目标节点任务ID
     * @param variables  流程变量
     * @throws Exception
     */
    private void turnTransition(String taskId,String activityId,Map<String,Object> variables)throws Exception{
        //当前节点
        ActivityImpl currActivity = findActivitiImpl(taskId,null);
        //清空当前流向
        List<PvmTransition> oriPvmTransitionList =  clearTransition(currActivity);

        //创建新流向
        TransitionImpl newTransition = currActivity.createOutgoingTransition();

        //目标节点
        ActivityImpl pointActivity = findActivitiImpl(taskId,activityId);

        //设置新流向的目标节点
        newTransition.setDestination(pointActivity);

        //执行转向任务
        taskService.complete(taskId,variables);

        //删除目标节点新流入
        pointActivity.getIncomingTransitions().remove(newTransition);

    }

    /**
     * 还原指定活动节点流向
     * @param activityImpl 活动节点
     * @param oriPvmTransitionList 原有节点流向集合
     */
    private static void restoreTransition(ActivityImpl activityImpl,List<PvmTransition> oriPvmTransitionList){
        //清空现有流向
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();
        pvmTransitionList.clear();

        for(PvmTransition pvmTransition:oriPvmTransitionList){
            pvmTransitionList.add(pvmTransition);
        }
    }


    /**
     * 清空指定活动节点流向
     * @param activityImpl 活动节点
     * @return 节点流向集合
     */
    private List<PvmTransition> clearTransition(ActivityImpl activityImpl){

        //存储当前节点所有流向临时变量
        List<PvmTransition> oriPvmTransitionList = new ArrayList<PvmTransition>();

        //获取当前节点所有流向,存储到临时变量,然后清空
        List<PvmTransition> pvmTransitionList = activityImpl.getOutgoingTransitions();

        for(PvmTransition pvmTransition : pvmTransitionList){
            oriPvmTransitionList.add(pvmTransition);
        }
        pvmTransitionList.clear();

        return oriPvmTransitionList;
    }

    /**
     * 根据流程实例ID和任务key值查询所有同级任务集合
     * @param processInstanceId
     * @param key
     * @return
     */
    public List<Task> findTaskListByKey(String processInstanceId, String key){

        return taskService.createTaskQuery().processInstanceId(processInstanceId).taskDefinitionKey(key).list();
    }


    /**
     * 根据当前任务ID,查询可以驳回的任务节点
     * @param taskId
     * 当前任务ID
     */
    public List<ActivityImpl> findBackActivity(String taskId)throws Exception{

        List<ActivityImpl> rtnList = iteratorBackActivity(taskId, findActivitiImpl(taskId,
                null), new ArrayList<ActivityImpl>(),
                new ArrayList<ActivityImpl>());
        return reverList(rtnList);

    }

    /**
     * 反向排序list集合,便于驳回节点按顺序显示
     * @param list
     * @return
     */
    public List<ActivityImpl>  reverList(List<ActivityImpl> list){

        List<ActivityImpl> rtnList = new ArrayList<ActivityImpl>();
        // 由于迭代出现重复数据,排除重复
        for(int i = list.size();i>0;i--){
            if (!rtnList.contains(list.get(i - 1)))
                rtnList.add(list.get(i - 1));
        }
        return rtnList;
    }

    /**
     * 根据任务ID和节点ID获取活动节点 <br>
     * @param taskId
     *            任务ID
     * @param activityId
     *            活动节点ID <br>
     *            如果为null或"",则默认查询当前活动节点 <br>
     *            如果为"end",则查询结束节点 <br>
     *
     * @return
     * @throws Exception
     */
    private ActivityImpl findActivitiImpl(String taskId,String activityId)throws Exception{

        //取得流程定义
        ProcessDefinitionEntity processDefinition = findProcessDefinitionEntityByTaskId(taskId);

        if(StringUtils.isEmpty(activityId)){
            activityId = findTaskById(taskId).getTaskDefinitionKey();
        }

        if(activityId.toUpperCase().equals("END")){
            for(ActivityImpl activityImpl : processDefinition.getActivities()){
                List<PvmTransition> pvmTransitionList =activityImpl.getOutgoingTransitions();
                if(pvmTransitionList.isEmpty()){
                    return activityImpl;
                }
            }
        }

        //根据节点ID,获取对应的活动节点
        ActivityImpl activityImpl = ((ProcessDefinitionImpl) processDefinition)
                .findActivity(activityId);

        return activityImpl;
    }

    /**
     * 根据任务ID获取任务实例
     * @param taskId
     * @return
     * @throws Exception
     */
    private TaskEntity findTaskById(String taskId)throws Exception{
        TaskEntity task = (TaskEntity)taskService.createTaskQuery().taskId(taskId).singleResult();

        if(task==null){
            throw new Exception("任务实例未找到");
        }

        return task;
    }

    /**
     * 根据任务ID获取流程定义
     * @param taskId 任务ID
     * @return
     * @throws Exception
     */
    public ProcessDefinitionEntity findProcessDefinitionEntityByTaskId(String taskId)throws Exception{

         ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity)((RepositoryServiceImpl) repositoryService).getDeployedProcessDefinition(findTaskById(taskId).getProcessDefinitionId());

         if(processDefinition == null){
             throw new Exception("流程定义未找到!");
         }
         return processDefinition;
    }

    /**
     * 根据当前节点,查询输出流向是否为并行终点,如果为并行终点,则拼装对应的并行起点ID
     * @param activityImpl   当前节点
     * @return
     */
    private String findParallelGatewayId(ActivityImpl activityImpl){
        List<PvmTransition> incomingTransitions = activityImpl.getOutgoingTransitions();

        for(PvmTransition pvmTransition : incomingTransitions){
            TransitionImpl transitionImpl = (TransitionImpl)pvmTransition;
            activityImpl = transitionImpl.getDestination();
            String type = (String)activityImpl.getProperty("type");
            if("parallelGateway".equals(type)){ //并行路线
                String gatewayId = activityImpl.getId();
                String gettewayType = gatewayId.substring(gatewayId.lastIndexOf("_")+1);
                if("END".equals(gettewayType.toUpperCase())){
                    return gatewayId.substring(0, gatewayId.lastIndexOf("_"))+"_start";
                }
            }
        }

        return null;
    }

    /**
     *  查询某个用户ID是否在某个Activity里面拥有权限
     * @param taskId    任务ID
     * @param userId    用户ID
     * @return
     */
    public boolean isPermissionInActivity(String taskId,String userId){

        List<IdentityLink> identityLinks = taskService.getIdentityLinksForTask(taskId);

        for(IdentityLink identityLink : identityLinks){

            //查询
            if("candidate".equals(identityLink.getType())){
                if(null==identityLink.getGroupId()){
                    if(identityLink.getUserId().equals(userId)){
                           return true;
                    }
                }
                //如果该任务执行权限为组,则匹配指派人toSign的组是否一致
                else{
                    List<Group> groupList =  identityService.createGroupQuery().groupMember(userId).list();
                    for(Group g : groupList){
                        if(g.getId().equals(identityLink.getGroupId())){
                            return true;
                        }
                    }
                }

            }
        }

          return false;
    }


    private ActivityImpl filterNewestActivity(ProcessInstance processInstance,
                                              List<ActivityImpl> tempList){
        while (tempList.size()>0){
            ActivityImpl activity_1 = tempList.get(0);
            HistoricActivityInstance activityInstance_1 = findHistoricUserTask(processInstance,activity_1.getId());

            if(activityInstance_1==null){
                tempList.remove(activity_1);
                continue;
            }

            if(tempList.size()>1){
                ActivityImpl activity_2 = tempList.get(1);
                HistoricActivityInstance activityInstance_2 = findHistoricUserTask(processInstance,activity_2.getId());

                if(activityInstance_2==null){
                    tempList.remove(activity_2);
                    continue;
                }

                if(activityInstance_1.getEndTime().before(activityInstance_2.getEndTime())){
                    tempList.remove(activity_1);
                }else {
                    tempList.remove(activity_2);
                }
            }else {
                break;
            }
        }

        if(tempList.size()>0){
            return  tempList.get(0);
        }
        return null;
    }



    /**
     * 查询指定任务节点的最新记录
     * @param processInstance 流程实例
     * @param activityId
     * @return
     */
     private HistoricActivityInstance findHistoricUserTask(ProcessInstance processInstance,
                                                           String activityId){
         HistoricActivityInstance rtnVal = null;
         //查询当前流程实例审批结束的历史节点
         List<HistoricActivityInstance> historicActivityInstances =historyService
                 .createHistoricActivityInstanceQuery().activityType("userTask")
                 .processInstanceId(processInstance.getId()).activityId(activityId)
                 .finished().orderByHistoricActivityInstanceEndTime().desc().list();

         if(historicActivityInstances.size()>0){
             rtnVal = historicActivityInstances.get(0);
         }

         return rtnVal;
     }


    /**
     * 获取流程实例
     * @param taskId
     * @return
     */
    public ProcessInstance findProcessInstanceByTaskId(String taskId){
        TaskEntity task = (TaskEntity)taskService.createTaskQuery().taskId(taskId).singleResult();

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();

        if(processInstance==null){

            throw new RuntimeException("流程实例未找到");
        }

        return processInstance;
    }
}