/*
 * @(#)ShortestDistanceConnector.java 5.1
 *
 */

package CH.ifa.draw.figures;

import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.io.Serial;

import CH.ifa.draw.standard.AbstractConnector;
import CH.ifa.draw.util.Geom;
import de.renew.draw.storables.ontology.ConnectionFigure;
import de.renew.draw.storables.ontology.Connector;
import de.renew.draw.storables.ontology.Figure;

/**
 * A ShortestDistance locates connection points by
 * finding the shortest distance between the start and
 * end of the connection.
 * It doesn't connect to the ares defined by Figure.connectionInsets()
 * {@link Figure#connectionInsets}
 * {@link Connector}
 */
public class ShortestDistanceConnector extends AbstractConnector {
    /*
     * Serialization support.
     */
    @Serial
    private static final long serialVersionUID = -2273446020593433887L;

    /**
     * Constructs a new ShortestDistanceConnector.
     */
    public ShortestDistanceConnector() { // only used for Storable implementation
        super();
    }

    /**
     * Constructs a new ShortestDistanceConnector
     *
     * @param owner Owner of the connector
     */
    public ShortestDistanceConnector(Figure owner) {
        super(owner);
    }

    @Override
    public Point findStart(ConnectionFigure connection) {
        return findPoint(connection, true);
    }

    @Override
    public Point findEnd(ConnectionFigure connection) {
        return findPoint(connection, false);
    }

    /**
     * Finds the start or end point of the shortest connection between two figures.
     *
     * @param connection the {@code ConnectionFigure} whose start and end figures are evaluated
     * @param getStart if {@code true}, returns the start point; otherwise, returns the end point
     * @return the start or end {@code Point} of the shortest connection
     */
    protected Point findPoint(ConnectionFigure connection, boolean getStart) {
        Figure startFigure = connection.start().owner();
        Figure endFigure = connection.end().owner();

        Rectangle r1 = startFigure.displayBox();
        Rectangle r2 = endFigure.displayBox();

        Insets i1 = startFigure.connectionInsets();
        Insets i2 = endFigure.connectionInsets();

        Point p1;
        Point p2;
        Point start = null;
        Point end = null;
        Point s = null;
        Point e = null;
        long len2 = Long.MAX_VALUE;
        long l2;
        int x1; // connection points
        int x2; // connection points
        int y1; // connection points
        int y2; // connection points
        int xMin;
        int xMax;
        int yMin;
        int yMax;

        // X-dimension
        // constrain width connection insets
        int r1x;

        // X-dimension
        // constrain width connection insets
        int r1width;

        // X-dimension
        // constrain width connection insets
        int r2x;

        // X-dimension
        // constrain width connection insets
        int r2width;

        // X-dimension
        // constrain width connection insets
        int r1y;

        // X-dimension
        // constrain width connection insets
        int r1height;

        // X-dimension
        // constrain width connection insets
        int r2y;

        // X-dimension
        // constrain width connection insets
        int r2height;
        r1x = r1.x + i1.left;
        r1width = r1.width - i1.left - i1.right - 1;
        r2x = r2.x + i2.left;
        r2width = r2.width - i2.left - i2.right - 1;

        // find x connection point
        if (r1x + r1width < r2x) {
            x1 = r1x + r1width;
            x2 = r2x;
        } else if (r1x > r2x + r2width) {
            x1 = r1x;
            x2 = r2x + r2width;
        } else {
            xMax = Math.max(r1x, r2x);
            xMin = Math.min(r1x + r1width, r2x + r2width);
            x1 = x2 = (xMax + xMin) / 2;
        }

        // Y-Dimension
        // constrain with connection insets
        r1y = r1.y + i1.top;
        r1height = r1.height - i1.top - i1.bottom - 1;
        r2y = r2.y + i2.top;
        r2height = r2.height - i2.top - i2.bottom - 1;

        // y connection point
        if (r1y + r1height < r2y) {
            y1 = r1y + r1height;
            y2 = r2y;
        } else if (r1y > r2y + r2height) {
            y1 = r1y;
            y2 = r2y + r2height;
        } else {
            yMax = Math.max(r1y, r2y);
            yMin = Math.min(r1y + r1height, r2y + r2height);
            y1 = y2 = (yMax + yMin) / 2;
        }

        // find the shortest connection
        for (int i = 0; i < 4; i++) {
            switch (i) {
                case 0:
                    // EAST-WEST
                    p1 = Geom.east(r1);
                    p2 = Geom.west(r2);
                    s = new Point(p1.x, y1);
                    e = new Point(p2.x, y2);
                    break;
                case 1:
                    // WEST-EAST
                    p1 = Geom.west(r1);
                    p2 = Geom.east(r2);
                    s = new Point(p1.x, y1);
                    e = new Point(p2.x, y2);
                    break;
                case 2:
                    // NORTH-SOUTH
                    p1 = Geom.north(r1);
                    p2 = Geom.south(r2);
                    s = new Point(x1, p1.y);
                    e = new Point(x2, p2.y);
                    break;
                case 3:
                    // SOUTH-NORTH
                    p1 = Geom.south(r1);
                    p2 = Geom.north(r2);
                    s = new Point(x1, p1.y);
                    e = new Point(x2, p2.y);
                    break;
            }
            l2 = Geom.length2(s.x, s.y, e.x, e.y);
            if (l2 < len2) {
                start = s;
                end = e;
                len2 = l2;
            }
        }
        if (getStart) {
            return start;
        }
        return end;
    }
}