package com.ho.flow.service.process.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.ho.common.tools.service.RedisService;
import com.ho.common.tools.util.PageResult;
import com.ho.common.tools.util.PageUtils;
import com.ho.flow.entity.process.ActReDeploymentVO;
import com.ho.flow.entity.process.DefinitionIdDTO;
import com.ho.flow.entity.process.ProcessDefinitionDTO;
import com.ho.flow.mapper.ActReDeploymentMapper;
import com.ho.flow.service.process.service.IProcessDefinitionService;
import com.ho.flow.util.XmlToJsonUtil;
import com.ho.flow.vo.XmlBean;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 *
 * 创建日期:2020/10/23-9:52
 * 版本   开发者     日期
 * 1.0    Danny    2020/10/23
 */

@Service
public class ProcessDefinitionServiceImpl implements IProcessDefinitionService {
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ActReDeploymentMapper actReDeploymentMapper;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private RuntimeService runtimeService;

    @Override
    public PageResult<ProcessDefinitionDTO> selectProcessDefinitionList(ProcessDefinitionDTO processDefinition) {
        PageResult pageResult = new PageResult<>();
        Page<ProcessDefinitionDTO> list = new Page<>();
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery().orderByProcessDefinitionId().orderByProcessDefinitionVersion().desc();
        if (StringUtils.isNotBlank(processDefinition.getName())) {
            processDefinitionQuery.processDefinitionNameLike("%" + processDefinition.getName() + "%");
        }
        if (StringUtils.isNotBlank(processDefinition.getKey())) {
            processDefinitionQuery.processDefinitionKeyLike("%" + processDefinition.getKey() + "%");
        }
        List<ProcessDefinition> processDefinitions = processDefinitionQuery.processDefinitionTenantId(processDefinition.getTenantId()).listPage((processDefinition.getPageNum() - 1) * processDefinition.getPageSize(), processDefinition.getPageSize());
        long count = processDefinitionQuery.count();
        list.setTotal(count);
        if (count!=0) {
            Set<String> ids = processDefinitions.parallelStream().map(pdl -> pdl.getDeploymentId()).collect(Collectors.toSet());
            List<ActReDeploymentVO> actReDeploymentVOS = actReDeploymentMapper.selectActReDeploymentByIds(ids);
            List<ProcessDefinitionDTO> processDefinitionDTOS = processDefinitions.stream()
                    .map(pd -> new ProcessDefinitionDTO((ProcessDefinitionEntityImpl) pd, actReDeploymentVOS.parallelStream().filter(ard -> pd.getDeploymentId().equals(ard.getId())).findAny().orElse(new ActReDeploymentVO())))
                    .collect(Collectors.toList());
            list.addAll(processDefinitionDTOS);
        }
        pageResult = PageUtils.getPageResult(new PageInfo<>(list));
        pageResult.setPageNum(processDefinition.getPageNum());
        pageResult.setPageSize(processDefinition.getPageSize());
        pageResult.setTotalRows(count);
        pageResult.setTotalPages((int) (count / processDefinition.getPageSize() + 1));
        return pageResult;
    }

    @Override
    public DefinitionIdDTO getDefinitionsByInstanceId(String instanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
        String deploymentId = processInstance.getDeploymentId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        return new DefinitionIdDTO(processDefinition);
    }

    @Override
    public int deleteProcessDefinitionById(String id) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(id)
                .singleResult();
        if (redisService.hasKey(processDefinition.getKey())){
            redisService.delete(processDefinition.getKey());
        }
        repositoryService.deleteDeployment(id, true);
        return 1;
    }

    @Override
    public void uploadStreamAndDeployment(MultipartFile file) throws IOException {
        // 获取上传的文件名
        String fileName = file.getOriginalFilename();
        // 得到输入流（字节流）对象
        InputStream fileInputStream = file.getInputStream();
        // 文件的扩展名
        String extension = FilenameUtils.getExtension(fileName);

        if (extension.equals("zip")) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            repositoryService.createDeployment()//初始化流程
                    .addZipInputStream(zip)
                    .deploy();
        } else {
            repositoryService.createDeployment()//初始化流程
                    .addInputStream(fileName, fileInputStream)
                    .deploy();
        }
    }

    @Override
    public void suspendOrActiveApply(String id, Integer suspendState) {
        if (1==suspendState) {
            // 当流程定义被挂起时，已经发起的该流程定义的流程实例不受影响（如果选择级联挂起则流程实例也会被挂起）。
            // 当流程定义被挂起时，无法发起新的该流程定义的流程实例。
            // 直观变化：act_re_procdef 的 SUSPENSION_STATE_ 为 2
            repositoryService.activateProcessDefinitionById(id);
        } else if (2==suspendState) {
            repositoryService.suspendProcessDefinitionById(id);
        }
    }
/*    @Override
    public String upload(MultipartFile multipartFile) throws IOException {
        R<SysFile> sysFileR =  remoteFileService.upload(multipartFile);
       return sysFileR.getData().getUrl();
//       return FileUploadUtils.upload(RuoYiConfig.getUploadPath()+"/processDefinition" , multipartFile);
    }*/

    @Autowired
    RedisService redisService;

    @Override
    public void addDeploymentByString(String stringBPMN,String tenantId) {
        Deployment deploy = repositoryService.createDeployment()
                .addString("CreateWithBPMNJS.bpmn", stringBPMN)
                .tenantId(tenantId)
                .deploy();
        //获取第一个任务节点的设置岗位
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId())
                .singleResult();
        //Map<String, String> taskUsers = XmlToJsonUtil.getTaskUsers(stringBPMN);
        //String postId = taskUsers.get("candidateGroups");
        //redisService.set(processDefinition.getKey(),postId);
        //Map<String,List<String>> map = new HashMap<>();
        //List<String> one = new ArrayList<>();
        //map.put("Activity_02mwyun",one);
        //List<String> two = new ArrayList<>();
        //two.add("Activity_02mwyun");
        //map.put("Activity_1fqvk3w",two);
        //List<String> three = new ArrayList<>();
        //three.add("Activity_02mwyun");
        //three.add("Activity_1fqvk3w");
        //map.put("Activity_1re1on4",three);
        //redisService.set(processDefinition.getKey()+"his",map);
        redisService.set(processDefinition.getKey(),stringBPMN);

        Map<String, Map<String, String>> allTaskUsers = XmlToJsonUtil.getAllTaskUsers(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.ALL_USER,allTaskUsers);

        Map<String, String> taskUsers = XmlToJsonUtil.getTaskUsers(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.FIRST_USER,taskUsers);

        LinkedList<String> preTaskNodes = XmlToJsonUtil.getPreTaskNodes(stringBPMN, "");
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.PRE_BACK,preTaskNodes);

        List<XmlBean> otherNodeType = XmlToJsonUtil.getOtherNodeType(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.AS_NODE,otherNodeType);

    }

    @Override
    public void getProcessDefineXML(HttpServletResponse response, String deploymentId, String resourceName) throws IOException {
        InputStream inputStream = repositoryService.getResourceAsStream(deploymentId, resourceName);
        int count = inputStream.available();
        byte[] bytes = new byte[count];
        response.setContentType("text/xml");
        OutputStream outputStream = response.getOutputStream();
        while (inputStream.read(bytes) != -1) {
            outputStream.write(bytes);
        }
        inputStream.close();
    }

    @Override
    public void initDeploymentByString() {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId("72501")
                .singleResult();
        String stringBPMN = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<bpmn2:definitions xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:bpmn2=\"http://www.omg.org/spec/BPMN/20100524/MODEL\" xmlns:bpmndi=\"http://www.omg.org/spec/BPMN/20100524/DI\" xmlns:dc=\"http://www.omg.org/spec/DD/20100524/DC\" xmlns:di=\"http://www.omg.org/spec/DD/20100524/DI\" xmlns:activiti=\"http://activiti.org/bpmn\" id=\"sample-diagram\" targetNamespace=\"http://activiti.org/bpmn\" xsi:schemaLocation=\"http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd\">\n  <bpmn2:process id=\"Process_approve\" isExecutable=\"true\">\n    <bpmn2:startEvent id=\"StartEvent_1\">\n      <bpmn2:outgoing>Flow_0gr15at</bpmn2:outgoing>\n    </bpmn2:startEvent>\n    <bpmn2:userTask id=\"Activity_1ygdu8u\" name=\"处理工单\" activiti:candidateGroups=\"44\">\n      <bpmn2:incoming>Flow_0gr15at</bpmn2:incoming>\n      <bpmn2:outgoing>Flow_0abyi27</bpmn2:outgoing>\n    </bpmn2:userTask>\n    <bpmn2:sequenceFlow id=\"Flow_0gr15at\" sourceRef=\"StartEvent_1\" targetRef=\"Activity_1ygdu8u\" />\n    <bpmn2:userTask id=\"Activity_1kdd4k8\" name=\"领导审批\" activiti:candidateGroups=\"34\">\n      <bpmn2:incoming>Flow_0abyi27</bpmn2:incoming>\n      <bpmn2:outgoing>Flow_02izal9</bpmn2:outgoing>\n    </bpmn2:userTask>\n    <bpmn2:sequenceFlow id=\"Flow_0abyi27\" sourceRef=\"Activity_1ygdu8u\" targetRef=\"Activity_1kdd4k8\" />\n    <bpmn2:endEvent id=\"Event_0ptvnic\">\n      <bpmn2:incoming>Flow_02izal9</bpmn2:incoming>\n    </bpmn2:endEvent>\n    <bpmn2:sequenceFlow id=\"Flow_02izal9\" sourceRef=\"Activity_1kdd4k8\" targetRef=\"Event_0ptvnic\" />\n  </bpmn2:process>\n  <bpmndi:BPMNDiagram id=\"BPMNDiagram_1\">\n    <bpmndi:BPMNPlane id=\"BPMNPlane_1\" bpmnElement=\"Process_approve\">\n      <bpmndi:BPMNEdge id=\"Flow_0gr15at_di\" bpmnElement=\"Flow_0gr15at\">\n        <di:waypoint x=\"448\" y=\"258\" />\n        <di:waypoint x=\"500\" y=\"258\" />\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNEdge id=\"Flow_0abyi27_di\" bpmnElement=\"Flow_0abyi27\">\n        <di:waypoint x=\"600\" y=\"258\" />\n        <di:waypoint x=\"660\" y=\"258\" />\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNEdge id=\"Flow_02izal9_di\" bpmnElement=\"Flow_02izal9\">\n        <di:waypoint x=\"760\" y=\"258\" />\n        <di:waypoint x=\"822\" y=\"258\" />\n      </bpmndi:BPMNEdge>\n      <bpmndi:BPMNShape id=\"_BPMNShape_StartEvent_2\" bpmnElement=\"StartEvent_1\">\n        <dc:Bounds x=\"412\" y=\"240\" width=\"36\" height=\"36\" />\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape id=\"Activity_1ygdu8u_di\" bpmnElement=\"Activity_1ygdu8u\">\n        <dc:Bounds x=\"500\" y=\"218\" width=\"100\" height=\"80\" />\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape id=\"Activity_1kdd4k8_di\" bpmnElement=\"Activity_1kdd4k8\">\n        <dc:Bounds x=\"660\" y=\"218\" width=\"100\" height=\"80\" />\n      </bpmndi:BPMNShape>\n      <bpmndi:BPMNShape id=\"Event_0ptvnic_di\" bpmnElement=\"Event_0ptvnic\">\n        <dc:Bounds x=\"822\" y=\"240\" width=\"36\" height=\"36\" />\n      </bpmndi:BPMNShape>\n    </bpmndi:BPMNPlane>\n  </bpmndi:BPMNDiagram>\n</bpmn2:definitions>\n";
        redisService.set(processDefinition.getKey(),stringBPMN);

        Map<String, Map<String, String>> allTaskUsers = XmlToJsonUtil.getAllTaskUsers(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.ALL_USER,allTaskUsers);

        Map<String, String> taskUsers = XmlToJsonUtil.getTaskUsers(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.FIRST_USER,taskUsers);

        LinkedList<String> preTaskNodes = XmlToJsonUtil.getPreTaskNodes(stringBPMN, "");
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.PRE_BACK,preTaskNodes);

        List<XmlBean> otherNodeType = XmlToJsonUtil.getOtherNodeType(stringBPMN);
        redisService.set(processDefinition.getKey()+XmlToJsonUtil.AS_NODE,otherNodeType);
    }
}
