package com.mossle.bpm.cmd;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.activiti.engine.ActivitiException;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.impl.interceptor.CommandContext;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.process.ProcessDefinitionImpl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CounterSignCmd implements Command<Object> {
    private static Logger log = LoggerFactory.getLogger(CounterSignCmd.class);
    private String operateType;
    private String activityId;
    private String assignee;
    private String processInstanceId;
    private String collectionVariableName;
    private String collectionElementVariableName;
    private CommandContext commandContext;
    private String taskId;

    public CounterSignCmd(String operateType, String assignee, String taskId) {
        this.operateType = operateType;
        this.assignee = assignee;
        this.taskId = taskId;
    }

    /**
     * @param operateType
     *            操作类型 add or remove
     * @param activityId
     *            节点ID
     * @param assignee
     *            人员代码
     * @param processInstanceId
     *            流程实例ID
     * @param collectionVariableName
     *            collection 设置的变量名
     * @param collectionElementVariableName
     *            collection 的每个元素变量名
     */
    public CounterSignCmd(final String operateType, final String activityId,
            final String assignee, final String processInstanceId,
            final String collectionVariableName,
            final String collectionElementVariableName) {
        this.operateType = operateType;
        this.activityId = activityId;
        this.assignee = assignee;
        this.processInstanceId = processInstanceId;
        this.collectionVariableName = collectionVariableName;
        this.collectionElementVariableName = collectionElementVariableName;
    }

    public Object execute(CommandContext commandContext) {
        this.commandContext = commandContext;

        if (this.taskId != null) {
            TaskEntity taskEntity = commandContext.getTaskEntityManager()
                    .findTaskById(taskId);
            activityId = taskEntity.getExecution().getActivityId();
            processInstanceId = taskEntity.getProcessInstanceId();
            this.collectionVariableName = "countersignUsers";
            this.collectionElementVariableName = "countersignUser";
        }

        if (operateType.equalsIgnoreCase("add")) {
            addInstance();
        } else if (operateType.equalsIgnoreCase("remove")) {
            removeInstance();
        }

        return null;
    }

    /**
     * 加签
     */
    public void addInstance() {
        if (isParallel()) {
            addParallelInstance();
        } else {
            addSequentialInstance();
        }
    }

    /**
     * 减签
     */
    public void removeInstance() {
        if (isParallel()) {
            removeParallelInstance();
        } else {
            removeSequentialInstance();
        }
    }

    /**
     * <li>添加一条并行实例
     */
    private void addParallelInstance() {
        ExecutionEntity parentExecutionEntity = commandContext
                .getExecutionEntityManager()
                .findExecutionById(processInstanceId).findExecution(activityId);
        ActivityImpl activity = getActivity();
        ExecutionEntity execution = parentExecutionEntity.createExecution();
        execution.setActive(true);
        execution.setConcurrent(true);
        execution.setScope(false);

        if (getActivity().getProperty("type").equals("subProcess")) {
            ExecutionEntity extraScopedExecution = execution.createExecution();
            extraScopedExecution.setActive(true);
            extraScopedExecution.setConcurrent(false);
            extraScopedExecution.setScope(true);
            execution = extraScopedExecution;
        }

        setLoopVariable(parentExecutionEntity, "nrOfInstances",
                (Integer) parentExecutionEntity
                        .getVariableLocal("nrOfInstances") + 1);
        setLoopVariable(parentExecutionEntity, "nrOfActiveInstances",
                (Integer) parentExecutionEntity
                        .getVariableLocal("nrOfActiveInstances") + 1);
        setLoopVariable(execution, "loopCounter", parentExecutionEntity
                .getExecutions().size() + 1);
        setLoopVariable(execution, collectionElementVariableName, assignee);
        execution.executeActivity(activity);
    }

    /**
     * <li>给串行实例集合中添加一个审批人
     */
    private void addSequentialInstance() {
        ExecutionEntity execution = getActivieExecutions().get(0);

        if (getActivity().getProperty("type").equals("subProcess")) {
            if (!execution.isActive()
                    && execution.isEnded()
                    && ((execution.getExecutions() == null) || (execution
                            .getExecutions().size() == 0))) {
                execution.setActive(true);
            }
        }

        Collection<String> col = (Collection<String>) execution
                .getVariable(collectionVariableName);
        col.add(assignee);
        execution.setVariable(collectionVariableName, col);
        setLoopVariable(execution, "nrOfInstances",
                (Integer) execution.getVariableLocal("nrOfInstances") + 1);
    }

    /**
     * <li>移除一条并行实例
     */
    private void removeParallelInstance() {
        List<ExecutionEntity> executions = getActivieExecutions();

        for (ExecutionEntity executionEntity : executions) {
            String executionVariableAssignee = (String) executionEntity
                    .getVariableLocal(collectionElementVariableName);

            if ((executionVariableAssignee != null)
                    && executionVariableAssignee.equals(assignee)) {
                executionEntity.remove();

                ExecutionEntity parentConcurrentExecution = executionEntity
                        .getParent();

                if (getActivity().getProperty("type").equals("subProcess")) {
                    parentConcurrentExecution = parentConcurrentExecution
                            .getParent();
                }

                setLoopVariable(parentConcurrentExecution, "nrOfInstances",
                        (Integer) parentConcurrentExecution
                                .getVariableLocal("nrOfInstances") - 1);
                setLoopVariable(parentConcurrentExecution,
                        "nrOfActiveInstances",
                        (Integer) parentConcurrentExecution
                                .getVariableLocal("nrOfActiveInstances") - 1);

                break;
            }
        }
    }

    /**
     * 删除串行列表中移除未完成的用户(当前执行的用户无法移除)
     */
    private void removeSequentialInstance() {
        ExecutionEntity executionEntity = getActivieExecutions().get(0);
        Collection<String> col = (Collection<String>) executionEntity
                .getVariable(collectionVariableName);
        log.info("移除前审批列表 : {}", col.toString());
        col.remove(assignee);
        executionEntity.setVariable(collectionVariableName, col);
        setLoopVariable(executionEntity, "nrOfInstances",
                (Integer) executionEntity.getVariableLocal("nrOfInstances") - 1);

        // 如果串行要删除的人是当前active执行,
        if (executionEntity.getVariableLocal(collectionElementVariableName)
                .equals(assignee)) {
            throw new ActivitiException("当前正在执行的实例,无法移除!");
        }

        log.info("移除后审批列表 : {}", col.toString());
    }

    /**
     * 获取活动的执行 , 子流程的活动执行是其孩子执行(并行多实例情况下) 
     * 串行情况下获取的结果数量为1
     */
    protected List<ExecutionEntity> getActivieExecutions() {
        List<ExecutionEntity> activeExecutions = new ArrayList<ExecutionEntity>();
        ActivityImpl activity = getActivity();
        List<ExecutionEntity> executions = getChildExecutionByProcessInstanceId();

        for (ExecutionEntity execution : executions) {
            if (execution.isActive()
                    && (execution.getActivityId().equals(activityId) || activity
                            .contains(execution.getActivity()))) {
                activeExecutions.add(execution);
            }
        }

        return activeExecutions;
    }

    /**
     * 获取流程实例根的所有子执行
     */
    protected List<ExecutionEntity> getChildExecutionByProcessInstanceId() {
        return commandContext.getExecutionEntityManager()
                .findChildExecutionsByProcessInstanceId(processInstanceId);
    }

    /**
     * 返回当前节点对象
     */
    protected ActivityImpl getActivity() {
        return this.getProcessDefinition().findActivity(activityId);
    }

    /**
     * 判断节点多实例类型是否是并发
     */
    protected boolean isParallel() {
        return getActivity().getProperty("multiInstance").equals("parallel");
    }

    /**
     * 返回流程定义对象
     */
    protected ProcessDefinitionImpl getProcessDefinition() {
        return this.getProcessInstanceEntity().getProcessDefinition();
    }

    /**
     * 返回流程实例的根执行对象
     */
    protected ExecutionEntity getProcessInstanceEntity() {
        return commandContext.getExecutionEntityManager().findExecutionById(
                processInstanceId);
    }

    /**
     * 添加本地变量
     */
    protected void setLoopVariable(ActivityExecution execution,
            String variableName, Object value) {
        execution.setVariableLocal(variableName, value);
    }
}
