/*
 * Copyright (c) 2004 Christopher M Butler
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.flexdock.docking;

import java.awt.Component;
import java.awt.Point;
import java.util.Set;

import org.flexdock.docking.event.DockingListener;
import org.flexdock.docking.event.DockingMonitor;
import org.flexdock.docking.props.DockingPortPropertySet;
import org.flexdock.docking.state.LayoutNode;

/**
 * This interface is designed to specify the API's required by
 * {@code DockingManager} for placing {@code Dockable} instances within a
 * container. A {@code DockingPort} is the parent container inside of which
 * {@code Dockable} instances may be placed.
 *
 * @author Chris Butler
 */
public interface DockingPort extends DockingListener, DockingMonitor {
    String INITIAL_TAB_POSITION = "DockingPort.init.tab.position";

    /**
     * Returns a boolean indicating whether or not docking is allowed within the
     * specified region. Used by {@code DockingManager} during drag operations.
     */
    boolean isDockingAllowed(Component comp, String region);

    /**
     * Removes all docked components from the {@code DockingPort}.
     */
    void clear();

    /**
     * Docks the specified Dockable in the specified region. The
     * {@code Dockable's} {@code getDockable()} component is used as the docking
     * component.
     */
    boolean dock(Dockable dockable, String region);

    /**
     * Docks the specified Component in the specified region. Returns
     * {@code true} for success and {@code false} for failure.
     */
    boolean dock(Component comp, String region);

    /**
     * Returns a reference to the currently docked component.
     */
    Component getDockedComponent();

    /**
     * Returns a reference to Dockable currently docked in the target region.
     * Returns null if there is no Dockable there. If a tabbed layout is
     * present, this method will return the Dockable in the currently selected
     * tab.
     */
    Dockable getDockable(String region);

    /**
     * Returns a reference to Component currently docked in the target region.
     * Returns null if there is no Component there. If a tabbed layout is
     * present, this method will return the Component in the currently selected
     * tab.
     */
    Component getComponent(String region);

    /**
     * Returns a {@code String} identifier that is unique within a JVM instance,
     * but persistent across JVM instances. This is used for configuration
     * mangement, allowing the JVM to recognize a {@code DockingPort} instance
     * within an application instance, persist the ID, and recall it in later
     * application instances. The ID should be unique within an appliation
     * instance so that there are no collisions with other {@code DockingPort}
     * instances, but it should also be consistent from JVM to JVM so that the
     * association between a {@code DockingPort} instance and its ID can be
     * remembered from session to session.
     */
    String getPersistentId();

    /**
     * Sets the persistent ID String to be returned by {@code getPersistentId()}.
     *
     * @param id
     *            the persistent ID to be applied.
     * @see #getPersistentId()
     */
    void setPersistentId(String id);

    /**
     * Indicates whether or not the specified component is a child component
     * docked within the {@code DockingPort}.
     */
    boolean isParentDockingPort(Component comp);

    /**
     * Removes the specified Component in from the {@code DockingPort}. Returns
     * {@code true} for success and {@code false} for failure.
     */
    boolean undock(Component comp);

    /**
     * Returns the region of this {@code DockingPort} containing the coordinates
     * within the specified {@code Point}. The return value will be one of the
     * regions specified in {@code org.flexdock.util.DockingConstants},
     * including {@code CENTER_REGION}, {@code NORTH_REGION},
     * {@code SOUTH_REGION}, {@code EAST_REGION}, {@code WEST_REGION}, or
     * {@code UNKNOWN_REGION}.
     *
     * @return the region containing the specified {@code Point}.
     */
    String getRegion(Point p);

    /**
     * Returns the value of the property with the specified key. Only properties
     * added with {@code putClientProperty} will return a non-{@code null}
     * value.
     *
     * @param key
     *            the being queried
     * @return the value of this property or {@code null}
     * @see javax.swing.JComponent#getClientProperty(java.lang.Object)
     */
    Object getClientProperty(Object key);

    /**
     * Adds an arbitrary key/value "client property" to this {@code DockingPort}.
     * {@code null} values are allowed.
     *
     * @see javax.swing.JComponent#putClientProperty(java.lang.Object,
     *      java.lang.Object)
     */
    void putClientProperty(Object key, Object value);

    /**
     * Returns a {@code DockingPortPropertySet} instance associated with this
     * {@code DockingPort}. Developers implementing the {@code DockingPort}
     * interface may or may not choose to provide their own
     * {@code DockingPortPropertySet} implementation for use with this method. A
     * default implementation is supplied by the framework and most
     * {@code DockingPort} implementations, including all implementations
     * provided by the framework, will return the default
     * {@code DockingPortPropertySet} via a call to
     * {@code org.flexdock.docking.props.PropertyManager}. Developers are
     * encouraged to take advantage of this by calling
     * {@code PropertyManager.getDockingPortPropertySet(this)}.
     *
     * @return the {@code DockingPortPropertySet} associated with this
     *         {@code DockingPort} This method may not return a {@code null}
     *         reference.
     * @see org.flexdock.docking.props.DockingPortPropertySet
     * @see org.flexdock.docking.props.PropertyManager#getDockingPortPropertySet(DockingPort)
     */
    DockingPortPropertySet getDockingProperties();

    /**
     * Returns the {@code DockingStrategy} instance used by this
     * {@code DockingPort} for docking operations.
     *
     * @see DockingStrategy
     */
    DockingStrategy getDockingStrategy();

    /**
     * Returns a {@code Set} of all {@code Dockables} presently contained by
     * this {@code DockingPort}.
     *
     * @return a {@code Set} of {@code Dockables} contained by this
     *         {@code DockingPort}. If the {@code DockingPort} contians no
     *         {@code Dockables}, and empty {@code Set} is returned. This
     *         method may not return a {@code null} reference.
     */
    Set getDockables();

    /**
     * Returns a boolean indicating whether or not this {@code DockingPort} is
     * nested within another {@code DockingPort}. If there are no other
     * {@code DockingPorts} within this {@code DockingPort's} container ancestor
     * hierarchy, then this method will return {@code true}. Otherwise, this
     * method will return {@code false}. If the this {@code DockingPort} is not
     * validated and/or is not part of a container hierarchy, this method should
     * return {@code true}.
     */
    boolean isRoot();

    /**
     * Examines a {@code LayoutNode} and constructs a corresponding component
     * hierarchy to match the specified layout. The supplied {@code LayoutNode}
     * will contain metadata describing a layout of {@code Dockables},
     * including relative sizes, split proportions, tabbing sequences, etc. This
     * {@code DockingPort} is reponsible for constructing a valid
     * {@code Dockable} component layout based upon the metadata contained
     * within the supplied {@code LayoutNode}
     *
     * @param node
     *            the {@code LayoutNode} describing the layout to construct
     * @see org.flexdock.docking.state.LayoutNode
     * @see #exportLayout()
     */
    void importLayout(LayoutNode node);

    /**
     * Returns a {@code LayoutNode} containing metadata that describes the
     * current layout contained within this {@code DockingPort}. The returned
     * {@code LayoutNode} should be structured such that a subsequent call to
     * {@code importLayout()} on the same {@code DockingPort} should construct a
     * visual component layout identical to that which currently exists in this
     * {@code DockingPort}
     *
     * @return a {@code LayoutNode} representing the current layout state within
     *         this {@code DockingPort}
     * @see org.flexdock.docking.state.LayoutNode
     * @see #importLayout(LayoutNode)
     */
    LayoutNode exportLayout();

    // --- Maximization

    /**
     * Asks this {@code DockingPort} to temporarily release its child
     * {@code Dockable} for use by another {@code DockingPort} to achieve
     * maximization. This method is called by {@code DockingManager} in the
     * course of maximizing a {@code Dockable}. Client code should not call
     * this method directly.
     * <p>
     * This {@code DockingPort} is expected to remove the specified dockable's
     * component from its swing container hierarchy. Also, this
     * {@code DockingPort} is expected to internally store enough information to
     * restore its current state after a subsequent call to
     * {@link #returnFromMaximization()}.
     *
     * @param dockable
     *            the {@code Dockable} that is requested to be maximized
     * @see DockingManager#toggleMaximized(Component)
     * @see DockingManager#toggleMaximized(Dockable)
     */
    void releaseForMaximization(Dockable dockable);

    /**
     * Notifies this {@code DockingPort} that the {@code Dockable} previously
     * released for maximization via a call to
     * {@link #releaseForMaximization(Dockable)} is now ready to be returned to
     * its original state inside this {@code DockingPort}. This method is
     * called by {@code DockingManager} in the course of restoring a maximized
     * {@code Dockable}. Client code should not call this method directly.
     *
     * @see DockingManager#toggleMaximized(Component)
     * @see DockingManager#toggleMaximized(Dockable)
     */
    void returnFromMaximization();

    /**
     * Asks this {@code DockingPort} to temporarily install the specified
     * {@code Dockable} and maximize its component. This method is called by
     * {@code DockingManager} in the course of maximizing a {@code Dockable}.
     * Client code should not call this method directly.
     * <p>
     * This {@code DockingPort} is expected to display the specified dockable's
     * component such that it occupies all (or the majority) of its screen
     * resources. Also, this {@code DockingPort} is expected to internally store
     * enough information to restore its current state after a subsequent call
     * to {@link #uninstallMaximizedDockable()}.
     *
     * @param dockable
     *            the {@code Dockable} that is requested to be maximized
     * @see DockingManager#toggleMaximized(Component)
     * @see DockingManager#toggleMaximized(Dockable)
     */
    void installMaximizedDockable(Dockable dockable);

    /**
     * Notifies this {@code DockingPort} that the {@code Dockable} previously
     * installed for maximization via a call to
     * {@link #installMaximizedDockable(Dockable)} should now be returned to its
     * original {@code DockingPort} and that this {@code DockingPort} should
     * return to its original state from before the call to
     * {@link #installMaximizedDockable(Dockable)}. This method is called by
     * {@code DockingManager} in the course of restoring a maximized
     * {@code Dockable}. Client code should not call this method directly.
     * <p>
     * This {@code DockingPort} is expected to remove the maximized dockable's
     * component from its swing container hierarchy.
     *
     * @see DockingManager#toggleMaximized(Component)
     * @see DockingManager#toggleMaximized(Dockable)
     */
    void uninstallMaximizedDockable();
}
