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

import javax.swing.*;
import javax.swing.event.*;
import java.net.URL;

/**
 * An IconStampPad consists of a list of small images, drawing area where 
 * the user can "stamp" copies of the images, and a few control buttons.
 * The images appear in a scrolling list to the right of the drawing
 * areay.  The user clicks an image in the list to select it, then
 * clicks on the drawing area to place copies of the selected image.
 */
public class IconStampPad extends JPanel implements ActionListener {
	
	// -------------- The data that represents the content of the display area

	    // TODO: Add support for an array of icons, including arrays of icon coordinates
		
	private int iconData = -1;  // An index into the iconImages array that indicates which
	                             // icon is currently displayed in the display panel; -1 is
	                             // not a legal index and it indicates that no icon is displayed.

	private int iconX;  // X-coordinate in the display panel where the icon is displayed.
	private int iconY;  // Y-coordinate in the display panel where the icon is displayed.
	

	//------------------------ Additional instance variables --------------------------------

	private JButton saveButton;  // A button for saving current image data to a file.
	private JButton loadButton;  // A button for loading image data from a previously saved file.
	private JButton undoButton;  // A button for removing the most recently added image.
	
	private IconDisplayPanel displayPanel;  // The dispaly panel.  The IconsDisplayPanel class is
	                                         // a nested class inside IconStampPad, and is defined below.
	
	private Image[] iconImages = new Image[35];  // The little images that can be "stamped".
	
	private int selectedIcon = 0;  // The index of the currently selected icon.  A copy of
	                                // the image iconImages[selectedIcon] is added to the display
	                                // area when the user clicks on the display area.

	
	//--------------- The Icon Display Panel class, representing the display area ----------
	
	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);  // Fill with background color.
			// TODO: Add support for an array of icons
			if (iconData >= 0) {
				g.drawImage(iconImages[iconData], iconX, iconY, this);  // Draw the single icon
			}
		}

		/**
		 * 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) { 
			// TODO: Add support for an array of icons
			iconX = e.getX() - 16;  // Offset x-coord so center of icon is at the point that was clicked.
			iconY = e.getY() - 16;  // Offset y-coord too.
			iconData = selectedIcon;
			repaint();  // Tell system to redraw the image, with the new data
		}

		public void mouseClicked(MouseEvent e) { }   // Not used, bu 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 the three control buttons.
	 */
	public IconStampPad() {

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

		displayPanel = new IconDisplayPanel();   // Create and configure the display panel
		displayPanel.setPreferredSize(new Dimension(650,500));
		displayPanel.setBackground( new Color(230,230,255) );  // Very light blue.
		displayPanel.addMouseListener(displayPanel);
		add(displayPanel,BorderLayout.CENTER);  // Add display panel to main panel.

		saveButton = new JButton("Save");  // Create buttons and set up action listeners.
		loadButton = new JButton("Load");
		undoButton = new JButton("Undo");
		saveButton.addActionListener(this);
		loadButton.addActionListener(this);
		undoButton.addActionListener(this);

		JPanel buttonPanel = new JPanel();  // Create a panel with a GridLayout to hold the buttons.
		buttonPanel.setLayout(new GridLayout(1,3));
		buttonPanel.add(saveButton);
		buttonPanel.add(loadButton);
		buttonPanel.add(undoButton);
		add(buttonPanel, BorderLayout.SOUTH);   // Add the button panel to the main panel.
		
		JScrollPane iconList = createScrollingIconList();  // Create the scrolling list of images.
		add( iconList, BorderLayout.EAST );  // Add the image list to the main panel.

	}
	
	/**
	 * This method will be called when the user clicks the "Undo" button.  It
	 * removes the most recently added icon (if any) from the display panel.
	 *
	 */
	private void doUndo() {
		// TODO: Add support for an array of icons
		iconData = -1;  // (Says there is no icon in the image.)
		displayPanel.repaint();  // Tell system to repaint the display panel with the new data.
	}
	
	/**
	 * When the user clicks the "Save" button, the saveFile() method is called.  The
	 * saveFile() method, in turn, calls this method after the output file has
	 * already been selected by the user and opened.  Data can be written to the
	 * file using the FileIO class, which works just like TextIO except that the
	 * data that is written will go to the file rather than to standard output.
	 */
	private void writeDataToFileIO() {
		// TODO: Add support for an array of icons
		FileIO.putln(iconData + " " + iconX + " " + iconY);
	}
	
	/**
	 * When the user clicks the "Load" button, the loadFile() method is called.  The
	 * loadFile() method, in turn, calls this method after the input file has
	 * already been selected by the user and opened.  Data can be read from the
	 * file using the FileIO class, which works just like TextIO except that the
	 * data that is read will come from the file rather than from standard output.
	 * When an input error, such as an illegal integer value, occurs during reading,
	 * FileIO will throw an IllegalArgumentException -- such errors will be caught
	 * and handled by the loadFile() method, so you don't have to worry about them.
	 * However, you might want to throw an exception yourself in some cases.
	 */
	private void readDataFromFileIO() {
		// TODO: Add support for an array of icons
		iconData = FileIO.getInt();
		if (iconData < 0 || iconData >= iconImages.length)
			throw new IllegalArgumentException("Illegal icon number (" + iconData + ") found in file.");
		iconX = FileIO.getInt();
		iconY = FileIO.getlnInt();
	}
	

	
	//------------- implementation details that you don't have to bother with ---------------
	
	/**
	 * Called when the user clicks one of the control buttons.  This method
	 * simply routes control to one of the methods saveFile(), loadFile(),
	 * or doUndo().
	 */
	public void actionPerformed(ActionEvent e) {
		Object source = e.getSource();
		if (source == saveButton)
			saveFile();
		else if (source == loadButton)
			loadFile();
		else if (source == undoButton)
			doUndo();
	}

	/**
	 * This will be called when the user clicks the "Save" button.  it asks
	 * the user to select an output file and opens that file.  It calls
	 * the writeDataToFile() method to write  the actual data.
	 */
	private void saveFile() {
		try {
			boolean fileOpened = FileIO.writeUserSelectedFile();
			if ( fileOpened == false )
				return;  // User canceled the file dialog box.
		}
		catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"An error occured while trying to open the file:\n" + e, 
					"Can't Open File", JOptionPane.ERROR_MESSAGE);
		}
		try {
			FileIO.putln("Silly Stamper Data File"); // The presence of this string is tested by the loadFile() method.
			writeDataToFileIO();
			FileIO.writeFile(null);  // Closes the output file.
		}
		catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"An error occured while trying to write to the file:\n" + e, 
					"Can't Write File", JOptionPane.ERROR_MESSAGE);
		}
	}
	
	/**
	 * This will be called when the user clicks the "Load" button.  it asks
	 * the user to select an input file and opens that file.  It calls
	 * the readDataFromFile() method to read the actual data.
	 */
	private void loadFile() {
		try {
			boolean fileOpened = FileIO.readUserSelectedFile();
			if ( fileOpened == false )
				return;
		}
		catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"An error occured while trying to open the file:\n" + e, 
					"Can't Open File", JOptionPane.ERROR_MESSAGE);
		}
		try {
			String firstLine = FileIO.getln();
			if ( ! firstLine.equals("Silly Stamper Data File") ) // Test to make it more likely file is of the correct type.
				throw new IllegalArgumentException("Input file not of correct type.");
			readDataFromFileIO();
			displayPanel.repaint();  // Tell system to redraw the display panel with the new image.
			FileIO.readFile(null);  // Closes the input file.
		}
		catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"An error occured while trying to read to the file:\n" + e, 
					"Can't Read File", JOptionPane.ERROR_MESSAGE);
		}
	}
	
	/**
	 * Create a scrolling list that contains all of the available icon images.
	 * The list itself is actually an object of type JList, but the is placed
	 * into a JScrollPane to add a scroll bar to the list and make it scrollable.
	 */
	private JScrollPane createScrollingIconList() {

		   // First, load the 35 icon images which must be in a folder named "icons"
		   // and must themselves be in files named icon0.png, icon1.png, ..., icon34.png.
		   // Note that if the icon images can't be found, then the program is terminated.

		ClassLoader classLoader = getClass().getClassLoader();
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		try {
			for (int i = 1; i <= 35; i++) {
				URL imageURL = classLoader.getResource("icons/icon" + i + ".png");
				if (imageURL == null)
					throw new Exception();
				iconImages[i-1] = toolkit.createImage(imageURL);
			}
		}
		catch (Exception e) {
			JOptionPane.showMessageDialog(null,"Can't load icon images.  Aborting program");
			System.exit(0);
		}

		   // 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[35];
		for (int i = 0; i < 35; i++)
			icons[i] = new ImageIcon(iconImages[i]);
		final JList list = new JList(icons);  // Makes a list containing all the image icons from the array.
		     // (Note: list is final variable for a technical reason involving the
		     // use of an anonymous nested class below.  A local variable that is used
		     // in an anonymous nested class must be declared to be final.)
		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 by control-clicking.)
		list.setSelectedIndex(0);  // The first item in the list is currently selected.
		
		   // Add a ListSelectionListener to the JList.  This is done using
		   // an "anonymous nested class."  The listener responds when
		   // the user selects a new icon from the list.  The response is
		   // simply to set the value of the selected instance variable to the
		   // index of the newly selected icon.
		
		list.addListSelectionListener( new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				selectedIcon = list.getSelectedIndex();
			}
		});

		return new JScrollPane(list);
	}

	

} // end class IconStampPad


