/*
 * Created on May 31, 2005
 */
package org.flexdock.docking.floating.policy;

import java.util.Set;

import org.flexdock.docking.Dockable;
import org.flexdock.docking.DockingPort;
import org.flexdock.docking.event.DockingEvent;
import org.flexdock.docking.floating.frames.FloatingDockingPort;
import org.flexdock.docking.floating.policy.FloatPolicy.NullFloatPolicy;

/**
 * This class provides an implementation of the {@code FloatPolicy} interface to
 * provide default behavior for the framework. It blocks floating operations for
 * {@code Dockables} without any frame drag sources, for already floating
 * {@code Dockables} that cannot be reparented within a new dialog, or if global
 * floating support has been disabled.
 *
 * @author Christopher Butler
 */
public class DefaultFloatPolicy extends NullFloatPolicy {

    private static final DefaultFloatPolicy SINGLETON = new DefaultFloatPolicy();

    /**
     * Returns a singleton instance of {@code DefaultFloatPolicy}.
     *
     * @return a singleton instance of {@code DefaultFloatPolicy}.
     */
    public static DefaultFloatPolicy getInstance() {
        return SINGLETON;
    }

    /**
     * Checks the previous {@code DockingPort} for the specified
     * {@code DockingEvent} and returns {@code false} if it is in a floating
     * dialog and contains less than two {@code Dockables}. A floating dialog
     * may contain multiple {@code Dockables}, each of which may be dragged out
     * of the current dialog to float in their own dialog. However, if a
     * floating dialog only contains a single {@code Dockable}, it makes no
     * sense to remove the {@code Dockable} only to float it within another
     * dialog. This situation is caught by this method and the docking operation
     * is blocked.
     *
     * @param evt
     *            the {@code DockingEvent} to be checked for drop-to-float
     *            support
     * @return {@code false} if the {@code DockingEvent} is attempting to float
     *         an already floating {@code Dockable} with no other
     *         {@code Dockables} in its current dialog; {@code true} otherwise.
     * @see FloatPolicy#isFloatDropAllowed(DockingEvent)
     * @see DockingEvent#getOldDockingPort()
     * @see FloatingDockingPort#getDockableCount()
     * @see DockingEvent#consume()
     */
    public boolean isFloatDropAllowed(DockingEvent evt) {
        DockingPort oldPort = evt.getOldDockingPort();
        // if we're already floating, and we're the only dockable
        // in a floating dockingport, then we don't want to undock
        // from the port and re-float (dispose and create a new DockingFrame).
        if (oldPort instanceof FloatingDockingPort) {
            FloatingDockingPort dockingPort = (FloatingDockingPort) oldPort;
            if (dockingPort.getDockableCount() < 2) {
                evt.consume();
                return false;
            }
        }

        return super.isFloatDropAllowed(evt);
    }

    /**
     * Blocks floating support (returns false) if {@code dockable} is
     * {@code null}, if {@code FloatPolicyManager.isGlobalFloatingEnabled()}
     * returns {@code false}, or if there are no entries within the {@code Set}
     * returned by {@code dockable.getFrameDragSources()}. Otherwise, this
     * method returns {@code true}.
     *
     * @param dockable
     *            the {@code Dockable} to be checked for floating support
     * @return {@code false} if floating is blocked for the specified
     *         {@code Dockable}; {@code true} otherwise.
     * @see Dockable#getFrameDragSources()
     * @see FloatPolicyManager#isGlobalFloatingEnabled()
     */
    public boolean isFloatingAllowed(Dockable dockable) {
        if (dockable == null || !FloatPolicyManager.isGlobalFloatingEnabled())
            return false;

        Set frameDragSources = dockable.getFrameDragSources();
        if (frameDragSources == null || frameDragSources.size() == 0)
            return false;

        return super.isFloatingAllowed(dockable);
    }
}
