package com.huigou.explorer.converters.bpmn;

import com.alibaba.fastjson.JSONArray;
import com.huigou.explorer.util.MxCellUtils;
import com.huigou.explorer.Shape;
import com.mxgraph.model.mxCell;
import com.mxgraph.model.mxGeometry;
import com.mxgraph.model.mxGraphModel;
import org.activiti.bpmn.model.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Optional;

/**
 * @author yonghuan
 */
public abstract class AbstractElementConverter implements ElementConverter, InitializingBean {

    protected final List<Shape> supportShapes;
    private ActivitiListenerParser activitiListenerParser;

    public AbstractElementConverter(List<Shape> supportShapes) {
        this.supportShapes = supportShapes;
    }

    public void setActivitiListenerParser(ActivitiListenerParser activitiListenerParser) {
        this.activitiListenerParser = activitiListenerParser;
    }

    protected ActivitiListenerParser getActivitiListenerParser() {
        return activitiListenerParser;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(activitiListenerParser, "activitiListenerParser 不能为空");
    }

    @Override
    public boolean supports(Object mxElement) {
        return (mxElement instanceof mxCell) && MxCellUtils.parseShape((mxCell) mxElement)
                .map(shape -> supportShapes.stream().filter(s -> s == shape).findAny())
                .filter(shape -> shape.isPresent())
                .isPresent();
    }

    @Override
    public void convert(BpmnModel bpmnModel, mxGraphModel graphModel, Object mxElement) {
        mxCell cell = (mxCell) mxElement;
        FlowElement fe = convertInternal(bpmnModel, graphModel, cell);

        fe.setId(cell.getAttribute("code"));
        fe.setName(cell.getAttribute("label"));
        fe.setDocumentation(cell.getAttribute("documentation"));
        if (fe instanceof FlowNode) {
            FlowNode flowNode = (FlowNode) fe;
            flowNode.setAsynchronous("true".equals(cell.getAttribute("asynchronous")));
            flowNode.setNotExclusive("false".equals(cell.getAttribute("exclusive")));
        }
        if (fe instanceof Activity) {
            Activity activity = (Activity) fe;
            String multiInstanceType = cell.getAttribute("multiInstanceType");
            if (StringUtils.isNotBlank(multiInstanceType)) {
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
                multiInstanceLoopCharacteristics.setSequential("sequential".equals(multiInstanceType));
                multiInstanceLoopCharacteristics.setInputDataItem(cell.getAttribute("multiInstanceCollection"));
                multiInstanceLoopCharacteristics.setLoopCardinality(cell.getAttribute("multiInstanceCardinality"));
                multiInstanceLoopCharacteristics.setElementVariable(cell.getAttribute("multiInstanceElementVariable"));
                multiInstanceLoopCharacteristics.setCompletionCondition(cell.getAttribute("multiInstanceCompletionCondition"));
                activity.setLoopCharacteristics(multiInstanceLoopCharacteristics);
            }
            if ("true".equals(cell.getAttribute("isForCompensation"))) {
                activity.setForCompensation(true);
            }
        }
        if (fe instanceof HasExecutionListeners) {
            Optional.ofNullable(cell.getAttribute("executionListeners"))
                    .filter(StringUtils::isNotBlank)
                    .map(JSONArray::parseArray)
                    .map(activitiListenerParser::parse)
                    .ifPresent(executionListeners -> ((HasExecutionListeners) fe).setExecutionListeners(executionListeners));
        }

        bpmnModel.getProcesses().get(0).addFlowElement(fe);

        // 形状GraphicInfo
        GraphicInfo graphicInfo = new GraphicInfo();
        mxGeometry geometry = cell.getGeometry();
        graphicInfo.setX(geometry.getX());
        graphicInfo.setY(geometry.getY());
        graphicInfo.setWidth(geometry.getWidth());
        graphicInfo.setHeight(geometry.getHeight());
        graphicInfo.setElement(fe);
        bpmnModel.addGraphicInfo(fe.getId(), graphicInfo);
    }

    protected abstract FlowElement convertInternal(BpmnModel bpmnModel, mxGraphModel graphModel, mxCell cell);
}
