package com.patzn.lims.workflow;

import com.patzn.cloud.commons.spring.SpringHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ProcessEngineConfiguration;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.image.exception.FlowableImageException;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;

import javax.imageio.ImageIO;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.List;

public class FlowableHelper {


    /**
     * <p>
     * 根据流程实例ID,获取实时流程图片
     * </p>
     *
     * @param processInstanceId 流程实例ID
     * @return
     */
    public static InputStream getFlowImgByInstantId(String processInstanceId) {
        if (StringUtils.isEmpty(processInstanceId)) {
            return null;
        }

        // 查询历史
        HistoryService historyService = SpringHelper.getBean(HistoryService.class);
        RepositoryService repositoryService = SpringHelper.getBean(RepositoryService.class);
        RuntimeService runtimeService = SpringHelper.getBean(RuntimeService.class);
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (historicProcessInstance.getEndTime() != null) {
            // 该流程已经结束
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(historicProcessInstance.getProcessDefinitionId()).singleResult();
            return repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getDiagramResourceName());
        }

        // 查询当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        ProcessEngineConfiguration processEngineConfiguration = SpringHelper.getBean(ProcessEngineConfiguration.class);
        ProcessDiagramGenerator diagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
        return diagramGenerator.generatePngDiagram(bpmnModel, true);
    }

    /**
     * <p>
     * BPMN 文件流转为 BpmnModel 对象
     * </p>
     *
     * @param bpmnStream BPMN 文件流转
     * @return
     */
    public BpmnModel convertToBpmnModel(InputStream bpmnStream) {
        XMLStreamReader xtr = null;
        try {
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
            xtr = xif.createXMLStreamReader(in);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (XMLStreamException e) {
            e.printStackTrace();
        }
        return new BpmnXMLConverter().convertToBpmnModel(xtr);
    }

    /**
     * <p>
     * 工作流执行实例 ProcessInstance 转换为 FlowProcess
     * </p>
     *
     * @param processInstance 工作流执行实例
     * @return
     */
    public static FlowProcess convertFlowProcess(ProcessInstance processInstance) {
        FlowProcess flowProcess = null;
        if (null != processInstance) {
            flowProcess = new FlowProcess();
            if (processInstance instanceof ExecutionEntityImpl) {
                List<ExecutionEntityImpl> entityList = ((ExecutionEntityImpl) processInstance).getExecutions();
                if (CollectionUtils.isNotEmpty(entityList)) {
                    Execution execution = entityList.get(0);
                    flowProcess.setId(execution.getId());
                }
            }
            flowProcess.setBusinessKey(processInstance.getBusinessKey());
            flowProcess.setSuspended(processInstance.isSuspended());
        }
        return flowProcess;
    }

    /**
     * <p>
     * 工作流任务 Task 转换为 FlowTask
     * </p>
     *
     * @param task 工作流任务
     * @return
     */
    public static FlowTask convertFlowTask(Task task) {
        FlowTask flowTask = null;
        if (null != task) {
            flowTask = new FlowTask();
            flowTask.setId(task.getId());
            flowTask.setName(task.getName());
            flowTask.setDescription(task.getDescription());
            flowTask.setPriority(task.getPriority());
            flowTask.setOwner(task.getOwner());
            flowTask.setAssignee(task.getAssignee());
            DelegationState delegationState = task.getDelegationState();
            if (null != delegationState) {
                flowTask.setDelegationState(delegationState.name());
            }
            flowTask.setDueDate(task.getDueDate());
            flowTask.setCategory(task.getCategory());
            flowTask.setParentTaskId(task.getParentTaskId());
            flowTask.setTenantId(task.getTenantId());
            flowTask.setFormKey(task.getFormKey());
            flowTask.setSuspended(task.isSuspended());
            flowTask.setProcessInstanceId(task.getProcessInstanceId());
            flowTask.setExecutionId(task.getExecutionId());
            flowTask.setProcessDefinitionId(task.getProcessDefinitionId());
            flowTask.setCreateTime(task.getCreateTime());
            flowTask.setTaskDefinitionKey(task.getTaskDefinitionKey());
            flowTask.setTaskLocalVariables(task.getTaskLocalVariables());
            flowTask.setProcessVariables(task.getProcessVariables());
            flowTask.setClaimTime(task.getClaimTime());
        }
        return flowTask;
    }


    public static BufferedImage createImage(BpmnModel bpmnModel, double scaleFactor) {
        ProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(scaleFactor);
        BufferedImage diagramImage = diagramGenerator.generatePngImage(bpmnModel, scaleFactor);
        return diagramImage;
    }


    public static byte[] createByteArrayForImage(BufferedImage image, String imageType) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, imageType, out);

        } catch (IOException e) {
            throw new FlowableImageException("Error while generating byte array for process image", e);
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ignore) {
                // Exception is silently ignored
            }
        }
        return out.toByteArray();
    }
}
