/*
 Tiamat - a role-playing game framework
 SpriteEditorFrame.java - main window for editing a Sprite
 
 Copyright  (C) 2004-2005 Hugues Johnson
 
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 the GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package com.huguesjohnson.tiamat.tools;

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.TitledBorder;
import com.huguesjohnson.PathResolver;
import com.huguesjohnson.fileFilter.XmlFileFilter;
import com.huguesjohnson.serialization.XmlFormatter;
import com.huguesjohnson.tiamat.graphics.Sprite;

/**
 * SpriteEditorFrame - main window for editing a Sprite
 * @author Hugues Johnson
 */
public class SpriteEditorFrame extends BaseEditorFrame{

	/** Determines if a de-serialized file is compatible with this class. See <a href="http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/class.html">Java Object Serialization Specifications</a>. */
	private static final long serialVersionUID=-206670896828031236L;

	//ui components
	private JToggleButton buttonDown;
	private JToggleButton buttonLeft;
	private JToggleButton buttonRight;
	private JToggleButton buttonUp;
	private JCheckBox checkBoxMovable;
	private JComboBox comboBoxDirection;
	private JLabel labelDirection;
	private JLabel labelFaster;
	private JLabel labelSlower;
	private JLabel labelSpriteFile;
	private JLabel labelStepIndex;
	private JLabel labelX;
	private JLabel labelY;
	private JPanel panelBaseImagePath;
	private JPanel panelDirectionStepIndex;
	private JPanel panelDirections;
	private JPanel panelLeftRightImages;
	private JPanel panelMainContent;
	private JPanel panelMovable;
	private JPanel panelPreview;
	private JPanel panelPreviewDirection;
	private JPanel panelPreviewImage;
	private JPanel panelSettings;
	private JPanel panelSettingsAndPreview;
	private JPanel panelSlowerFaster;
	private JPanel panelSpriteFile;
//	private JPanel panelToolbar;
	private JPanel panelUpDownImages;
	private JPanel panelXY;
	private JScrollBar scrollBarSpeed;
	private JTextField textFieldSpriteFile;
	private JTextField textFieldStepIndex;
	private JTextField textFieldX;
	private JTextField textFieldY;
	// panels to hold sprite images
	private SpriteImagePanel leftImagePanel;
	private SpriteImagePanel rightImagePanel;
	private SpriteImagePanel upImagePanel;
	private SpriteImagePanel downImagePanel;
	// last path that was opened
	private String lastOpenPath;
	// timer for preview animation
	Timer timer;
	// preview sprite
	private Sprite previewSprite;
	// the directory the current Sprite file is in
	private String absoluteDir;

	/** Creates new form SpriteEditorFrame */
	public SpriteEditorFrame(){
		super("Sprite");
		// add selections to the directions combo box
		this.comboBoxDirection.addItem(new String(Sprite.DIRECTION_UP+" (UP)"));
		this.comboBoxDirection.addItem(new String(Sprite.DIRECTION_DOWN+" (DOWN)"));
		this.comboBoxDirection.addItem(new String(Sprite.DIRECTION_LEFT+" (LEFT)"));
		this.comboBoxDirection.addItem(new String(Sprite.DIRECTION_RIGHT+" (RIGHT)"));
		// set the last open path
		this.lastOpenPath=System.getProperty("last-open-path");
		if(this.lastOpenPath==null){
			this.lastOpenPath=System.getProperty("user.dir");
		}
		// add the timer
		ActionListener listener=new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				onTimer();
			}
		};
		this.timer=new Timer(500,listener);
		this.timer.start();
		this.previewSprite=null;
	}

	protected void initComponents(){
		this.panelMainContent=new JPanel();
		this.panelSettingsAndPreview=new JPanel();
		this.panelSettings=new JPanel();
		this.panelSpriteFile=new JPanel();
		this.labelSpriteFile=new JLabel();
		this.textFieldSpriteFile=new JTextField();
		this.panelBaseImagePath=new JPanel();
		this.panelDirectionStepIndex=new JPanel();
		this.labelDirection=new JLabel();
		this.comboBoxDirection=new JComboBox();
		this.labelStepIndex=new JLabel();
		this.textFieldStepIndex=new JTextField();
		this.panelXY=new JPanel();
		this.labelX=new JLabel();
		this.textFieldX=new JTextField();
		this.labelY=new JLabel();
		this.textFieldY=new JTextField();
		this.panelMovable=new JPanel();
		this.checkBoxMovable=new JCheckBox();
		this.panelPreview=new JPanel();
		this.panelPreviewDirection=new JPanel();
		this.panelPreviewImage=new JPanel();
		this.panelDirections=new JPanel();
		this.buttonLeft=new JToggleButton();
		this.buttonUp=new JToggleButton();
		this.buttonDown=new JToggleButton();
		this.buttonRight=new JToggleButton();
		this.panelSlowerFaster=new JPanel();
		this.labelFaster=new JLabel();
		this.scrollBarSpeed=new JScrollBar();
		this.labelSlower=new JLabel();
		this.panelUpDownImages=new JPanel();
		this.panelLeftRightImages=new JPanel();
//		this.panelToolbar=new JPanel();
		this.buttonNew=new JButton();
		this.buttonOpen=new JButton();
		this.buttonSave=new JButton();
		this.buttonSaveAs=new JButton();

		this.panelMainContent.setLayout(new BoxLayout(this.panelMainContent,BoxLayout.Y_AXIS));
		this.panelSettingsAndPreview.setLayout(new BoxLayout(this.panelSettingsAndPreview,BoxLayout.X_AXIS));
		this.panelSettings.setLayout(new BoxLayout(this.panelSettings,BoxLayout.Y_AXIS));

		this.panelSettings.setBorder(new TitledBorder("Settings"));
		this.panelSettings.setMaximumSize(new Dimension(2147483647,205));
		this.panelSettings.setMinimumSize(new Dimension(180,205));
		this.panelSpriteFile.setLayout(new BoxLayout(this.panelSpriteFile,BoxLayout.X_AXIS));

		this.labelSpriteFile.setHorizontalAlignment(SwingConstants.RIGHT);
		this.labelSpriteFile.setText("Sprite File:  ");
		this.labelSpriteFile.setMaximumSize(new Dimension(110,15));
		this.labelSpriteFile.setMinimumSize(new Dimension(110,15));
		this.labelSpriteFile.setPreferredSize(new Dimension(110,15));
		this.panelSpriteFile.add(this.labelSpriteFile);

		this.textFieldSpriteFile.setEditable(false);
		this.textFieldSpriteFile.setMaximumSize(new Dimension(2147483647,21));
		this.panelSpriteFile.add(this.textFieldSpriteFile);

		this.panelSettings.add(this.panelSpriteFile);

		this.panelBaseImagePath.setLayout(new BoxLayout(this.panelBaseImagePath,BoxLayout.X_AXIS));

		this.panelSettings.add(this.panelBaseImagePath);

		this.panelDirectionStepIndex.setLayout(new BoxLayout(this.panelDirectionStepIndex,BoxLayout.X_AXIS));

		this.labelDirection.setHorizontalAlignment(SwingConstants.RIGHT);
		this.labelDirection.setText("Direction:  ");
		this.labelDirection.setMaximumSize(new Dimension(110,15));
		this.labelDirection.setMinimumSize(new Dimension(110,15));
		this.labelDirection.setPreferredSize(new Dimension(110,15));
		this.panelDirectionStepIndex.add(this.labelDirection);

		this.comboBoxDirection.setToolTipText("Which direction the Sprite is facing");
		this.comboBoxDirection.setMaximumSize(new Dimension(32767,21));
		this.comboBoxDirection.setMinimumSize(new Dimension(120,21));
		this.comboBoxDirection.setPreferredSize(new Dimension(120,21));
		this.panelDirectionStepIndex.add(this.comboBoxDirection);

		this.labelStepIndex.setHorizontalAlignment(SwingConstants.RIGHT);
		this.labelStepIndex.setText("Step Index:  ");
		this.labelStepIndex.setMaximumSize(new Dimension(78,15));
		this.labelStepIndex.setMinimumSize(new Dimension(78,15));
		this.labelStepIndex.setPreferredSize(new Dimension(78,15));
		this.panelDirectionStepIndex.add(this.labelStepIndex);

		this.textFieldStepIndex.setToolTipText("Animation step");
		this.textFieldStepIndex.setMaximumSize(new Dimension(2147483647,21));
		this.panelDirectionStepIndex.add(this.textFieldStepIndex);

		this.panelSettings.add(this.panelDirectionStepIndex);

		this.panelXY.setLayout(new BoxLayout(this.panelXY,javax.swing.BoxLayout.X_AXIS));

		this.labelX.setHorizontalAlignment(SwingConstants.RIGHT);
		this.labelX.setText("X:  ");
		this.labelX.setMaximumSize(new Dimension(110,15));
		this.labelX.setMinimumSize(new Dimension(110,15));
		this.labelX.setPreferredSize(new Dimension(110,15));
		this.panelXY.add(this.labelX);

		this.textFieldX.setToolTipText("X coordinate");
		this.textFieldX.setMaximumSize(new Dimension(120,21));
		this.textFieldX.setMinimumSize(new Dimension(120,21));
		this.textFieldX.setPreferredSize(new Dimension(120,21));
		this.panelXY.add(this.textFieldX);

		this.labelY.setHorizontalAlignment(SwingConstants.RIGHT);
		this.labelY.setText("Y:  ");
		this.labelY.setMaximumSize(new Dimension(78,15));
		this.labelY.setMinimumSize(new Dimension(78,15));
		this.labelY.setPreferredSize(new Dimension(78,15));
		this.panelXY.add(this.labelY);

		this.textFieldY.setToolTipText("Y coordinate");
		this.textFieldY.setMaximumSize(new Dimension(2147483647,21));
		this.panelXY.add(this.textFieldY);

		this.panelSettings.add(this.panelXY);

		this.panelMovable.setLayout(new FlowLayout(FlowLayout.LEFT));

		this.panelMovable.setMaximumSize(new Dimension(32767,23));
		this.panelMovable.setMinimumSize(new Dimension(93,23));
		this.checkBoxMovable.setText("Movable");
		this.checkBoxMovable.setToolTipText("Whether this Sprite can be moved");
		this.checkBoxMovable.setMaximumSize(new Dimension(83,23));
		this.checkBoxMovable.setMinimumSize(new Dimension(83,23));
		this.panelMovable.add(this.checkBoxMovable);

		this.panelSettings.add(this.panelMovable);

		this.panelSettingsAndPreview.add(this.panelSettings);

		this.panelPreview.setLayout(new BoxLayout(this.panelPreview,javax.swing.BoxLayout.Y_AXIS));

		this.panelPreview.setBorder(new TitledBorder("Preview"));
		this.panelPreviewImage.setFont(new Font("MS Sans Serif",0,8));
		this.panelPreviewImage.setMinimumSize(new Dimension(48,48));
		this.panelPreviewImage.setPreferredSize(new Dimension(48,48));
		this.panelPreviewDirection.add(this.panelPreviewImage);

		this.panelDirections.setLayout(new BorderLayout());

		this.buttonLeft.setText("Left");
		this.buttonLeft.setToolTipText("Preview left animation");
		this.buttonLeft.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				buttonLeftActionPerformed(evt);
			}
		});

		this.panelDirections.add(this.buttonLeft,BorderLayout.CENTER);

		this.buttonUp.setText("Up");
		this.buttonUp.setToolTipText("Preview up animation");
		this.buttonUp.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				buttonUpActionPerformed(evt);
			}
		});

		this.panelDirections.add(this.buttonUp,BorderLayout.NORTH);

		this.buttonDown.setText("Down");
		this.buttonDown.setToolTipText("Preview down animation");
		this.buttonDown.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				buttonDownActionPerformed(evt);
			}
		});

		this.panelDirections.add(this.buttonDown,BorderLayout.SOUTH);

		this.buttonRight.setText("Right");
		this.buttonRight.setToolTipText("Preview right animation");
		this.buttonRight.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent evt){
				buttonRightActionPerformed(evt);
			}
		});

		this.panelDirections.add(this.buttonRight,BorderLayout.EAST);

		this.panelPreviewDirection.add(this.panelDirections);

		this.panelPreview.add(this.panelPreviewDirection);

		this.panelSlowerFaster.setLayout(new BoxLayout(this.panelSlowerFaster,javax.swing.BoxLayout.X_AXIS));

		this.panelSlowerFaster.setMaximumSize(new Dimension(32767,32));
		this.panelSlowerFaster.setPreferredSize(new Dimension(100,32));
		this.labelFaster.setText("Faster");
		this.panelSlowerFaster.add(this.labelFaster);

		this.scrollBarSpeed.setBlockIncrement(500);
		this.scrollBarSpeed.setMaximum(1500);
		this.scrollBarSpeed.setMinimum(300);
		this.scrollBarSpeed.setOrientation(Adjustable.HORIZONTAL);
		this.scrollBarSpeed.setToolTipText("Control the speed of the Sprite preview");
		this.scrollBarSpeed.setUnitIncrement(150);
		this.scrollBarSpeed.setMinimumSize(new Dimension(100,16));
		this.scrollBarSpeed.setPreferredSize(new Dimension(100,16));
		this.scrollBarSpeed.addAdjustmentListener(new AdjustmentListener(){
			public void adjustmentValueChanged(AdjustmentEvent evt){
				scrollBarSpeedAdjustmentValueChanged(evt);
			}
		});

		this.panelSlowerFaster.add(this.scrollBarSpeed);

		this.labelSlower.setText("Slower");
		this.panelSlowerFaster.add(this.labelSlower);

		this.panelPreview.add(this.panelSlowerFaster);

		this.panelSettingsAndPreview.add(this.panelPreview);

		this.panelMainContent.add(this.panelSettingsAndPreview);

		this.panelUpDownImages.setLayout(new BoxLayout(this.panelUpDownImages,javax.swing.BoxLayout.X_AXIS));

		this.panelMainContent.add(this.panelUpDownImages);

		this.panelLeftRightImages.setLayout(new BoxLayout(this.panelLeftRightImages,javax.swing.BoxLayout.X_AXIS));

		this.panelMainContent.add(this.panelLeftRightImages);

		this.getContentPane().add(this.panelMainContent,BorderLayout.CENTER);

//		this.panelToolbar.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));

//		this.panelToolbar.setBorder(new javax.swing.border.CompoundBorder());
//		this.panelToolbar.setMinimumSize(new java.awt.Dimension(99,16));
//		this.panelToolbar.add(this.toolbar);

//		this.getContentPane().add(this.panelToolbar,java.awt.BorderLayout.NORTH);

		this.leftImagePanel=new SpriteImagePanel("Left");
		this.panelLeftRightImages.add(this.leftImagePanel);
		this.rightImagePanel=new SpriteImagePanel("Right");
		this.panelLeftRightImages.add(this.rightImagePanel);
		this.upImagePanel=new SpriteImagePanel("Up");
		this.panelUpDownImages.add(this.upImagePanel);
		this.downImagePanel=new SpriteImagePanel("Down");
		this.panelUpDownImages.add(this.downImagePanel);
		// re-pack so these panels are visible
		this.pack();
		// disable save by default
		this.disableSave();
	}

	protected void onViewXml(){
		String filePath="file:///"+this.textFieldSpriteFile.getText();
		this.viewXml(filePath);
	}

	public void scrollBarSpeedAdjustmentValueChanged(AdjustmentEvent evt){
		if(!this.scrollBarSpeed.getValueIsAdjusting()){
			this.timer.setDelay(this.scrollBarSpeed.getValue());
		}
	}

	public void buttonDownActionPerformed(ActionEvent evt){
		this.buttonDown.setSelected(true);
		this.buttonUp.setSelected(false);
		this.buttonRight.setSelected(false);
		this.buttonLeft.setSelected(false);
	}

	public void buttonRightActionPerformed(ActionEvent evt){
		this.buttonRight.setSelected(true);
		this.buttonUp.setSelected(false);
		this.buttonDown.setSelected(false);
		this.buttonLeft.setSelected(false);
	}

	public void buttonLeftActionPerformed(ActionEvent evt){
		this.buttonLeft.setSelected(true);
		this.buttonUp.setSelected(false);
		this.buttonRight.setSelected(false);
		this.buttonDown.setSelected(false);
	}

	public void buttonUpActionPerformed(ActionEvent evt){
		this.buttonUp.setSelected(true);
		this.buttonDown.setSelected(false);
		this.buttonRight.setSelected(false);
		this.buttonLeft.setSelected(false);
	}

	protected void onSaveAs(){
		JFileChooser fileChooser=new JFileChooser();
		// do not allow multiple selections
		fileChooser.setMultiSelectionEnabled(false);
		// only accept specific filters
		fileChooser.setAcceptAllFileFilterUsed(false);
		// set the current directory
		String currentDirectory=this.textFieldSpriteFile.getText();
		if(currentDirectory.length()>0){
			fileChooser.setCurrentDirectory(new File(currentDirectory));
		} else{
			fileChooser.setCurrentDirectory(new File(this.lastOpenPath));
		}
		// add an xml file filter
		fileChooser.setFileFilter(new XmlFileFilter());
		// show the dialog
		int returnValue=fileChooser.showSaveDialog(this);
		// was 'OK' selected?
		if(returnValue==JFileChooser.APPROVE_OPTION){
			this.absoluteDir=fileChooser.getCurrentDirectory().getPath();
			if(!this.absoluteDir.endsWith(File.separator)){
				this.absoluteDir=this.absoluteDir+File.separator;
			}
			String filePath=fileChooser.getSelectedFile().getPath();
			if(!filePath.toLowerCase().endsWith(".sprite.xml")){
				filePath=filePath+".sprite.xml";				
			}
			this.textFieldSpriteFile.setText(filePath);
			this.lastOpenPath=fileChooser.getSelectedFile().getParent();
			this.onSave();
			this.enableSave();
			this.addRecentFile(filePath);
		}
	}

	protected void onNew(){
		// null the preview sprite
		this.previewSprite=null;
		// clear fields
		this.textFieldSpriteFile.setText("");
		this.textFieldStepIndex.setText("");
		this.textFieldX.setText("");
		this.textFieldY.setText("");
		// remove images from sprite panels
		this.upImagePanel.removeAllImages();
		this.downImagePanel.removeAllImages();
		this.leftImagePanel.removeAllImages();
		this.rightImagePanel.removeAllImages();
		// disable save
		this.disableSave();
		// eventually code here to stop the preview
	}

	protected void onOpen(){
		JFileChooser fileChooser=new JFileChooser();
		// do not allow multiple selections
		fileChooser.setMultiSelectionEnabled(false);
		// only accept specific filters
		fileChooser.setAcceptAllFileFilterUsed(false);
		// set the current directory
		String currentDirectory=this.textFieldSpriteFile.getText();
		if(currentDirectory.length()>0){
			fileChooser.setCurrentDirectory(new File(currentDirectory));
		} else{
			fileChooser.setCurrentDirectory(new File(this.lastOpenPath));
		}
		// add an xml file filter
		fileChooser.setFileFilter(new XmlFileFilter());
		// show the dialog
		int returnValue=fileChooser.showOpenDialog(this);
		// was 'OK' selected
		if(returnValue==JFileChooser.APPROVE_OPTION){
			// clear the form
			this.onNew();
			// set the absolute dir
			this.absoluteDir=fileChooser.getSelectedFile().getParent().toString();
			// load a Sprite from the selected file
			String filePath=fileChooser.getSelectedFile().getPath();
			this.loadSprite(filePath);
			this.addRecentFile(filePath);
		}
	}

	protected void onRecentFile(String filePath){
		this.onNew();
		this.loadSprite(filePath);
	}
	
	private void loadSprite(String filePath){
		try{
			XmlFormatter formatter=new XmlFormatter();
			Sprite sprite=(Sprite)formatter.loadFrom(filePath);
			sprite.setCanvas(this.panelPreviewImage); // setting canvas also buffers images
			// set fields
			this.textFieldSpriteFile.setText(filePath);
			this.textFieldStepIndex.setText(String.valueOf(sprite.getStepIndex()));
			this.textFieldX.setText(String.valueOf(sprite.getX()));
			this.textFieldY.setText(String.valueOf(sprite.getY()));
			this.checkBoxMovable.setSelected(sprite.getMovable());
			// load the images
			String[][] imagePaths=sprite.getImagePathsAbsolute();
			int length=imagePaths[Sprite.DIRECTION_UP].length;
			for(int index=0;index<length;index++){
				this.upImagePanel.addImage(imagePaths[Sprite.DIRECTION_UP][index]);
			}
			length=imagePaths[Sprite.DIRECTION_DOWN].length;
			for(int index=0;index<length;index++){
				this.downImagePanel.addImage(imagePaths[Sprite.DIRECTION_DOWN][index]);
			}
			length=imagePaths[Sprite.DIRECTION_LEFT].length;
			for(int index=0;index<length;index++){
				this.leftImagePanel.addImage(imagePaths[Sprite.DIRECTION_LEFT][index]);
			}
			length=imagePaths[Sprite.DIRECTION_RIGHT].length;
			for(int index=0;index<length;index++){
				this.rightImagePanel.addImage(imagePaths[Sprite.DIRECTION_RIGHT][index]);
			}
			// setup the preview
			this.previewSprite=sprite;
			this.previewSprite.setCanvas(this.panelPreviewImage);
			// enable save
			this.enableSave();
		} catch(Exception x){
			x.printStackTrace();
			JOptionPane.showMessageDialog(this,"Error loading Sprite: "+x.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
		}
	}
	
	protected void onSave(){
		// set to false if something isn't filled out
		boolean canSave=true;
		// get direction
		int direction=0;
		if(canSave){
			try{
				String selectedText=(String)this.comboBoxDirection.getSelectedItem();
				direction=java.lang.Integer.parseInt(selectedText.substring(0,1));
			} catch(NumberFormatException nfx){
				canSave=false;
				JOptionPane.showMessageDialog(this,nfx.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
			}
		}
		// get x
		int x=0;
		if(canSave){
			try{
				String text=this.textFieldX.getText();
				if(text.length()<1){
					JOptionPane.showMessageDialog(this,"Enter a value for 'X'","Error",JOptionPane.ERROR_MESSAGE);
					canSave=false;
				} else{
					x=Integer.parseInt(text);
				}
			} catch(NumberFormatException nfx){
				canSave=false;
				JOptionPane.showMessageDialog(this,"The value for 'X' is not a valid number","Error",JOptionPane.ERROR_MESSAGE);
			}
		}
		// get y
		int y=0;
		if(canSave){
			try{
				String text=this.textFieldY.getText();
				if(text.length()<1){
					JOptionPane.showMessageDialog(this,"Enter a value for 'Y'","Error",JOptionPane.ERROR_MESSAGE);
					canSave=false;
				} else{
					y=java.lang.Integer.parseInt(text);
				}
			} catch(java.lang.NumberFormatException nfx){
				canSave=false;
				JOptionPane.showMessageDialog(this,"The value for 'Y' is not a valid number","Error",JOptionPane.ERROR_MESSAGE);
			}
		}
		// get movable - no validation needed
		boolean movable=this.checkBoxMovable.isSelected();
		// get paths to the actual images
		String[][] imagePaths=new String[4][];
		// up
		if(canSave){
			imagePaths[Sprite.DIRECTION_UP]=this.upImagePanel.getImagePaths();
			if(imagePaths[Sprite.DIRECTION_UP].length<1){
				JOptionPane.showMessageDialog(this,"Please select some 'Up' animation images","Error",JOptionPane.ERROR_MESSAGE);
				canSave=false;
			}
		}
		// down
		if(canSave){
			imagePaths[Sprite.DIRECTION_DOWN]=this.downImagePanel.getImagePaths();
			if(imagePaths[Sprite.DIRECTION_DOWN].length<1){
				JOptionPane.showMessageDialog(this,"Please select some 'Down' animation images","Error",JOptionPane.ERROR_MESSAGE);
				canSave=false;
			}
		}
		// left
		if(canSave){
			imagePaths[Sprite.DIRECTION_LEFT]=this.leftImagePanel.getImagePaths();
			if(imagePaths[Sprite.DIRECTION_LEFT].length<1){
				JOptionPane.showMessageDialog(this,"Please select some 'Left' animation images","Error",JOptionPane.ERROR_MESSAGE);
				canSave=false;
			}
		}
		// right
		if(canSave){
			imagePaths[Sprite.DIRECTION_RIGHT]=this.rightImagePanel.getImagePaths();
			if(imagePaths[Sprite.DIRECTION_RIGHT].length<1){
				JOptionPane.showMessageDialog(this,"Please select some 'Right' animation images","Error",JOptionPane.ERROR_MESSAGE);
				canSave=false;
			}
		}
		// clean up image paths
		if(canSave){
			for(int index=0;index<imagePaths.length;index++){
				for(int index1=0;index1<imagePaths[index].length;index1++){
					imagePaths[index][index1]=PathResolver.getRelativePath(this.absoluteDir,imagePaths[index][index1]);
					String startWith=PathResolver.SELF_PATH+File.separator;
					// trim off starting '.\\' characters
					if(imagePaths[index][index1].startsWith(startWith)){
						imagePaths[index][index1]=imagePaths[index][index1].substring(startWith.length());
					}
				}
			}
		}
		// now create and save sprite
		if(canSave){
			Sprite sprite=new Sprite(direction,x,y,this.absoluteDir,imagePaths,this.panelPreviewImage);
			sprite.setMovable(movable);
			String filePath=this.textFieldSpriteFile.getText();
			if(filePath.length()>0){
				try{
					XmlFormatter formatter=new XmlFormatter();
					formatter.saveTo(sprite,filePath);
					// setup the preview
					this.previewSprite=sprite;
				} catch(Exception ex){
					ex.printStackTrace();
					// show an error
					JOptionPane.showMessageDialog(this,"Error occured saving sprite: "+ex.getMessage(),"Error",JOptionPane.ERROR_MESSAGE);
				}
			} else{ // no file specified, goto saveAs()
				this.onSaveAs();
			}
		}
	}

	/* executes when timer fires */
	public void onTimer(){
		if(this.previewSprite!=null){
			// figure out which direction to move the sprite
			int direction=-1;
			if(this.buttonUp.isSelected()){
				direction=Sprite.DIRECTION_UP;
			} else if(this.buttonRight.isSelected()){
				direction=Sprite.DIRECTION_RIGHT;
			} else if(this.buttonLeft.isSelected()){
				direction=Sprite.DIRECTION_LEFT;
			} else if(this.buttonDown.isSelected()){
				direction=Sprite.DIRECTION_DOWN;
			} else{ // use down animation by default
				this.buttonDown.setSelected(true);
				direction=Sprite.DIRECTION_DOWN;
			}
			// move the preview sprite to the next animation frame
			this.previewSprite.move(direction);
			// get sizes
			BufferedImage image=this.previewSprite.getCurrentImage();
			if(image!=null){
				int imageWidth=this.previewSprite.getCurrentImage().getWidth();
				int imageHeight=this.previewSprite.getCurrentImage().getHeight();
				int canvasWidth=this.panelPreviewImage.getWidth();
				int canvasHeight=this.panelPreviewImage.getHeight();
				// erase the old image
				Graphics2D g2=(Graphics2D)this.panelPreviewImage.getGraphics();
				g2.setColor(this.panelPreviewImage.getBackground());
				g2.fillRect(0,0,canvasWidth,canvasHeight);
				// draw the sprite
				int x=0;
				int y=0;
				this.previewSprite.setX(x);
				this.previewSprite.setY(y);
				this.previewSprite.draw();
			}
		}
	}

	/**
	 * main - just used for testing
	 * @param args the command line arguments
	 */
	public static void main(String args[]){
		try{
			(new SpriteEditorFrame()).setVisible(true);
		} catch(Exception x){
			x.printStackTrace();			
		}
	}
}