import * as go from 'gojs';
import { Diagram, Link, LinkingTool } from 'gojs';

export class PoolLink extends Link { 
    public diagram: Diagram;


    constructor() {
        super();
        go.Link.call(this);
    }
/** @override */
public getLinkPoint(node:any, port:any, spot:any, from:any, ortho:any, othernode:any, otherport:any) {
  var r = new go.Rect(port.getDocumentPoint(go.Spot.TopLeft),
                      port.getDocumentPoint(go.Spot.BottomRight));
  var op = go.Link.prototype.getLinkPoint.call(this, othernode, otherport, spot, from, ortho, node, port);

  var below = op.y > r.centerY;
  var y = below ? r.bottom : r.top;

  if (node.category === "privateProcess") {
    if (op.x < r.left) return new go.Point(r.left, y);
    if (op.x > r.right) return new go.Point(r.right, y);
    return new go.Point(op.x, y);
  } else { // otherwise get the standard link point by calling the base class method
    return go.Link.prototype.getLinkPoint.call(this, node, port, spot, from, ortho, othernode, otherport);
  }
};

// If there are two links from & to same node... and pool is offset in X from node... the link toPoints collide on pool
/** @override */
public computeOtherPoint(othernode:any, otherport:any) {
  var op = super.computeOtherPoint(othernode, otherport);
  var node = this.toNode;
  if (node === othernode) node = this.fromNode;
  if (othernode.category === "privateProcess") {
    op.x = node.getDocumentPoint(go.Spot.MiddleBottom).x;
  } else {
    if ((node === this.fromNode) !== (node.actualBounds.centerY < othernode.actualBounds.centerY)) {
      op.x -= 1;
    } else {
      op.x += 1;
    }
  }
  return op;
};

/** @override */
public getLinkDirection(node:any, port:any, linkpoint:any, spot:any, from:any, ortho:any, othernode:any, otherport:any) {
  if (node.category === "privateProcess") {
    var p = port.getDocumentPoint(go.Spot.Center);
    var op = otherport.getDocumentPoint(go.Spot.Center);
    var below = op.y > p.y;
    return below ? 90 : 270;
  } else {
    return go.Link.prototype.getLinkDirection.call(this, node, port, linkpoint, spot, from, ortho, othernode, otherport);
  }
};
}


export class BPMNLinkingTool extends LinkingTool { 
  constructor() {
      super();
      go.LinkingTool.call(this);
      // don't allow user to create link starting on the To node
      this.direction = go.LinkingTool.ForwardsOnly;
      this.temporaryLink.routing = go.Link.Orthogonal;
      this.linkValidation = this.customLinkValidation
  }
 public customLinkValidation(fromnode:any, fromport:any, tonode:any, toport:any) {
    let isValid =  validateSequenceLinkConnection(fromnode, fromport, tonode, toport) ||
    validateMessageLinkConnection(fromnode, fromport, tonode, toport);
    return isValid;
  };  
/** @override */
  public insertLink(fromnode:any, fromport:any, tonode:any, toport:any) {

  alert("Insert Link");
    var lsave = null;
    // maybe temporarily change the link data that is copied to create the new link
    if (validateMessageLinkConnection(fromnode, fromport, tonode, toport)) {
      lsave = this.archetypeLinkData;
      this.archetypeLinkData = { category: "msg" };
    }
  
    // create the link in the standard manner by calling the base method
    var newlink = super.insertLink.call(this, fromnode, fromport, tonode, toport);
  
    // maybe make the label visible
    if (fromnode.category === "gateway") {
      var label = newlink.findObject("Label");
      if (label !== null) label.visible = true;
    }
  
    // maybe restore the original archetype link data
    if (lsave !== null) this.archetypeLinkData = lsave;
    return newlink;
  };
  
  // static utility validation routines for linking & relinking as well as insert link logic
  
  
}
// in BPMN, can't link sequence flows across subprocess or pool boundaries
let validateSequenceLinkConnection = function(fromnode:any, fromport:any, tonode:any, toport:any) :boolean{
  if (fromnode.category === null || tonode.category === null) return true;

  // if either node is in a subprocess, both nodes must be in same subprocess (not even Message Flows) 
  if ((fromnode.containingGroup !== null && fromnode.containingGroup.category === "subprocess") || 
      (tonode.containingGroup !== null && tonode.containingGroup.category === "subprocess")) {
    if (fromnode.containingGroup !== tonode.containingGroup) return false;
  }

  if (fromnode.containingGroup === tonode.containingGroup) return true;  // a valid Sequence Flow
  // also check for children in common pool
  var common = fromnode.findCommonContainingGroup(tonode);
  return common != null;
}

// in BPMN, Message Links must cross pool boundaries
let validateMessageLinkConnection = function(fromnode:any, fromport:any, tonode:any, toport:any) :boolean {

  if (fromnode.category === null || tonode.category === null) return true;
    
  if (fromnode.category === "privateProcess" || tonode.category === "privateProcess") return true;    

  // if either node is in a subprocess, both nodes must be in same subprocess (not even Message Flows) 
  if ((fromnode.containingGroup !== null && fromnode.containingGroup.category === "subprocess") || 
      (tonode.containingGroup !== null && tonode.containingGroup.category === "subprocess")) {
    if (fromnode.containingGroup !== tonode.containingGroup) return false;
  }

  if (fromnode.containingGroup === tonode.containingGroup) return false;  // an invalid Message Flow
  // also check for children in common pool
  var common = fromnode.findCommonContainingGroup(tonode);
  return common === null;
}