/*
 * Copyright (c) 2005 Andrew Pietsch. All Rights Reserved.
 */
package org.pietschy.command.interceptor;

import org.pietschy.command.ActionCommand;
import org.pietschy.command.ActionCommandInterceptor;

import javax.swing.*;
import java.awt.*;

/**
 * GlassPaneInterceptor provides a simple interceptor that activates the glass pane before
 * a command executes and deactivates it afterward.  This makes it easy block user
 * interaction while the command is executing making it easy to use libraries
 * like Foxtrot.
 * <p>
 * Generally this interceptor would be shared by all commands that need it.  To this
 * end the static methods {@link #install(org.pietschy.command.ActionCommand)} and
 * {@link #uninstall(org.pietschy.command.ActionCommand)} are provided for
 * your convenience.
 * <pre>
 * public class FoxtrotCommand extends ActionCommand
 * {
 *    public FoxtrotCommand(CommandManager cm, String id)
 *    {
 *       super(cm, id);
 *       GlassPaneInterceptor.install(this);
 *    }
 *
 *    public void handleExecute()
 *    {
 *       // the glass pane will be activated by the interceptor...
 *
 *       Worker.post(...);
 *
 *       // ..and removed when we're finished.
 *    }
 * }
 * </pre>
 * <p>
 * You can configure appearance of the glass pane by installing a custom instance of {@link GlassPaneHandler}.
 *
 * @author andrewp
 * @version $Revision: 1.5 $
 */
public class
GlassPaneInterceptor
implements ActionCommandInterceptor
{
   public static final GlassPaneInterceptorStrategy STACK_STRATEGY = new StackStrategy();
   public static final GlassPaneInterceptorStrategy COUNTER_STRATEGY = new CounterStrategy();

   public static GlassPaneInterceptor SHARED_INSTANCE = new GlassPaneInterceptor();


   /**
    * A convenience method for adding the shared interceptor to the specified command.
    *
    * @param command the command to which the interceptor is to be added.
    */
   public static void
   install(ActionCommand command)
   {
      command.addInterceptor(SHARED_INSTANCE);
   }

   /**
    * A convenience method for removing the shared interceptor from the specified command.
    *
    * @param command the command from which the interceptor is to be removed.
    */
   public static void
   uninstall(ActionCommand command)
   {
      command.removeInterceptor(SHARED_INSTANCE);
   }

   /**
    * Gets the shared instance of this interceptor.
    * @return the shared instance of this interceptor.
    */
   public static GlassPaneInterceptor
   sharedInstance()
   {
      return SHARED_INSTANCE;
   }


   private GlassPaneInterceptorStrategy strategy = STACK_STRATEGY;

   private GlassPaneHandler glassPaneHandler = new DefaultGlassPaneHandler();


   private GlassPaneInterceptor()
   {
   }

   /**
    * Creates a new interceptor that uses the specified component as the glass pane.
    * @param glassPane the glass pane to use.
    */
   public GlassPaneInterceptor(Component glassPane)
   {
      setGlassPaneHandler(new CustomComponentGlassPaneHandler(glassPane));
   }

   /**
    * Get the commands invoking window and activates its glass pane.
    * @param command the command whose window is to be blocked.
    * @return <code>true</code>.
    */
   public boolean
   beforeExecute(ActionCommand command)
   {
      Window window = command.getInvokerWindow();
      if (window != null)
      {
         block(window);
      }

      return true;
   }

   /**
    * Get the commands invoking window and deactivates its glass pane.
    * @param command the command whose window is to be unblocked.
    */
   public void
   afterExecute(ActionCommand command)
   {
      Window window = command.getInvokerWindow();
      if (window != null)
      {
         unblock(window);
      }
   }


   /**
    * Sets the {@link GlassPaneInterceptorStrategy} this interceptor is to use.
    * @param modalstrategy the {@link GlassPaneInterceptorStrategy} this interceptor is to use.
    */
   public void
   setStrategy(GlassPaneInterceptorStrategy modalstrategy)
   {
      strategy = modalstrategy;
   }

   public void
   setGlassPaneHandler(GlassPaneHandler glassPaneHandler)
   {
      this.glassPaneHandler = glassPaneHandler;
   }


   private void
   block(Component component)
   {
      RootPaneContainer rootpanecontainer = getRootPaneContainerFor(component);

      if (strategy.isFirstBlockRequest(rootpanecontainer, component))
         glassPaneHandler.activateGlassPane(rootpanecontainer);
   }

   private void
   unblock(Component component)
   {
      RootPaneContainer rootpanecontainer = getRootPaneContainerFor(component);
      if (strategy.isLastUnblockRequest(rootpanecontainer, component))
         glassPaneHandler.deactivateGlassPane(rootpanecontainer);
   }

   private static RootPaneContainer
   getRootPaneContainerFor(Component component)
   {
      if (component instanceof RootPaneContainer)
         return (RootPaneContainer) component;
      java.awt.Window window = SwingUtilities.getWindowAncestor(component);
      if (window instanceof RootPaneContainer)
         return (RootPaneContainer) window;
      else
         throw new IllegalStateException("Component is niether an instance of RootPaneContianer or contained within one.");
   }

}
