import { assign } from "../../../min-dash/dist/index.esm.mjs";
import { is } from "../util/ModelUtil.mjs";
import { isLabelExternal, getExternalLabelBounds } from "../util/LabelUtil.mjs";
import { getMid } from "../../../diagram-js/lib/layout/LayoutUtil.mjs";
import { isExpanded } from "../util/DiUtil.mjs";
import { getLabel } from "../features/label-editing/LabelUtil.mjs";
import { elementToString } from "./Util.mjs";
function elementData(semantic, di, attrs) {
  return assign({
    id: semantic.id,
    type: semantic.$type,
    businessObject: semantic,
    di
  }, attrs);
}
function getWaypoints(di, source, target) {
  var waypoints = di.waypoint;
  if (!waypoints || waypoints.length < 2) {
    return [getMid(source), getMid(target)];
  }
  return waypoints.map(function(p) {
    return { x: p.x, y: p.y };
  });
}
function notYetDrawn(translate, semantic, refSemantic, property) {
  return new Error(translate("element {element} referenced by {referenced}#{property} not yet drawn", {
    element: elementToString(refSemantic),
    referenced: elementToString(semantic),
    property
  }));
}
function BpmnImporter(eventBus, canvas, elementFactory, elementRegistry, translate, textRenderer) {
  this._eventBus = eventBus;
  this._canvas = canvas;
  this._elementFactory = elementFactory;
  this._elementRegistry = elementRegistry;
  this._translate = translate;
  this._textRenderer = textRenderer;
}
BpmnImporter.$inject = [
  "eventBus",
  "canvas",
  "elementFactory",
  "elementRegistry",
  "translate",
  "textRenderer"
];
BpmnImporter.prototype.add = function(semantic, di, parentElement) {
  var element, translate = this._translate, hidden;
  var parentIndex;
  if (is(di, "bpmndi:BPMNPlane")) {
    var attrs = is(semantic, "bpmn:SubProcess") ? { id: semantic.id + "_plane" } : {};
    element = this._elementFactory.createRoot(elementData(semantic, di, attrs));
    this._canvas.addRootElement(element);
  } else if (is(di, "bpmndi:BPMNShape")) {
    var collapsed = !isExpanded(semantic, di), isFrame = isFrameElement(semantic);
    hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
    var bounds = di.bounds;
    element = this._elementFactory.createShape(elementData(semantic, di, {
      collapsed,
      hidden,
      x: Math.round(bounds.x),
      y: Math.round(bounds.y),
      width: Math.round(bounds.width),
      height: Math.round(bounds.height),
      isFrame
    }));
    if (is(semantic, "bpmn:BoundaryEvent")) {
      this._attachBoundary(semantic, element);
    }
    if (is(semantic, "bpmn:Lane")) {
      parentIndex = 0;
    }
    if (is(semantic, "bpmn:DataStoreReference")) {
      if (!isPointInsideBBox(parentElement, getMid(bounds))) {
        parentElement = this._canvas.findRoot(parentElement);
      }
    }
    this._canvas.addShape(element, parentElement, parentIndex);
  } else if (is(di, "bpmndi:BPMNEdge")) {
    var source = this._getSource(semantic), target = this._getTarget(semantic);
    hidden = parentElement && (parentElement.hidden || parentElement.collapsed);
    element = this._elementFactory.createConnection(elementData(semantic, di, {
      hidden,
      source,
      target,
      waypoints: getWaypoints(di, source, target)
    }));
    if (is(semantic, "bpmn:DataAssociation")) {
      parentElement = this._canvas.findRoot(parentElement);
    }
    this._canvas.addConnection(element, parentElement, parentIndex);
  } else {
    throw new Error(translate("unknown di {di} for element {semantic}", {
      di: elementToString(di),
      semantic: elementToString(semantic)
    }));
  }
  if (isLabelExternal(semantic) && getLabel(element)) {
    this.addLabel(semantic, di, element);
  }
  this._eventBus.fire("bpmnElement.added", { element });
  return element;
};
BpmnImporter.prototype._attachBoundary = function(boundarySemantic, boundaryElement) {
  var translate = this._translate;
  var hostSemantic = boundarySemantic.attachedToRef;
  if (!hostSemantic) {
    throw new Error(translate("missing {semantic}#attachedToRef", {
      semantic: elementToString(boundarySemantic)
    }));
  }
  var host = this._elementRegistry.get(hostSemantic.id), attachers = host && host.attachers;
  if (!host) {
    throw notYetDrawn(translate, boundarySemantic, hostSemantic, "attachedToRef");
  }
  boundaryElement.host = host;
  if (!attachers) {
    host.attachers = attachers = [];
  }
  if (attachers.indexOf(boundaryElement) === -1) {
    attachers.push(boundaryElement);
  }
};
BpmnImporter.prototype.addLabel = function(semantic, di, element) {
  var bounds, text, label;
  bounds = getExternalLabelBounds(di, element);
  text = getLabel(element);
  if (text) {
    bounds = this._textRenderer.getExternalLabelBounds(bounds, text);
  }
  label = this._elementFactory.createLabel(elementData(semantic, di, {
    id: semantic.id + "_label",
    labelTarget: element,
    type: "label",
    hidden: element.hidden || !getLabel(element),
    x: Math.round(bounds.x),
    y: Math.round(bounds.y),
    width: Math.round(bounds.width),
    height: Math.round(bounds.height)
  }));
  return this._canvas.addShape(label, element.parent);
};
BpmnImporter.prototype._getEnd = function(semantic, side) {
  var element, refSemantic, type = semantic.$type, translate = this._translate;
  refSemantic = semantic[side + "Ref"];
  if (side === "source" && type === "bpmn:DataInputAssociation") {
    refSemantic = refSemantic && refSemantic[0];
  }
  if (side === "source" && type === "bpmn:DataOutputAssociation" || side === "target" && type === "bpmn:DataInputAssociation") {
    refSemantic = semantic.$parent;
  }
  element = refSemantic && this._getElement(refSemantic);
  if (element) {
    return element;
  }
  if (refSemantic) {
    throw notYetDrawn(translate, semantic, refSemantic, side + "Ref");
  } else {
    throw new Error(translate("{semantic}#{side} Ref not specified", {
      semantic: elementToString(semantic),
      side
    }));
  }
};
BpmnImporter.prototype._getSource = function(semantic) {
  return this._getEnd(semantic, "source");
};
BpmnImporter.prototype._getTarget = function(semantic) {
  return this._getEnd(semantic, "target");
};
BpmnImporter.prototype._getElement = function(semantic) {
  return this._elementRegistry.get(semantic.id);
};
function isPointInsideBBox(bbox, point) {
  var x = point.x, y = point.y;
  return x >= bbox.x && x <= bbox.x + bbox.width && y >= bbox.y && y <= bbox.y + bbox.height;
}
function isFrameElement(semantic) {
  return is(semantic, "bpmn:Group");
}
export {
  BpmnImporter as default
};
