package netmatch.qtool;

import edu.umd.cs.piccolo.PLayer;
import edu.umd.cs.piccolo.nodes.PPath;

import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.AffineTransform;

import netmatch.piccolox.PFixedWidthStroke;

public class QPath extends PPath implements QElement {
  private static final Color PATH_COLOR = new Color(33, 33, 119);
  private final static float dash[] = {10.0f};
  private static final PFixedWidthStroke eStroke = new PFixedWidthStroke(2, PFixedWidthStroke.CAP_BUTT, PFixedWidthStroke.JOIN_MITER, 10.0f, dash, 0.0f);
  private QNode source, target;
  private String attr = "?>0";
  private String id;
  private int valence = 0;
  Point2D.Float p11;
  Point2D.Float p21;
  Point2D.Float middle;
  Point2D.Float p1;
  Point2D.Float p2;
  Point2D.Float point;
  AffineTransform at;

  public QPath(String id) {
    setStroke(eStroke);
    setStrokePaint(PATH_COLOR);
    this.id = id;
    point = new Point2D.Float();
    p11 = new Point2D.Float();
    p21 = new Point2D.Float();
    p1 = new Point2D.Float();
    p2 = new Point2D.Float();
    middle = new Point2D.Float();
    at = new AffineTransform();
  }

  public QPath(QNode s, QNode t, String id) {
    this(id);
    source = s;
    target = t;
    update();
  }

  public void delete(PLayer nodeLayer, PLayer edgeLayer) {
    if(getSource() != null)
      getSource().removePath(this);
    if(getTarget() != null)
      getTarget().removePath(this);
    edgeLayer.removeChild(this);
  }

  public String getInfo() {
    return "Path\n" + "Id: " + id + "\n" + "Attribute: " + attr + "\n" + "Source Id: " + getSource().getId() + "\n" + "Target Id: " + getTarget().getId() + "\n";
  }

  public String getId() {
    return id;
  }

  public void update() {
    boolean flag = false;
    QNode first = source;
    QNode second = target;
    float shift = 20 * (float) Math.floor((valence + 1) / 2);
    float sign = +1;
    if(valence % 2 == 0)
      sign = -1;
    if(target.getId().compareTo(source.getId()) < 0) {
      first = target;
      second = source;
      flag = true;
    }
    Point2D start = first.getFullBoundsReference().getCenter2D();
    Point2D end = second.getFullBoundsReference().getCenter2D();
    reset();
    moveTo((float) start.getX(), (float) start.getY());
    // multi-edge
    if(valence > 0) {
      point.setLocation((float) start.getX() + (float) (end.getX() - start.getX()) / 2, (float) start.getY() + (float) (end.getY() - start.getY()) / 2);
      double angle = Math.atan((end.getY() - start.getY()) / (end.getX() - start.getX()));
      if(end.getX() >= start.getX())
        point.setLocation(point.getX() + sign * shift * Math.sin(angle), point.getY() - sign * shift * Math.cos(angle));
      else
        point.setLocation(point.getX() - sign * shift * Math.sin(angle), point.getY() + sign * shift * Math.cos(angle));
      lineTo((float) point.getX(), (float) point.getY());
    }
    else
      point.setLocation((start.getX() + end.getX()) / 2, (start.getY() + end.getY()) / 2);
    lineTo((float) end.getX(), (float) end.getY());
    if(flag)
      drawArrow(point, start);
    else
      drawArrow(point, end);
  }

  private void drawArrow(Point2D start, Point2D end) {
    middle.setLocation(((float) start.getX() + (float) end.getX()) / 2, ((float) start.getY() + (float) end.getY()) / 2);
    p1.setLocation((float) middle.getX() - 5, (float) middle.getY() - 5);
    p2.setLocation((float) middle.getX() - 5, (float) middle.getY() + 5);
    double d1 = start.distance(end);
    double d2 = start.distance(end.getX(), start.getY());
    double angle = Math.acos(d2 / d1);
    if(end.getX() < start.getX()) {
      p1.setLocation((float) middle.getX() + 5, (float) middle.getY() - 5);
      p2.setLocation((float) middle.getX() + 5, (float) middle.getY() + 5);
    }
    else if(end.getX() == start.getX() && end.getY() < start.getY()) {
      p1.setLocation((float) p1.getX() + 10, (float) p1.getY());
      p2.setLocation((float) p2.getX() + 10, (float) p2.getY());
    }
    if((end.getX() < start.getX() && end.getY() > start.getY()) || (end.getX() > start.getX() && end.getY() < start.getY()))
      angle = -angle;
    at.setToIdentity();
    at.rotate(angle, middle.getX(), middle.getY());
    at.transform(p1, p11);
    at.transform(p2, p21);
    moveTo((float) middle.getX(), (float) middle.getY());
    lineTo((float) p11.getX(), (float) p11.getY());
    moveTo((float) middle.getX(), (float) middle.getY());
    lineTo((float) p21.getX(), (float) p21.getY());
  }

  void drawTo(Point2D end) {
    if(source == null)
      return;
    Point2D start = source.getFullBoundsReference().getCenter2D();
    reset();
    moveTo((float) start.getX(), (float) start.getY());
    lineTo((float) end.getX(), (float) end.getY());
    point.setLocation((start.getX() + end.getX()) / 2, (start.getY() + end.getY()) / 2);
    drawArrow(point, end);
  }

  public void setAttr(String attr) {
    this.attr = attr;
  }

  public String getAttr() {
    return attr;
  }

  public QNode getSource() {
    return source;
  }

  public void setSource(QNode s) {
    source = s;
  }

  public QNode getTarget() {
    return target;
  }

  public void setTarget(QNode t) {
    target = t;
  }

  public void setValence(int newValence) {
    valence = newValence;
  }

  public int getValence() {
    return valence;
  }

}
