import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
import java.net.URL;
import java.util.ArrayList;

/**
 * An SillyStamper panel contains a JList of icons, a drawing area where 
 * the user can "stamp" images of the icons, and a few control buttons.
 * The user clicks an icon in the list to select it, then clicks on the drawing
 * area to place copies of the selected image.  This class can be run as a
 * main program, and it contains a nested class that can be used to run
 * the program as an application.
 * 
 * This program requires the icon image files from the stamper_icons directory.
 * (The images were taken from a KDE desktop icon collection.)
 */
public class SillyStamper extends JPanel {

   /**
    * The main routine simply opens a window that shows a SillyStamper panel.
    */
   public static void main(String[] args) {
      JFrame window = new JFrame("Silly Stamper");
      SillyStamper content = new SillyStamper();
      window.setContentPane(content);
      window.pack(); 
      window.setResizable(false); 
      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
      window.setLocation( (screenSize.width - window.getWidth())/2,
            (screenSize.height - window.getHeight())/2 );
      window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      window.setVisible(true);
   }
   
   
   /**
    * The public static class SillyStamper.Applet represents this program
    * as an applet.  The applet's init() method simply sets the content 
    * pane of the applet to be a SillyStamper.  To use the applet on
    * a web page, use code="SillyStamper$Applet.class" as the name of
    * the class.
    */
   public static class Applet extends JApplet {
      public void init() {
         SillyStamper content = new SillyStamper();
         setContentPane( content );
      }
   }
   
   
   /**
    * An object of type IconInfo stores the information needed to draw
    * one icon image on the display area.
    */
   private static class IconInfo {
      int iconNumber;  // an index into the iconImages array.
      int x, y;        // coords of the upper left corner of the image
   }

   /**
    * Contains info for all the icons that have been placed on the
    * display area.  Might contain more than have actually been shown,
    * because of the Undo command.  An icon that is removed from the
    * display area by an undo is not removed from this list.
    */
   private ArrayList<IconInfo> icons = new ArrayList<IconInfo>();

   private int iconsShown;  // Number of icons shown in the display area.
   private int iconsPlaced; // Number of icons that have been placed.  Can be
                            //  greater than iconsShown, because of undo/redo.
   
   private JList iconList;  // The JList from which the user selects the icon for stamping.

   private JButton undoButton;  // A button for removing the most recently added image.
   private JButton redoButton;  // A button for restoring the most recently removed image.

   private IconDisplayPanel displayPanel;  // The dispaly panel.  The IconsDisplayPanel class is
                                            // a nested class, and is defined below.

   private Image[] iconImages;  // The little images that can be "stamped".




   /**
    * This class represents the drawing area where the user can stamp images.
    */
   private class IconDisplayPanel extends JPanel implements MouseListener {

      /**
       * Draws the display panel, based on the data about what icons are
       * to be displayed there and what their coordinates are.
       */
      public void paintComponent(Graphics g) {
         super.paintComponent(g); 
         if (iconImages == null) {
            g.drawString("Can't load icons.", 10, 30);
            return;
         }
         for (int i = 0; i < iconsShown; i++) {
            IconInfo info = icons.get(i);
            g.drawImage(iconImages[info.iconNumber], info.x, info.y, this);
         }
      }

      /**
       * When the user clicks the display panel, place a copy of the currently selected
       * icon image at the point where the user clicked.
       */
      public void mousePressed(MouseEvent e) { 
         IconInfo info  = new IconInfo();
         info.iconNumber = iconList.getSelectedIndex();
         info.x = e.getX() - 16;  // Offset x-coord, so center of icon is at the point that was clicked.
         info.y = e.getY() - 16;  // Offset y-coord too.
         if (iconsShown == icons.size())
            icons.add(info);
         else
            icons.set(iconsShown, info);
         iconsShown++;
         iconsPlaced = iconsShown;
         redoButton.setEnabled(false);
         undoButton.setEnabled(true);
         repaint();  // Tell system to redraw the image, with the new data
      }

      public void mouseClicked(MouseEvent e) { }   // Not used, but required by MouseListener interface.
      public void mouseEntered(MouseEvent e) { }
      public void mouseExited(MouseEvent e) { }
      public void mouseReleased(MouseEvent e) { }

   } // end nested class IconDisplayPanel


   /**
    * The constructor sets up a BorderLayout on the panel with a display panel
    * in the CENTER position, a list of icon images in the EAST position, and 
    * a JPanel in the SOUTH position that contains two control buttons.
    */
   public SillyStamper() {

      setLayout(new BorderLayout(2,2));   // Set basic properties of this panel.
      setBackground(Color.GRAY);
      setBorder(BorderFactory.createLineBorder(Color.GRAY,2));

      displayPanel = new IconDisplayPanel();   // Create and configure the display panel
      displayPanel.setPreferredSize(new Dimension(400,300));
      displayPanel.setBackground( new Color(220,220,255) );  // Very light blue.
      displayPanel.addMouseListener(displayPanel);
      add(displayPanel,BorderLayout.CENTER);  

      iconList = createIconList();  // Create the scrolling list of icons.
      if (iconList == null)
         return;
      add( new JScrollPane(iconList), BorderLayout.EAST );  

      Action undoAction = new AbstractAction("Undo") {
         public void actionPerformed(ActionEvent evt) {
            if (iconsShown > 0) {
               iconsShown--;
               redoButton.setEnabled(true);
               displayPanel.repaint();
            }
         }
      };

      Action redoAction = new AbstractAction("Redo") {
         public void actionPerformed(ActionEvent evt) {
            if (iconsShown < iconsPlaced) {
               iconsShown++;
               if (iconsShown == iconsPlaced)
                  redoButton.setEnabled(false);
               undoButton.setEnabled(true);
               displayPanel.repaint();
            }
         }
      };

      undoButton = new JButton(undoAction);
      redoButton = new JButton(redoAction);
      undoButton.setEnabled(false);
      redoButton.setEnabled(false);

      JPanel buttonPanel = new JPanel();  
      buttonPanel.add(undoButton);
      buttonPanel.add(redoButton);
      add(buttonPanel, BorderLayout.SOUTH);  

   }



   /**
    * Create a JList that contains all of the available icon images.
    */
   private JList createIconList() {
      String[] iconNames = new String[] {
         "icon5.png", "icon7.png", "icon8.png", "icon9.png", "icon10.png", "icon11.png", 
         "icon24.png", "icon25.png", "icon26.png", "icon31.png", "icon33.png", "icon34.png"

      };

      iconImages = new Image[iconNames.length];

      ClassLoader classLoader = getClass().getClassLoader();
      Toolkit toolkit = Toolkit.getDefaultToolkit();
      try {
         for (int i = 0; i < iconNames.length; i++) {
            URL imageURL = classLoader.getResource("stamper_icons/" + iconNames[i]);
            if (imageURL == null)
               throw new Exception();
            iconImages[i] = toolkit.createImage(imageURL);
         }
      }
      catch (Exception e) {
         iconImages = null;
         return null;
      }

         // Create an array of objects of type ImageIcon.  This is required for
         // creating the JList.  It is easy to create a JList from an array
         // of ImageIcons -- just pass the array as a parameter to the constructor.
         // (You could do the same thing with an array of Strings, to get a list
         // of strings.  But JLists can't use other types so easily.)

      ImageIcon[] icons = new ImageIcon[iconImages.length];
      for (int i = 0; i < iconImages.length; i++)
         icons[i] = new ImageIcon(iconImages[i]);
      
      JList list = new JList(icons); // Makes a list containing the image icons from the array.
      
      list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
          // (Note:  With the default selection mode, it would be possible for the user
          // to select several list items at the same time or to select no item at all.)
      
      list.setSelectedIndex(0);  // The first item in the list is currently selected.
      
      return list;
   }



} // end class SillyStamper

