package org.newdawn.slick.tools.packulike;

import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;

/**
 * A simple GUI on the front of the packing tool
 * 
 * @author kevin
 */
public class Packer extends JFrame {
	/** The panel showing the currently generated sprite sheet */
	private SheetPanel sheetPanel;
	/** The sprites currently displayed */
	private DefaultListModel sprites = new DefaultListModel();
	/** The visual list of the sprites */
	private JList list = new JList(sprites);
	/** The width of the texture being generated */
	private int twidth;
	/** The height of the texture being generated */
	private int theight;
	
	/** The width texture sizes model */
	private DefaultComboBoxModel sizes = new DefaultComboBoxModel();
	/** The height texture sizes model */
	private DefaultComboBoxModel sizes2 = new DefaultComboBoxModel();
	/** The visual selection for texture width */
	private JComboBox widths = new JComboBox(sizes);
	/** The visual selection for texture height */
	private JComboBox heights = new JComboBox(sizes2);
	/** The visual selection for border size */
	private JSpinner border = new JSpinner(new SpinnerNumberModel(0,0,50,1));
	
	/** The chooser used to select sprites */
	private JFileChooser chooser = new JFileChooser(".");
	/** The chooser used to save the sprite sheet */
	private JFileChooser saveChooser = new JFileChooser(".");
	/** The packing tool */
	private Pack pack = new Pack();
	
	/**
	 * Create the new GUI for packer
	 */
	public Packer() {
		super("Pack-U-Like");
		
		saveChooser.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory()) {
					return true;
				}
				
				return (f.getName().endsWith(".png"));
			}

			public String getDescription() {
				return "PNG Images (*.png)";
			}
			
		});
		chooser.setMultiSelectionEnabled(true);
		chooser.setFileFilter(new FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory()) {
					return true;
				}
				
				return (f.getName().endsWith(".png") ||
						f.getName().endsWith(".jpg") ||
						f.getName().endsWith(".gif"));
			}

			public String getDescription() {
				return "Images (*.jpg, *.png, *.gif)";
			}
			
		});
		
		sizes.addElement(new Integer(64));
		sizes.addElement(new Integer(128));
		sizes.addElement(new Integer(256));
		sizes.addElement(new Integer(512));
		sizes.addElement(new Integer(1024));
		sizes.addElement(new Integer(2048));
		sizes2.addElement(new Integer(64));
		sizes2.addElement(new Integer(128));
		sizes2.addElement(new Integer(256));
		sizes2.addElement(new Integer(512));
		sizes2.addElement(new Integer(1024));
		sizes2.addElement(new Integer(2048));
		
		JMenuBar bar = new JMenuBar();
		JMenu file = new JMenu("File");
		bar.add(file);
		JMenuItem save = new JMenuItem("Save");
		file.add(save);
		file.addSeparator();
		JMenuItem quit = new JMenuItem("Quit");
		file.add(quit);
		save.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				save();
			}
		});
		quit.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		
		setJMenuBar(bar);
		JPanel panel = new JPanel();
		panel.setLayout(null);
	
		sheetPanel = new SheetPanel(this);
		JScrollPane pane = new JScrollPane(sheetPanel);
		pane.setBounds(5,5,530,530);
		JScrollPane listScroll = new JScrollPane(list);
		listScroll.setBounds(540,5,200,350);
		list.addListSelectionListener(new ListSelectionListener() {

			public void valueChanged(ListSelectionEvent e) {
				Object[] values = list.getSelectedValues();
				ArrayList sprites = new ArrayList();
				for (int i=0;i<values.length;i++) {
					sprites.add(values[i]);
				}
				
				list.removeListSelectionListener(this);
				select(sprites);
				list.addListSelectionListener(this);
			}
		});
		list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		list.setCellRenderer(new FileListRenderer());
		panel.add(pane);
		panel.add(listScroll);
		
		JButton add = new JButton("Add");
		add.setFont(add.getFont().deriveFont(Font.BOLD));
		add.setMargin(new Insets(0,0,0,0));
		add.setBounds(745,5,40,30);
		panel.add(add);
		
		add.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int resp = chooser.showOpenDialog(Packer.this);
				if (resp == JFileChooser.APPROVE_OPTION) {
					File[] selected = chooser.getSelectedFiles();
					for (int i=0;i<selected.length;i++) {
						try {
							sprites.addElement(new Sprite(selected[i]));
						} catch (IOException x) {
							x.printStackTrace();
							JOptionPane.showMessageDialog(Packer.this, "Unable to load: "+selected[i].getName());
						}
					}
				}
				regenerate();
			}
		});
		JButton remove = new JButton("Del");
		remove.setFont(add.getFont().deriveFont(Font.BOLD));
		remove.setMargin(new Insets(0,0,0,0));
		remove.setBounds(745,35,40,30);
		panel.add(remove);
		remove.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Object[] selected = list.getSelectedValues();
				for (int i=0;i<selected.length;i++) {
					sprites.removeElement(selected[i]);
				}
				regenerate();
			}
		});
		
		JLabel label;

		label = new JLabel("Border");
		label.setBounds(540,375,200,25);
		panel.add(label);
		border.setBounds(540,400,200,25);
		panel.add(border);
		border.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent e) {
				regenerate();
			}
		});
		
		label = new JLabel("Width");
		label.setBounds(540,425,200,25);
		panel.add(label);
		widths.setBounds(540,450,200,25);
		panel.add(widths);
		widths.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				twidth = ((Integer) widths.getSelectedItem()).intValue();
				sheetPanel.setTextureSize(twidth, theight);
				regenerate();
			}
		});
		
		label = new JLabel("Height");
		label.setBounds(540,475,200,25);
		panel.add(label);
		heights.setBounds(540,500,200,25);
		heights.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				theight = ((Integer) heights.getSelectedItem()).intValue();
				sheetPanel.setTextureSize(twidth, theight);
				regenerate();
			}
		});
		
		panel.add(heights);
		
		twidth = 512;
		theight = 512;
		sheetPanel.setTextureSize(twidth, theight);
		widths.setSelectedItem(new Integer(twidth));
		heights.setSelectedItem(new Integer(theight));
		
		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		setContentPane(panel);
		setSize(800,600);
		setResizable(false);
		setVisible(true);
	}
	
	/**
	 * Get the sprite a given location on the current sheet
	 * 
	 * @param x The x coordinate to look for the sprite
	 * @param y The y coordinate to look for the sprite
	 * @return The sprite found at the given location or null if no sprite can be found
	 */
	public Sprite getSpriteAt(int x, int y) {
		for (int i=0;i<sprites.size();i++) {
			if (((Sprite) sprites.get(i)).contains(x,y)) {
				return ((Sprite) sprites.get(i));
			}
		}
		
		return null;
	}
	
	/**
	 * Select a series of sprites
	 * 
	 * @param selection The series of sprites to be selected (Sprite objects)
	 */
	public void select(ArrayList selection) {
		list.clearSelection();
		int[] selected = new int[selection.size()];
		for (int i=0;i<selection.size();i++) {
			selected[i] = sprites.indexOf(selection.get(i));
		}
		list.setSelectedIndices(selected);
		
		sheetPanel.setSelection(selection);
	}
	
	/**
	 * Save the sprite sheet
	 */
	private void save() {
		int resp = saveChooser.showSaveDialog(this);
		if (resp == JFileChooser.APPROVE_OPTION) {
			File out = saveChooser.getSelectedFile();
			
			ArrayList list = new ArrayList();
			for (int i=0;i<sprites.size();i++) {
				list.add(sprites.elementAt(i));
			}
			
			try {
				int b = ((Integer) border.getValue()).intValue();
				pack.packImages(list, twidth, theight, b, out);
			} catch (IOException e) {
				// shouldn't happen 
				e.printStackTrace();
				JOptionPane.showMessageDialog(this, "Failed to write output");
			}
		}
	}
	
	/**
	 * Regenerate the sprite sheet that is being displayed
	 */
	private void regenerate() {
		try {
			ArrayList list = new ArrayList();
			for (int i=0;i<sprites.size();i++) {
				list.add(sprites.elementAt(i));
			}
			
			int b = ((Integer) border.getValue()).intValue();
			Sheet sheet = pack.packImages(list, twidth, theight, b, null);
			sheetPanel.setImage(sheet);
		} catch (IOException e) {
			// shouldn't happen 
			e.printStackTrace();
		}
	}
	
	/**
	 * A list cell renderer to show just the plain names
	 * 
	 * @author kevin
	 */
	private class FileListRenderer extends DefaultListCellRenderer {
		/**
		 * @see javax.swing.DefaultListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
		 */
		public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
			JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected,
					cellHasFocus);
			
			Sprite sprite = (Sprite) value;
			label.setText(sprite.getName());
			
			return label;
		}
	}
	
	/**
	 * Entry point to the simple UI
	 * 
	 * @param argv The arguments to the program
	 */
	public static void main(String[] argv) {
		new Packer();
	}
}