		
package com.sa.ibiz.demo.workflow;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.UserTask;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.util.io.StringStreamSource;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.entitylink.service.EntityLinkServiceConfiguration;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.ui.modeler.domain.Model;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSONObject;
import com.sa.ibiz.demo.ibizutil.SpringContextHolder;

@Service
public class WFServiceImpl {

	private Log log = LogFactory.getLog(WFServiceImpl.class);

	@Autowired
	private ProcessEngineConfiguration processEngineConfiguration;

	@Autowired
	private ModelService modelService;
		
	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private RuntimeService runtimeService;

	@Autowired
	private HistoryService historyService;
	
	@Autowired
	private TaskService taskService;

	/**
	 * 部署流程
	 * 
	 * @param inputStream
	 * @return
	 */
	public Deployment deployByModelid(String modelid) {
		try {
			 Model model = modelService.getModel(modelid);
			 BpmnModel bpmnModel = modelService.getBpmnModel(model);
			List<Process> processes = bpmnModel.getProcesses();
			Process curProcess = null;
			if (CollectionUtils.isEmpty(processes)) {
				log.error("BPMN模型没有配置流程");
				return null;
			}
			curProcess = processes.get(0);
			String strDeployName = System.currentTimeMillis()+".bpmn" ;

			List<SequenceFlow> sequenceFlows = curProcess.findFlowElementsOfType(SequenceFlow.class) ;
			for (SequenceFlow sequenceFlow : sequenceFlows) {
				if(curProcess.getFlowElement(sequenceFlow.getSourceRef()) instanceof UserTask) {
					sequenceFlow.setConditionExpression(String.format("${ACTION=='%1$s'}",sequenceFlow.getId()));
				}
			}

			Deployment deployment =  repositoryService.createDeployment().addBpmnModel(strDeployName, bpmnModel).deploy() ;
			log.debug("部署流程 name:" + curProcess.getName() + " key " + deployment.getKey() + " deploy " + deployment);
			return deployment;
//			return null ;
		} catch (Exception e) {
			log.error("BPMN模型创建流程异常", e);
			return null;
		} finally {
			
		}
	}
	
	/**
	 * 部署流程
	 * 
	 * @param inputStream
	 * @return
	 */
	public Deployment deploy(String workflowModel) {
		// 解析BPMN模型看是否成功
		try {
			BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter();
			StringStreamSource stringStreamSource = new StringStreamSource(workflowModel) ;
			BpmnModel model = bpmnXMLConverter.convertToBpmnModel(stringStreamSource,true,true) ;
			List<Process> processes = model.getProcesses();
			Process curProcess = null;
			if (CollectionUtils.isEmpty(processes)) {
				log.error("BPMN模型没有配置流程");
				return null;
			}
			
			String strDeployName = System.currentTimeMillis()+".bpmn" ;
			Deployment deployment = repositoryService.createDeployment().name(strDeployName).addString(strDeployName, workflowModel).deploy();
			
			curProcess = processes.get(0);
			log.debug("部署流程 name:" + curProcess.getName() + " key " + deployment.getKey() + " deploy " + deployment);
			return deployment ;
		} catch (Exception e) {
			log.error("BPMN模型创建流程异常", e);
			return null;
		} finally {
		
		}
	}
	
	/**
	 * 获取流程模型（最新）
	 * 
	 * @param workflowKey
	 * @param businessKey
	 * @return
	 */
	public BpmnModel getWorkflowModel(String workflowKey) {
		ProcessDefinition pde = repositoryService.createProcessDefinitionQuery().processDefinitionKey(workflowKey).latestVersion().singleResult();
		BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
		return bpmnModel ;
	}
	
	/**
	 * 获取流程模型指定版本
	 * 
	 * @param workflowKey
	 * @param version
	 * @return
	 */
	public BpmnModel getWorkflowModel(String workflowKey , int version) {
		ProcessDefinition pde = repositoryService.createProcessDefinitionQuery().processDefinitionKey(workflowKey).processDefinitionVersion(version).singleResult();
		BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
		return bpmnModel ;
	}

	/**
	 * 获取流程图最新）
	 * @param workflowKey
	 * @return
	 */
	public InputStream getWorkflowDiagram(String workflowKey) {
		ProcessDefinition pde = repositoryService.createProcessDefinitionQuery().processDefinitionKey(workflowKey).latestVersion().singleResult();
		if (pde != null && pde.hasGraphicalNotation()) {
			BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
			ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();

			InputStream resource = diagramGenerator.generateDiagram(bpmnModel, "png", Collections.<String>emptyList(),
					Collections.<String>emptyList(), processEngineConfiguration.getActivityFontName(),
					processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(),
					processEngineConfiguration.getClassLoader(), 1.0, true);

			return resource;

		} else {
			log.error(String.format("没有对应[%1$s]流程", workflowKey));
		}
		return null ;
	}
	
	/**
	 * 启动流程
	 * 
	 * @param initiator
	 *            发起人
	 * @param workflowKey
	 *            流程Key
	 * @param businessKey
	 *            业务数据标识
	 * @param vars
	 *            参数
	 * @return
	 */
	public ProcessInstance start(String initiator, String workflowKey, String businessKey, Map<String, Object> vars) {
		if (StringUtils.isEmpty(workflowKey)) {
			return null;
		}
		if (null == vars) {
			vars = new HashMap<>();
		}
		
		List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery().processDefinitionKey(workflowKey).processInstanceBusinessKey(businessKey).list() ;
		for (ProcessInstance processInstance : processInstanceList) {
			runtimeService.deleteProcessInstance(processInstance.getProcessInstanceId(), "重复提交流程");
		}
		Authentication.setAuthenticatedUserId(initiator);
		ProcessDefinition pde = repositoryService.createProcessDefinitionQuery().processDefinitionKey(workflowKey).latestVersion().singleResult();
		vars.put("srfworkflowkey", workflowKey) ;
		vars.put("srfworkflowversion", pde.getVersion()) ;
		vars.put("srfbusinesskey", businessKey) ;
		vars.put("srfinitiator", initiator) ;
		ProcessInstance ProcessInstance = runtimeService.startProcessInstanceById(pde.getId(), businessKey, vars);
		Authentication.setAuthenticatedUserId(null);
		return ProcessInstance;
	}
	
	/**
	 * 获取流程图（指定版本）
	 * @param workflowKey
	 * @param version
	 * @return
	 */
	public InputStream getWorkflowDiagram(String workflowKey,int version) {
		ProcessDefinition pde = repositoryService.createProcessDefinitionQuery().processDefinitionKey(workflowKey).processDefinitionVersion(version).singleResult();
		if (pde != null && pde.hasGraphicalNotation()) {
			BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
			ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();

			InputStream resource = diagramGenerator.generateDiagram(bpmnModel, "png", Collections.<String>emptyList(),
					Collections.<String>emptyList(), processEngineConfiguration.getActivityFontName(),
					processEngineConfiguration.getLabelFontName(), processEngineConfiguration.getAnnotationFontName(),
					processEngineConfiguration.getClassLoader(), 1.0, true);

			return resource;

		} else {
			log.error(String.format("没有对应[%1$s]流程", workflowKey));
		}
		return null ;
	}

	/**
	 * 获取流程实例运行图
	 * 
	 * @param workflowKey
	 * @param businessKey
	 * @return
	 */
	public InputStream getProcessInstanceDiagram(String workflowKey, String businessKey) {
		List<HistoricProcessInstance> historicProcessInstanceList = historyService.createHistoricProcessInstanceQuery()
				.processDefinitionKey(workflowKey).processInstanceBusinessKey(businessKey)
				.orderByProcessInstanceStartTime().desc().list();

		if (historicProcessInstanceList.size() > 0) {
			HistoricProcessInstance historicProcessInstance = historicProcessInstanceList.get(0);
			ProcessDefinition pde = repositoryService
					.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());

			if (pde != null && pde.hasGraphicalNotation()) {
				BpmnModel bpmnModel = repositoryService.getBpmnModel(pde.getId());
				ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();

				List<String> highLightedActivitis = new ArrayList<String>();
				List<String> flowActivitis = new ArrayList<String>();
				/**
				 * 获得活动的节点
				 */
				List<HistoricActivityInstance> highLightedActivitList = historyService
						.createHistoricActivityInstanceQuery().processInstanceId(historicProcessInstance.getId())
						.orderByHistoricActivityInstanceStartTime().asc().list();

				for (HistoricActivityInstance tempActivity : highLightedActivitList) {
					String activityId = tempActivity.getActivityId();
					if (tempActivity.getActivityType().equals("sequenceFlow")) {
						flowActivitis.add(activityId);
					} else {
						highLightedActivitis.add(activityId);
					}

				}

				InputStream resource = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitis,
						flowActivitis, processEngineConfiguration.getActivityFontName(),
						processEngineConfiguration.getLabelFontName(),
						processEngineConfiguration.getAnnotationFontName(), processEngineConfiguration.getClassLoader(),
						1.0, true);

				return resource;

			} else {
				throw new FlowableIllegalArgumentException("Process instance with id '"
						+ historicProcessInstance.getId() + "' has no graphical notation defined.");
			}
		} else {
			return null;
		}
	}

	/**
	 * 查找流程
	 * @param processDefinitionId
	 * @return
	 */	
	public ProcessDefinition findprocessDefinitionId(String processDefinitionId) {
		return repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
	}
	
	/**
	 * 查找实例
	 * @param instanceId
	 * @return
	 */
	public ProcessInstance findInstance(String instanceId) {
		return runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult() ;
	}

	/**
	 * 查找任务
	 * @param instanceId
	 * @return
	 */
	public Task findTask(String taskId) {
		return taskService.createTaskQuery().taskId(taskId).singleResult() ;
	}

	/**
	 * 查找用户任务
	 * @param instanceId
	 * @return
	 */
	public Task findTask(String userId , String taskId) {
		return taskService.createTaskQuery().taskId(taskId).taskCandidateUser(userId).singleResult() ;
	}

	/**
	 * 查询任务操作
	 * @param workflowKey
	 * @param businessKey
	 * @param user
	 * @return
	 */
	public List<SequenceFlow> findTaskLinkByUser(String workflowKey, String businessKey , String user) {
		List<SequenceFlow> links = new ArrayList<SequenceFlow>() ;
		List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
				.processDefinitionKey(workflowKey).processInstanceBusinessKey(businessKey).orderByStartTime()
				.desc().list();
		ProcessInstance processInstance = null;
		if (processInstanceList.size() > 0) {
			processInstance = processInstanceList.get(0);
		}
		if (processInstance != null) {
			List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(user).includeIdentityLinks()
					.processInstanceId(processInstance.getProcessInstanceId()).orderByTaskCreateTime().desc().list();
			if (taskList.size() > 0) {
				String strResourceTaskf = runtimeService.getActiveActivityIds(processInstance.getId()).get(0);
				List<SequenceFlow> flowElements = repositoryService
						.getBpmnModel(processInstance.getProcessDefinitionId()).getProcesses().get(0)
						.findFlowElementsOfType(SequenceFlow.class);
				for (SequenceFlow sequenceFlow : flowElements) {
					if (sequenceFlow.getSourceRef().equals(strResourceTaskf)) {
						links.add(sequenceFlow) ;
					}
				}
			}
		}
		return links ;
	}
	
	/**
	 * 查询待办任务
	 * @param workflowKey
	 * @param user
	 * @return
	 */
	public List<Task> findTaskByUser(String user) {
		List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(user).includeIdentityLinks().orderByTaskCreateTime().desc().list();
		
		return taskList ;
	}
	
	/**
	 * 查询待办任务
	 * @param workflowKey
	 * @param user
	 * @return
	 */
	public List<Task> findTaskByUser(String workflowKey, String user) {
		List<Task> taskList = taskService.createTaskQuery().processDefinitionKey(workflowKey).taskCandidateUser(user).includeIdentityLinks().orderByTaskCreateTime().desc().list();
		
		return taskList ;
	}
	
	public void execTask(String action , String workflowKey, String businessKey , String user , Map<String, Object> vars) {
		List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
				.processDefinitionKey(workflowKey).processInstanceBusinessKey(businessKey).orderByStartTime()
				.desc().list();
		ProcessInstance processInstance = null;
		if (processInstanceList.size() == 0) {
			throw new FlowableException("can't find processInstance.");
		}
		processInstance = processInstanceList.get(0);
		List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(user).includeIdentityLinks()
				.processInstanceId(processInstance.getProcessInstanceId()).orderByTaskCreateTime().desc().list();
		Task task = taskList.get(0);
		
		try {
			Authentication.setAuthenticatedUserId(user);
			if ("complete".equals(action)) {
				taskService.complete(task.getId(), vars);

			} else if ("claim".equals(action)) {
				taskService.claim(task.getId(), user);

			} else if ("delegate".equals(action)) {
				taskService.delegateTask(task.getId(), user);

			} else if ("resolve".equals(action)) {
				taskService.resolveTask(task.getId(), vars);

			} else {
				throw new FlowableIllegalArgumentException("Invalid action: '" + action + "'.");
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			Authentication.setAuthenticatedUserId(null);
		}
	}
	
	public void execComment(String action , String workflowKey, String businessKey , String user , Map<String, Object> vars) {
		List<ProcessInstance> processInstanceList = runtimeService.createProcessInstanceQuery()
				.processDefinitionKey(workflowKey).processInstanceBusinessKey(businessKey).orderByStartTime()
				.desc().list();
		ProcessInstance processInstance = null;
		if (processInstanceList.size() == 0) {
			throw new FlowableException("can't find processInstance.");
		}
		processInstance = processInstanceList.get(0);
		List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(user).includeIdentityLinks()
				.processInstanceId(processInstance.getProcessInstanceId()).orderByTaskCreateTime().desc().list();
		Task task = taskList.get(0);
		try {
			Authentication.setAuthenticatedUserId(user);
			Comment createdComment = taskService.addComment(task.getId(),processInstance.getId(),"");
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		} finally {
			Authentication.setAuthenticatedUserId(null);
		}
		
	}
	
}
