// calculate the coordinates of the startpoint (endpoint) if parameter is
  // true (false) of the arrow when drawing this link
  private Point calcCoord(boolean startpoint) {
    if (!startpoint && (end == null)) return openEnd;

    int deltaX;
    int deltaY;

    if (end == null) {
      deltaX = start.getX() - openEnd.x;
      deltaY = start.getY() - openEnd.y;
    } else {
      deltaX = start.getX() - end.getX();
      deltaY = start.getY() - end.getY();
    }

    double dist = Math.sqrt(deltaX * deltaX + deltaY * deltaY);

    double prop = (Client.RADIUS) / dist;

    int offsetX = (int) Math.round(deltaX * prop);
    int offsetY = (int) Math.round(deltaY * prop);

    if (startpoint) {
      return new Point(start.getX() - offsetX, start.getY() - offsetY);
    } else return new Point(end.getX() + offsetX, end.getY() + offsetY);
  }
  // returns the endpoint which is closer to (x,y) than the other one
  public Client getPointNear(int x, int y) {
    if (start == null || end == null)
      throw new RuntimeException("ERROR: getPointNear() not allowed on open links");

    int distToStart = (int) Math.pow(start.getX() - x, 2) + (int) Math.pow(start.getY() - y, 2);

    int distToEnd = (int) Math.pow(end.getX() - x, 2) + (int) Math.pow(end.getY() - y, 2);

    if (distToStart < distToEnd) {
      return start;
    } else {
      return end;
    }
  }
  // get the delay of the next packet
  public int getDelay() {
    switch (delayType) {
      case NO_DELAY:
        return 0;

      case CONST_DELAY:
        return constDelayMs;

      case DIST_DELAY:
        // calculate distance between start and end node
        int x = start.getX() - end.getX();
        int y = start.getY() - end.getY();
        float dist = (float) Math.sqrt(x * x + y * y);
        return (int) (dist * distDelayFactor);

      default:
        throw new RuntimeException("ERROR: Illegal delay type " + delayType);
    }
  }
  // returns true if (x,y) is close to the link
  public boolean intersects(int x0, int y0) {

    if (end == null) throw new RuntimeException("ERROR: intersect() not allowed on open links");

    int x1, x2, y1, y2;

    x1 = start.getX();
    x2 = end.getX();
    y1 = start.getY();
    y2 = end.getY();

    // calculate the distance between the direct start-end connection and the point
    double o = Math.abs((x2 - x1) * (y1 - y0) - (x1 - x0) * (y2 - y1));
    double u = Math.sqrt(Math.pow((x2 - x1), 2) + Math.pow((y2 - y1), 2));

    if ((o / u) < INTERSECTION_DISTANCE) {
      return true;
    }

    return false;
  }
  // draw the link
  public void draw(Graphics g1D) {
    // don't draw an open link if the end is inside of its startpoint
    if ((end == null) && (openEnd.distance(start.getX(), start.getY()) < Client.RADIUS)) return;

    // activate anti aliasing
    Graphics2D g = (Graphics2D) g1D;
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    // set correct color
    if ((end != null) && start.isRunning() && end.isRunning()) g.setColor(RUNNING_LINK_COLOR);
    else g.setColor(SLEEPING_LINK_COLOR);

    // calculate start- & endpoint and link length
    Point s = new Point();
    Point e = new Point();
    double dist;

    s.x = start.getX();
    s.y = start.getY();

    if (end == null) {
      e.x = openEnd.x;
      e.y = openEnd.y;
      dist = s.distance(e) + Client.RADIUS;
    } else {
      e.x = end.getX();
      e.y = end.getY();
      dist = s.distance(e);
    }

    int fact = 1; //  fact = +/- 1 (needed if s not on the left of e)

    if (s.x > e.x) fact = -1;

    // rotate g so that we can draw horizontally
    double theta = Math.atan(((float) (e.y - s.y)) / (e.x - s.x));
    g.rotate(theta, s.x, s.y);

    // draw the link
    Stroke old = g.getStroke();

    if (getErrorType() == NO_ERROR) {
      g.setStroke(normal);
    } else {
      g.setStroke(dashed);
    }
    if (this.isBidirectional) {
      g.drawLine(
          s.x + fact * (Client.RADIUS + ARROW_LENGTH),
          s.y,
          (s.x + fact * ((int) dist - Client.RADIUS - ARROW_LENGTH)),
          s.y);
    } else {
      g.drawLine(
          s.x + fact * Client.RADIUS,
          s.y,
          (s.x + fact * ((int) dist - Client.RADIUS - ARROW_LENGTH)),
          s.y);
    }
    g.setStroke(old);

    // draw the arrows
    //
    //  /| |\ ^ W = ARROW_WIDTH
    // < ------ > W L = ARROW_LENGTH
    //  \| |/ v
    //
    // <L>
    // <-- dist -->

    int arrowX[] = new int[3];
    int arrowY[] = new int[3];

    arrowX[0] = s.x + fact * (Client.RADIUS);
    arrowX[1] = s.x + fact * (Client.RADIUS + ARROW_LENGTH);
    arrowX[2] = s.x + fact * (Client.RADIUS + ARROW_LENGTH);

    arrowY[0] = s.y;
    arrowY[1] = s.y - ARROW_WIDTH / 2;
    arrowY[2] = s.y + ARROW_WIDTH / 2;

    if (this.isBidirectional) {
      if (flashingStart) {
        g.setColor(USED_LINK_COLOR);
        g.fillPolygon(arrowX, arrowY, 3);
        g.setColor(RUNNING_LINK_COLOR);
      } else {
        g.fillPolygon(arrowX, arrowY, 3);
      }
    } else {

    }

    arrowX[0] = s.x + fact * ((int) dist - Client.RADIUS);
    arrowX[1] = s.x + fact * ((int) dist - Client.RADIUS - ARROW_LENGTH);
    arrowX[2] = s.x + fact * ((int) dist - Client.RADIUS - ARROW_LENGTH);

    if (flashingEnd) {
      g.setColor(USED_LINK_COLOR);
      g.fillPolygon(arrowX, arrowY, 3);
      g.setColor(RUNNING_LINK_COLOR);
    } else {
      g.fillPolygon(arrowX, arrowY, 3);
    }

    // write delay & packet info to the link
    if (end != null) {
      // write delay above the link
      String delayText = Integer.toString(getDelay()) + " ms";

      if (delayText.equals("0 ms")) {
        delayText = "no delay";
      }

      int delayWidth = g.getFontMetrics().stringWidth(delayText);

      // draw the text only if there is enough space
      if (delayWidth < dist - 2 * Client.RADIUS)
        g.drawString(delayText, (int) (s.x + fact * dist / 2 - delayWidth / 2), s.y - 8);

      // write sent/lost packets below the link
      String packetText =
          "sent: " + Integer.toString(succCount) + " lost: " + Integer.toString(lostCount);

      String packetTextShort = Integer.toString(succCount) + " / " + Integer.toString(lostCount);

      int packetWidth = g.getFontMetrics().stringWidth(packetText);
      int packetWidthShort = g.getFontMetrics().stringWidth(packetTextShort);

      // draw the text only if there is enough space
      if (packetWidth < dist - 2 * Client.RADIUS)
        g.drawString(packetText, (int) (s.x + fact * dist / 2 - packetWidth / 2), s.y + 15);
      else if (packetWidthShort < dist - 2 * Client.RADIUS)
        g.drawString(
            packetTextShort, (int) (s.x + fact * dist / 2 - packetWidthShort / 2), s.y + 15);
    }

    // rotate g back
    g.rotate(-theta, s.x, s.y);
  }