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

package CH.ifa.draw.standard;

import java.awt.Desktop;
import java.awt.Graphics;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import CH.ifa.draw.DrawPlugin;
import CH.ifa.draw.framework.ChildFigure;
import CH.ifa.draw.framework.PartialSelectableFigure;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.ui.ontology.DrawingEditor;
import de.renew.draw.ui.ontology.DrawingView;
import de.renew.draw.ui.ontology.FigureHandle;
import de.renew.draw.ui.ontology.Tool;
import de.renew.util.StringUtil;


/**
 * Tool to select and manipulate figures.
 * A selection tool is in one of three states, e.g., background
 * selection, figure selection, handle manipulation. The different
 * states are handled by different child tools.
 * <hr>
 * <b>Design Patterns</b><P>
 * <img src="images/red-ball-small.gif" width=6 height=6 alt=" o ">
 * <b><a href=../pattlets/sld032.htm>State</a></b><br>
 * SelectionTool is the StateContext and child is the State.
 * The SelectionTool delegates state specific
 * behavior to its current child tool.
 * <hr>
 */
public class SelectionTool extends AbstractTool {
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(SelectionTool.class);
    private Tool _child = null;
    private Tool _lastChild = null;
    private DrawingView _frozenView = null;

    public SelectionTool(DrawingEditor editor) {
        super(editor);
    }

    /**
     * Handles mouse down events and starts the corresponding tracker.
     * <br>
     * Caution: This method freezes the view until a
     * <code>mouseReleased</code> event is received or the tool is
     * <code>deactivate</code>d.
     */
    @Override
    public void mouseDown(MouseEvent e, int x, int y) {
        // on Windows NT: AWT generates additional mouse down events
        // when the left button is down && right button is clicked.
        // To avoid deadlocks we ignore such events
        if (_child != null) {
            return;
        }


        // Since Java 1.4.2, the AWT event thread dies after an Error or
        // RuntimeException. Because the mouseUp event then belongs to a
        // different thread, it cannot unfreeze the view. So, we have to
        // catch any abnormal situation and unfreeze the view immediately.
        try {
            boolean rightClick = (e.getModifiersEx()
                & (InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK)) != 0;
            if (_lastChild != null && e.getClickCount() == 2 && !rightClick) {
                // If left-double-click: use previous _child!
                _child = _lastChild;
                _lastChild = null;
            } else {
                if (_frozenView == null) {
                    _frozenView = view();
                    _frozenView.freezeView();
                }

                FigureHandle handle = null;
                if (!rightClick && view().selectionCount() == 1) {
                    handle = view().findHandle(x, y);
                }
                if (handle != null) {
                    _child = createHandleTracker(fEditor, handle);
                    _lastChild = _child; // store for double-clicks!
                } else {
                    _lastChild = null;
                    Figure figure = drawing().findFigure(x, y);

                    // also interprets clicks on a figure's handle as clicks
                    // on the figure:
                    if (figure == null) {
                        handle = view().findHandle(x, y);
                        if (handle != null) {
                            figure = handle.owner();
                        }
                    }
                    boolean selectableFigureFound = false;
                    if (figure != null) {
                        selectableFigureFound = true;
                        if (figure instanceof PartialSelectableFigure partialSelectableFigure) {
                            if (!view().selection().contains(figure)) {
                                selectableFigureFound = false;
                                if (partialSelectableFigure.isModifierSelectable()
                                    && e.isAltDown()) {
                                    selectableFigureFound = true;
                                } else if (partialSelectableFigure.isSelectableInRegion(x, y)) {
                                    selectableFigureFound = true;
                                }
                            }
                        }
                    }

                    if (selectableFigureFound) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("Click-on-figure: " + figure);
                        }
                        if (e.isControlDown()) {
                            if (rightClick) {
                                // FIXME: handle ctrl / right click with editing window for ttt
                            } else {
                                openTargetLocation(figure);
                            }
                        } else {
                            if (e.getClickCount() >= 2 && (!rightClick)) {
                                figure.inspect(view(), false);
                                return;
                            }
                            if (rightClick) {
                                if (alternateInspectFigure(figure)) {
                                    return;
                                }
                            }
                            _child = createDragTracker(fEditor, figure);
                        }
                    } else { // figure == null
                        if (!e.isShiftDown()) {
                            view().clearSelection();
                        }
                        _child = createAreaTracker(fEditor);
                    }
                }
            }
            if (_child != null) { //can be null if a control left click is performed
                if (_child instanceof DragTracker || _child instanceof SelectAreaTracker) {
                    _child.mouseDown(e, x, y);
                } else {
                    _child.mouseDown(e, e.getX(), e.getY());
                }
            }
        } catch (RuntimeException | Error ex) {
            if (_frozenView != null) {
                _frozenView.unfreezeView();
                _frozenView = null;
            }
            throw ex;
        }
    }

    /**
     * @param figure
     */
    private void openTargetLocation(Figure figure) {
        //try to open targetLocation                            
        Object target = figure.getAttribute("targetLocation");
        if (target != null) {
            try {
                URI targetURI = new URI((String) target);
                try {
                    String scheme = targetURI.getScheme();
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(
                            SelectionTool.class.getSimpleName() + ": " + "Trying to open: "
                                + "Scheme | Host | Path = " + scheme + " | " + targetURI.getHost()
                                + " | " + targetURI.getPath());
                    }
                    if (scheme == null && !DrawPlugin.getGui().canOpen(targetURI)) {
                        scheme = "file";
                        targetURI = new URI(
                            scheme, targetURI.getHost(), targetURI.getPath(),
                            targetURI.getFragment());
                    }
                    if (scheme == null) {
                        URI drawingURI =
                            new File(drawing().getFilename().getCanonicalPath()).toURI();

                        URI uri = drawingURI.resolve(targetURI);
                        DrawPlugin.getGui().openOrLoadDrawing(new File(uri.getPath()));
                    } else if ("sim".equals(scheme)) {
                        simAccess(targetURI);
                    } else {
                        Desktop.getDesktop().browse(targetURI);
                    }
                } catch (URISyntaxException e) {
                    URI drawingURI = new File(drawing().getFilename().getCanonicalPath()).toURI();
                    String path = StringUtil.getPath(drawingURI.getPath());
                    Desktop.getDesktop().open(new File(path, (String) target));
                } catch (IOException e2) {
                    LOGGER.error(
                        "Could not find file: " + targetURI.getPath() + " " + e2.getMessage());
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug(SelectionTool.class.getSimpleName() + ": ", e2);
                    }
                }
            } catch (Exception e3) {
                LOGGER.error(
                    "An error occurred during file access (malformed URL?): " + e3.getMessage());
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug(SelectionTool.class.getSimpleName() + ": ", e3);
                }
            }
        }
    }

    /**
     * Handles mouse drag events. The events are forwarded to the
     * current tracker.
     */
    @Override
    public void mouseDrag(MouseEvent e, int x, int y) {
        if (_child != null) { // JDK1.1 doesn't guarantee mouseDown, mouseDrag, mouseUp
                              // If we have a zooming factor, we don't want to scale the selection border
                              // We do however want to scale dragging figures and Handles
            _child.mouseDrag(e, x, y);

            _lastChild = null; // don't do double-clicks after dragging
        }
    }

    /**
     * Handles mouse up events. The events are forwarded to the
     * current tracker.
     * <p>
     * Unfreezes the view, if frozen by a <code>mouseDown</code> event.
     * </p>
     */
    @Override
    public void mouseUp(MouseEvent e, int x, int y) {
        if (_frozenView != null) {
            _frozenView.unfreezeView();
            _frozenView = null;
        }
        if (_child != null) { // JDK1.1 doesn't guarantee mouseDown, mouseDrag, mouseUp
            _child.mouseUp(e, x, y);

        }
        _child = null;
    }

    /**
     * Factory method to create a Handle tracker. It is used to track a handle.
     */
    protected Tool createHandleTracker(DrawingEditor editor, FigureHandle handle) {
        return new HandleTracker(editor, handle);
    }

    /**
     * Factory method to create a Drag tracker. It is used to drag a figure.
     */
    protected Tool createDragTracker(DrawingEditor editor, Figure f) {
        if (f instanceof ChildFigure) {
            return new ChildDragTracker(editor, (ChildFigure) f);
        } else {
            return new DragTracker(editor, f);
        }
    }

    /**
     * Factory method to create an area tracker. It is used to select an
     * area.
     */
    protected Tool createAreaTracker(DrawingEditor editor) {
        return new SelectAreaTracker(editor);
    }

    protected boolean alternateInspectFigure(Figure f) {
        return f.inspect(view(), true);
    }

    /**
     * Delegates the graphical feedback update to the child.
     **/
    @Override
    public void draw(Graphics g) {
        if (_child != null) {
            _child.draw(g);
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Unfreezes the view, if frozen by a <code>mouseDown</code> event.
     * </p>
     **/
    @Override
    public void deactivate() {
        if (_frozenView != null) {
            _frozenView.unfreezeView();
            _frozenView = null;
        }
        super.deactivate();
    }
}