/*
 * Created on Mar 14, 2005
 */
package org.flexdock.docking;

import javax.swing.JSplitPane;

import org.flexdock.docking.drag.DragOperation;

/**
 * This interface defines an API used by {@code DockingManager} and
 * {@code DockingPort} to support customizable behaviors during docking
 * operations. {@code DockingManager} will associate a {@code DockingStrategy}
 * with a particular {@code DockingPort} or {@code Dockable} class type. Calls
 * to {@code DockingManager.dock()} and {@code DockingManager.undock()} will be
 * deferred to the {@code DockingStrategy} associated with the parameters
 * supplied in the respective method calls. {@code DockingStrategies} are also
 * responsible for creating sub-DockingPorts and split panes for nested
 * {@code DockingPorts}
 * 
 * Implementations of {@code DockingStrategy} are responsible for managing
 * component relationships between {@code Dockables} and parent containers. This
 * includes making determinations as to whether a particular docking operation
 * will be allowed for the specified parameters and the specifics of how a
 * particular {@code Dockable} may be removed from one parent
 * {@code Container} or {@code DockingPort} and added to another.
 * {@code DockingStrategy} may determine whether a call to {@code dock()}
 * implies an attempt to float a {@code Dockable} in a separate window.
 * 
 * Because of the potentially large scope of responsibilities associated with a
 * {@code DockingStrategy}, implementations may range from being very simple to
 * highly complex. Although custom implementations of {@code DockingStrategy}
 * are not discouraged, the recommeded path is to subclass
 * {@code DefaultDockingStrategy} for consistency of behavior.
 * 
 * {@code DockingStrategies} are associated with a particular type of
 * {@code Dockable} or {@code DockingPort} by calling
 * {@code DockingManager.setDockingStrategy(Class c, DockingStrategy strategy)}.
 * {@code DefaultDockingStrategy} is the default implementation used for all
 * classes that do not have a custom {@code DockingStrategy} registered.
 * 
 * @author Christopher Butler
 */
public interface DockingStrategy {

    /**
     * Attempts to dock the specified {@code Dockable} into the supplied
     * {@code DockingPort} in the specified region. If docking is not possible
     * for the specified parameters, then the method returns {@code false} and
     * no action is taken. Since there is no {@code DragOperation} parameter
     * present, this method implies programmatic docking as opposed to docking
     * as a result of drag-events.
     * 
     * @param dockable
     *            the {@code Dockable} we wish to dock
     * @param dockingPort
     *            the {@code DockingPort} into which we wish to dock
     * @param dockingRegion
     *            the region of the specified {@code DockingPort} into which we
     *            wish to dock.
     * @return whether or not the docking operation was successful.
     */
    boolean dock(Dockable dockable, DockingPort dockingPort,
            String dockingRegion);

    /**
     * Attempts to dock the specified {@code Dockable} into the supplied
     * {@code DockingPort} in the specified region based upon the semantics of
     * the specified {@code DragOperation}.   If docking is not possible
     * for the specified parameters, then the method returns {@code false} and
     * no action is taken.
     * 
     * @param dockable
     *            the {@code Dockable} we wish to dock
     * @param dockingPort
     *            the {@code DockingPort} into which we wish to dock
     * @param dockingRegion
     *            the region of the specified {@code DockingPort} into which we
     *            wish to dock.
     * @param operation
     *            the {@code DragOperation} describing the state of the
     *            application/mouse at the point in time in which we're
     *            attempting to dock.
     * @return whether or not the docking operation was successful.
     */
    boolean dock(Dockable dockable, DockingPort dockingPort,
            String dockingRegion, DragOperation operation);

    /**
     * Undocks the specified {@code Dockable} instance from its containing
     * {@code DockingPort}.
     * 
     * @param dockable
     *            the {@code Dockable} we wish to undock
     * @return {@code true} if the {@code Dockable} was successfully undocked.
     *         Otherwise, returns {@code false}.
     */
    boolean undock(Dockable dockable);

    /**
     * Creates and returns a new {@code DockingPort} instance based upon the
     * supplied {@code DockingPort} parameter. For layouts that support nested
     * {@code DockingPorts}, this method is useful for creating child
     * {@code DockingPorts} suitable for embedding within the base
     * {@code DockingPort}
     * 
     * @param base
     *            the {@code DockingPort} off of which the returned instance
     *            will be based.
     * @return a new {@code DockingPort} instance based upon the supplied
     *         parameter.
     */
    DockingPort createDockingPort(DockingPort base);

    /**
     * Creates and returns a new {@code JSplitPane} instance based upon the
     * supplied parameters. The returned {@code JSplitPane} should be suitable
     * for embedding within the base {@code DockingPort} and its orientation
     * should reflect the supplied {@code region} parameter.
     * 
     * @param base
     *            the {@code DockingPort} off of which the returned
     *            {@code JSplitPane} will be based.
     * @param region
     *            the region within the base {@code DockingPort} used to
     *            determine the orientation of the returned {@code JSplitPane}.
     * @return a new {@code JSplitPane} suitable for embedding within the base
     *         {@code DockingPort} parameter.
     */
    JSplitPane createSplitPane(DockingPort base, String region);

    /**
     * Returns the initial divider location to be used by the specified
     * {@code JSplitPane}. This method assumes that the {@code JSplitPane}
     * parameter is embedded within the specified {@code DockingPort} and that
     * is has been validated and its current dimensions are non-zero.
     * 
     * @param dockingPort
     *            the {@code DockingPort} that contains, or will contain the
     *            specified {@code JSplitPane}.
     * @param splitPane
     *            the {@code JSplitPane} whose initial divider location is to be
     *            determined.
     * @return the desired divider location of the supplied {@code JSplitPane}.
     */
    int getInitialDividerLocation(DockingPort dockingPort, JSplitPane splitPane);

    /**
     * Returns the desired divider proportion of the specified
     * {@code JSplitPane} after rendering. This method assumes that the
     * {@code JSplitPane} parameter is, or will be embedded within the specified
     * {@code DockingPort}. This method does <b>not</b> assume that the
     * {@code JSplitPane} has been validated and that it's current dimensions
     * are non-zero.
     * 
     * @param dockingPort
     *            the {@code DockingPort} that contains, or will contain the
     *            specified {@code JSplitPane}.
     * @param splitPane
     *            the {@code JSplitPane} whose initial divider location is to be
     *            determined.
     * @return the desired divider proportion of the supplied {@code JSplitPane}.
     */
    double getDividerProportion(DockingPort dockingPort, JSplitPane splitPane);
}
