package slides;

import java.awt.*;
import java.util.*;

/**
 * A LayoutManager that treats each component in a container 
 * as if it is on a page of a flip chart.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FlipChartLayout implements LayoutManager
{
    private LinkedList       components;


    private static final int MINIMUM_SIZE   = 0;
    private static final int PREFERRED_SIZE = 1;


    /**
     * Default Constructor
     */
    public FlipChartLayout() 
    {
       components = new LinkedList();
    }



    /**
     * Adds the specified component to this LayoutManager.
     * (Required by LayoutManager)
     *
     * @param name The name of the component (not used)
     * @param comp The component to be added
     */
    public void addLayoutComponent(String name, Component comp) 
    {
       synchronized (comp.getTreeLock()) 
       {
          if   (components.isEmpty()) comp.setVisible(true);
          else                        comp.setVisible(false);
          components.addLast(comp);
       }
    }




    /**
     * Flips the pages backward by one
     *
     * @param parent   The parent container
     */
    public void backward(Container parent) 
    {
       Component     comp;

       synchronized (parent.getTreeLock()) 
       {
          // Throw an Exception if the parent isn't using this layout
          checkLayout(parent);

          if (!components.isEmpty()) 
          {
             comp = (Component)components.getFirst();
             comp.setVisible(false);
             comp = (Component)components.removeLast();
             comp.setVisible(true);
             components.addFirst(comp);
             parent.validate();
          }
       }
    }





    /**
     * Determines either maximum (of each dimension) 
     * of the components in the container.
     *
     * @param  parent  The name of the parent container
     * @return         The preferred size
     */
    private Dimension calculateLayoutSize(Container parent, int type) 
    {
       Component    comp;
       Dimension    d;
       Insets       insets;
       int          w, h;
       Iterator     iter;

       synchronized (parent.getTreeLock()) 
       {
          // Throw an Exception if the parent isn't using this layout
          checkLayout(parent);

          // Initialize the width and height
          w = 0;
          h = 0;

          // Get the insets (i.e., unused space) for the parent
          insets = parent.getInsets();

          // Calculate the maximum (along each dimension)
          iter = components.iterator();
          while (iter.hasNext()) 
          {
             comp = (Component)iter.next();

             if (type == PREFERRED_SIZE) d = comp.getPreferredSize();
             else                        d = comp.getMinimumSize();

             if (d.width  > w) w = d.width;
             if (d.height > h) h = d.height;
          }

          return new Dimension(insets.left + insets.right + w,
                               insets.top + insets.bottom + h);
       }
    }





    /**
     * Make sure that the parent is actually using this
     * FlipChartLayout
     *
     * @param parent   The parent container
     */
    private void checkLayout(Container parent) 
    {
       if ((parent == null) || (parent.getLayout() != this)) 
       {
          throw new IllegalArgumentException("The parent container "+
                                             "is not using a "+
                                             "FlipChartLayout");
       }
    }




    /**
     * Flips the pages forward by one
     *
     * @param parent   The parent container
     */
    public void forward(Container parent) 
    {
       Component     comp;

       synchronized (parent.getTreeLock()) 
       {
          // Throw an Exception if the parent isn't using this layout
          checkLayout(parent);

          if (!components.isEmpty()) 
          {
             comp = (Component)components.removeFirst();
             comp.setVisible(false);
             components.addLast(comp);
             comp = (Component)components.getFirst();
             comp.setVisible(true);
             parent.validate();
          }
       }
    }



    /**
     * Gets the current front/first page
     *
     * @param parent   The parent container
     */
    public Component getFirst(Container parent) 
    {
       Component     comp;

       synchronized (parent.getTreeLock()) 
       {
          // Throw an Exception if the parent isn't using this layout
          checkLayout(parent);

          comp = null;
          if (!components.isEmpty()) 
          {
             comp = (Component)components.getFirst();
          }

          return comp;
       }
    }



    /**
     * Lays out the specified container.
     * (Required by LayoutManager)
     *
     * Specifically, the first component is reshaped
     * to be the size of the container, minus space for surrounding
     * insets.
     *
     * @param parent  The parent container
     */
    public void layoutContainer(Container parent) 
    {
       Component    first;
       Dimension    d;
       Insets       insets;

       synchronized (parent.getTreeLock()) 
       {
          // Throw an Exception if the parent isn't using this layout
          checkLayout(parent);

          if (!components.isEmpty()) 
          {
             d      = parent.getSize();
             insets = parent.getInsets();

             first  = (Component)components.getFirst();

             first.setBounds(insets.left, insets.top,
                             d.width - (insets.left + insets.right),
                             d.height - (insets.top + insets.bottom));

             first.setVisible(true);
          }
       }
    }



    /**
     * Calculates the minimum size for the specified panel.
     * (Required by LayoutManager)
     *
     * In this case, it is simply the maximum of the minimum sizes
     * of the "pages" of the FlipChart.
     *
     * @param  parent  The name of the parent container
     * @return         The preferred size
     */
    public Dimension minimumLayoutSize(Container parent) 
    {
       return calculateLayoutSize(parent, MINIMUM_SIZE);
    }




    /**
     * Determines the preferred size of the container.
     * (Required by LayoutManager)
     *
     * In this case, it is simply the maximum of the preferred sizes
     * of the "pages" of the FlipChart.
     *
     * @param  parent  The name of the parent container
     * @return         The preferred size
     */
    public Dimension preferredLayoutSize(Container parent) 
    {
       return calculateLayoutSize(parent, PREFERRED_SIZE);
    }



    /**
     * Removes the specified component from the layout.
     * (Required by LayoutManager)
     *
     * @param   comp   The component to be removed
     */
    public void removeLayoutComponent(Component comp) 
    {
       Component   first;
       int         index;

       synchronized (comp.getTreeLock()) 
       {
          if (!components.isEmpty()) 
          {
             first = (Component)components.getFirst();
             if (first == comp) 
             {
                comp.setVisible(false);
                components.removeFirst();

                if (!components.isEmpty()) 
                {
                   first = (Component)components.getFirst();
                   first.setVisible(true);
                }
             } 
             else 
             {
                components.remove(comp);
             }
          }
       }
    }

}

