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

package CH.ifa.draw.standard;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;

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.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.ui.ontology.DrawingView;


/**
 * ChangeConnectionHandle factors the common code for handles
 * that can be used to reconnect connections.
 *
 * @see ChangeConnectionEndHandle
 * @see ChangeConnectionStartHandle
 */
public abstract class ChangeConnectionHandle extends AbstractHandle {
    protected Connector fOriginalTarget;
    protected Figure fTarget;
    protected final ConnectionFigure fConnection;
    protected Point fStart;

    /**
     * Initializes the change connection handle.
     */
    protected ChangeConnectionHandle(Figure owner) {
        super(owner);
        fConnection = (ConnectionFigure) owner();
        fTarget = null;
    }

    /**
     * Returns the target connector of the change.
     */
    protected abstract Connector target();

    /**
     * Disconnects the connection.
     */
    protected abstract void disconnect();

    /**
     * Connect the connection with the given figure.
     */
    protected abstract void connect(Connector c);

    /**
     * Sets the location of the target point.
     */
    protected abstract void setPoint(int x, int y);

    /**
     * Gets the side of the connection that is unaffected by
     * the change.
     */
    protected Connector source() {
        if (target() == fConnection.start()) {
            return fConnection.end();
        }
        return fConnection.start();
    }

    /**
     * Disconnects the connection.
     */
    @Override
    public void invokeStart(int x, int y, DrawingView view) {
        super.invokeStart(x, y, view);
        fOriginalTarget = target();
        fStart = new Point(x, y);
        disconnect();
    }

    /**
     * Finds a new target of the connection.
     */
    @Override
    public void invokeStep(int x, int y, int anchorX, int anchorY, DrawingView view) {
        Point p = new Point(x, y);
        List<Figure> possibleTargets = findAllConnectableFigures(x, y, view.drawing());
        for (Figure f : possibleTargets) {
            // track the figure containing the mouse
            if (f != fTarget) {
                if (fTarget != null) {
                    fTarget.connectorVisibility(false);
                }
                fTarget = f;
                if (fTarget != null) {
                    fTarget.connectorVisibility(true);
                }
            }

            Connector target = findConnectionTarget(p.x, p.y, view.drawing());
            if (target != null) {
                p = Geom.center(target.displayBox());
                break;
            }
        }
        setPoint(p.x, p.y);
    }

    /**
     * Connects the figure to the new target. If there is no
     * new target the connection reverts to its original one.
     */
    @Override
    public void invokeEnd(int x, int y, int anchorX, int anchorY, DrawingView view) {
        Connector target = findConnectionTarget(x, y, view.drawing());
        if (target == null) {
            target = fOriginalTarget;
            noChangesMade();
        }
        setPoint(x, y);
        connect(target);
        fConnection.updateConnection();
        if (fTarget != null) {
            fTarget.connectorVisibility(false);
            fTarget = null;
        }
        super.invokeEnd(x, y, anchorX, anchorY, view);
    }

    private Connector findConnectionTarget(int x, int y, Drawing drawing) {
        List<Figure> possibleTargets = findAllConnectableFigures(x, y, drawing);
        for (Figure target : possibleTargets) {
            if ((target != null) && target.canConnect() && target != fOriginalTarget
                && !target.includes(owner()) && fConnection.canConnect(source().owner(), target)) {
                return findConnector(x, y, target);
            }
        }
        return null;
    }

    protected Connector findConnector(int x, int y, Figure f) {
        return f.connectorAt(x, y);
    }

    /**
     * Draws this handle.
     */
    @Override
    public void draw(Graphics g) {
        Rectangle r = displayBox();

        g.setColor(Color.green);
        g.fillRect(r.x, r.y, r.width, r.height);

        g.setColor(Color.black);
        g.drawRect(r.x, r.y, r.width, r.height);
    }

    private List<Figure> findAllConnectableFigures(int x, int y, Drawing drawing) {
        List<Figure> connectableFigures = new ArrayList<>();
        FigureEnumeration k = drawing.figuresReverse();
        while (k.hasMoreElements()) {
            Figure figure = k.nextFigure();
            if (!figure.includes(fConnection) && figure.canConnect()) {
                if (figure.containsPoint(x, y)) {
                    connectableFigures.add(figure);
                }
            }
        }
        return connectableFigures;
    }
}