/**
 * Copyright (c) 2011, Martin Foster <martin@ethereal-realms.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice, 
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright 
 *       notice, this list of conditions and the following disclaimer in the 
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the Ethereal-Realms.org nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lyra.Interface;

import javax.swing.JPanel;

import java.awt.Color;
import java.awt.Frame;

import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.ImageIcon;
import javax.swing.BorderFactory;

import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.swing.JRadioButton;
import javax.swing.JButton;
import javax.swing.JTextField;

import org.lyra.Lyra;
import org.lyra.Listener.WindowSizeListener;
import org.lyra.Widgets.RapidDialog;
import org.lyra.Widgets.RegexConstrainedDocument;
import org.lyra.Widgets.ResizeIcon;

/**
 * The ability to save a playlist is not limited in scope.  Not only do we need
 * to confirm a name for the list, we also need to select if it will be a global
 * or local list.   Local lists are essentially restricted to use on this 
 * specific installation only.
 * <p>
 * @author Martin Foster
 * @version 1.0.1, %Revision, 94%, %LastChangedDate, 09/03/11 10:00PM%
 */
public class PlaylistSaveDialog extends JDialog implements Runnable {

	// Serial Requirements
	private static final long serialVersionUID = 1L;
	
	/*
	 * DISPLAY ELEMENTS
	 */
	
	// Main Container
	private JPanel jContentPane = null;
	
	// Message Section
	private JPanel panelMessage = null;
	private JLabel iconMessage = null;
	private JTextArea areaMessage = null;
	
	// Options Holder
	private JPanel panelOptions = null;
	private JPanel panelSelect = null;
	
	// Name of new playlist
	private JPanel panelName = null;
	private JTextField fieldName = null;
	
	// Local Button Choice
	private JPanel panelLocal = null;
	private JRadioButton radioLocal = null;
	private JLabel labelLocal = null;
	
	// Remote Button Choice
	private JPanel panelRemote = null;
	private JRadioButton radioRemote = null;
	private JLabel labelRemote = null;

	// Last.fm Service Integration
	private JPanel panelService = null;
	private JLabel labelService = null;
	private JCheckBox checkService = null;
	
	// Action Choice
	private JPanel panelCommand = null;
	private JButton buttonSave = null;
	private JButton buttonCancel = null;
	
	// Status Section
	private JPanel panelStatus = null;
	private JLabel iconResize = null;
	
	/*
	 * PACKAGE INSTANCES AND VARIABLES
	 */
	private Set<String> myLocal;
	private Set<String> myRemote;
	
	private Map<String,String> Lang;
	
	private boolean Integrate;

	
	/*
	 * CLASS INSTANCES AND VARIABLES
	 */
	
	// Action and code logic
	private Interact Interface;
	
	// Passage variables
	private boolean Local;
	private boolean Service;
	private String  Name;
	
	// Processing
	private boolean Process = true;
	
	// Additional process
	private Thread myThread;
	
	// Self
	private JDialog myFrame;


	/**
	 * Base constructor for the visual class editors.  Does not require any
	 * information from external sources such as databases or the user.
	 * <p>
	 * @param owner Window owner to block.
	 */
	@SuppressWarnings("serial")
	private PlaylistSaveDialog(Frame owner) {
		
		// Let super class do its business
		super(owner);
		
		/*
		 * Under normal circumstances this section will be passed on from the
		 * database.   However Visual Editors do not have this ability to we
		 * create a HashMap that can be used either way.
		 * 
		 */
		Lang = new HashMap<String,String>(){
			{
				// Titles and Sections
	        	put("Playlist.Window.Title","Save the current playlist to...");
	        	
	        	// Main Message
	        	put("Playlist.Area.Message","This message is used to notify users that they are saving their playlist.");

	        	put("Playlist.Option.Remote","Available through host");
	        	put("Playlist.Option.Local","Make available locally only");
	        	
	        	// Generic features found elsewhere
	        	put("Button.Cancel","Cancel");
	        	put("Button.Save","Accept");
			}
		};
		
		// Capture self
		myFrame = this;
		
		// Initialise according to baseline
		initDialog(owner);
	}
	// End of Constructor Method
	

	/**
	 * Primary constructor for this class.   Is passed the necessary instances
	 * from the calling class to establish language and data elements.
	 * <p>
	 * @param lang Instance of the Language map.
	 * @param local Set containing all local playlists.
	 * @param remote Set containing all remote playlists.
	 * @param integrate Boolean indicating need to integrate with Last.fm.
	 * @param owner Window owner to block.
	 */
	private PlaylistSaveDialog(Map<String,String> lang, Set<String> local, Set<String> remote, boolean integrate, Frame owner) {
		
		// Let super class do its business
		super(owner);
		
		// Pass Up
		this.Lang      = lang;
		this.Integrate = integrate;
		this.myRemote  = remote;
		this.myLocal   = local;
		
		// Capture self
		myFrame = this;
		
		// Create Instance of Listener
		this.Interface = new Interact();
		
		// Initialise according to baseline
		initDialog(owner);
	}
	// End of Constructor Method
	
	
	/**
	 * Element which will deal with the creation of the class instance and
	 * extracting the information prior to returning it to the calling program.
	 * <p>
	 * @param lang Instance of the Language map.
	 * @param local Set containing all local playlists.
	 * @param remote Set containing all remote playlists.
	 * @param integrate Boolean containing integration flag for Last.fm Service.
	 * @param owner Window owner to block.
	 * @return Instance of PlaylistSave.
	 */
	static public PlaylistSave getVerdict(Map<String,String> lang, Set<String> local, Set<String> remote, boolean integrate, Frame owner) {

		// Create a new instance and make visible
    	PlaylistSaveDialog psd = new PlaylistSaveDialog(lang, local, remote, integrate, owner);

    	// Create a new thread that will wait for a completed Host entry
		psd.myThread = new Thread(psd);
		psd.myThread.start();
		
		// Set the display to be visible
    	psd.setVisible(true);
		
    	// Wait out for our needs
		try {
			// Join in and wait for the results
			psd.myThread.join();
		} 
		
		// Something went wrong so just pass on the defaults
		catch (InterruptedException e) {}
		
		// Return element
		return psd.getPlaylistSave();
	}
	// End of getVerdict Method
	
	
	/**
	 * Element which will deal with the creation of the class instance and
	 * extracting the information prior to returning it to the calling program.
	 * <p>
	 * @param lang Instance of the Language map.
	 * @param local Set containing all local playlists.
	 * @param remote Set containing all remote playlists.
	 * @param owner Window owner to block.
	 * @return Instance of PlaylistSave.
	 */
	static public PlaylistSave getVerdict(Map<String,String> lang, Set<String> local, Set<String> remote, Frame owner) {

		// Pass on with default integration
		return getVerdict(lang, local, remote, false, owner);
	}
	// End of getVerdict Method
	
	
	/**
	 * Creates a new instance of PlaylistSave from information already
	 * collected.   This method should only be called after the values have
	 * been propagated.
	 * <p>
	 * @return PlaylistSave instance.
	 */
	private PlaylistSave getPlaylistSave() {
		
		// Direct Pass-Through
		return new PlaylistSave(Local, Service, Name);
	}
	// End of getPlaylistSave Method
	
	
	/**
	 * This method will wait out until the HashMap is defined.  If not it will
	 * sleep until interrupted.
	 */
	public void run() {
		
		/*
		 * We essentially wait until such a time that the user has opted to 
		 * do something.  Otherwise we have an arbitrarily large sleep period
		 * as to prevent unnecessary cycles.
		 */
		while (Process) {
			
			try {
				// Nothing for us to do sleep
				Thread.sleep(300000);
			} 
			
			/*
			 * Another component has interrupted us from our sleep.  This
			 * should mean the end of the cycle.
			 */
			catch (InterruptedException e) {}
		}
	}
	// End of Run Method
	
	
	/*
	 * DISPLAY ELEMENTS
	 */
	
	/**
	 * This method initialises the look and feel of this dialog to a baseline.
	 * This is the natural entry point after any constructor has finishes
	 * setting its own baseline.
	 * <p>
	 * @param owner Window owner to block.
	 */
	private void initDialog(Frame owner) {
		
		// Establish Titles and modality
		this.setTitle(Lang.get("Playlist.Window.Title"));
		this.setModal(true);
		
		// Adjust Icon
		this.setIconImage(Constants.getIcon());
		
		// Establish contents
		this.setContentPane(getJContentPane());
		
		// Pack display
		this.pack();
		
		// Set position relative to parent window
		this.setLocationRelativeTo(owner);
		
		// Event Listener
		this.addWindowListener(Interface);
	}
	// End of initDialog Method
	

	/**
	 * This method initialises jContentPane.  This panel holds all of the 
	 * other elements in the window.
	 * <p>
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		
		// Check for Existence
		if (jContentPane == null) {
			
			// Create Instance
			jContentPane = new JPanel();
			
			// Establish Layout
			jContentPane.setLayout(new BoxLayout(getJContentPane(), BoxLayout.Y_AXIS));

			/*
			 * Here we add in the core sections of the display.  The message
			 * panel contains an information notice, the options panel contains
			 * the widgets necessary to get an idea on the operation and lastly
			 * the command panel is used to establish buttons for control.
			 */
			
			// Spacer and Message
			jContentPane.add(Box.createVerticalStrut(Constants.getSectionGap()));
			jContentPane.add(getPanelMessage(), null);
			
			// Spacer and Options
			jContentPane.add(Box.createVerticalStrut(Constants.getSectionGap()));
			jContentPane.add(getPanelOptions(), null);
			
			// Spacer and Buttons
			jContentPane.add(Box.createVerticalStrut(Constants.getSectionGap()));
			jContentPane.add(getPanelCommand(), null);
						
			/*
			 * The status section is there to provide a familiar look and feel
			 * for all larger interface elements.
			 */
			jContentPane.add(getPanelStatus(), null);
		}
		
		// Return Instance
		return jContentPane;
	}
	// End of getJContentPane Method
	

	/**
	 * This method initialises panelMessage.  Contains the information and notices
	 * necessary to prompt the user to proceed with the process.
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelMessage() {
		
		// Check for Existence
		if (panelMessage == null) {
			
			// Create Instance
			panelMessage = new JPanel();
			
			// Establish Layout
			panelMessage.setLayout(new BoxLayout(getPanelMessage(), BoxLayout.X_AXIS));

			/*
			 * Append individual elements with the addition of spacers that
			 * will enforce positioning.
			 */
			
			// Spacer and Icon
			panelMessage.add(Box.createHorizontalStrut(Constants.getSectionGap()));
			panelMessage.add(getIconMessage(), null);
			
			// Spacer and Message
			panelMessage.add(Box.createHorizontalStrut(Constants.getSectionGap()));
			panelMessage.add(getAreaMessage(), null);

			// Padding at the end
			panelMessage.add(Box.createHorizontalStrut(Constants.getSectionGap()));
		}
		
		// Return instance
		return panelMessage;
	}
	// End of getPanelMessage Method
	
	
	/**
	 * This method initialises areaMessage.	Designed to act like a multi line
	 * JLabel without any of the nasty side effects.
	 * <p>
	 * @return javax.swing.JTextArea	
	 */
	private JTextArea getAreaMessage() {
		
		// Check for existence
		if (areaMessage == null) {
			
			// Create Instance
			areaMessage = new JTextArea();
			
			// Establish Message
			areaMessage.setText(Lang.get("Playlist.Area.Message"));
			
			// Make this to look like a Label
			areaMessage.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
			areaMessage.setMargin(new Insets(2, 2, 2, 2));
			areaMessage.setBackground(new Color(0,0,0,0));
			areaMessage.setOpaque(false);
			
			// Cannot Make Modifications
			areaMessage.setEditable(false);
			
			// Adjust the font to match a label
			areaMessage.setFont(new JLabel().getFont());
			
			// Adjust size to allow for proper sizing
			areaMessage.setRows(areaMessage.getLineCount());
		}
		
		// Return instance
		return areaMessage;
	}
	// End of getAreaMessage Method

	
	/**
	 * This method initialises panelOptions.  Options contain all of the
	 * elements necessary to proceed with the user request.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelOptions() {
		
		// Check for Existence
		if (panelOptions == null) {

			// Create Instance
			panelOptions = new JPanel();
			
			// Establish Layout
			panelOptions.setLayout(new BoxLayout(getPanelOptions(), BoxLayout.Y_AXIS));

			// Add in Name and both Radio Buttons
			panelOptions.add(getPanelName(), null);
			
			
			/*
			 * We have no idea if we will be able to integrate with the Last.fm
			 * service.   So we wrap our design accordingly. 
			 */
			if (Integrate) {
				
				// Create panel which combines all elements
				panelOptions.add(getPanelSelect());
			}
			
			else {
				
				// Implement the radio buttons directly
				panelOptions.add(getPanelLocal(), null);
				panelOptions.add(getPanelRemote(), null);			
			}
			
			/*
			 * Radio Buttons have to be grouped in order to ensure that they
			 * work the way we expect them to.  We could do it manually, but
			 * there is no real advantage to it.
			 */
			ButtonGroup group = new ButtonGroup();
			
			// Group both radio buttons into one
			group.add(getRadioLocal());
			group.add(getRadioRemote());
		}
		
		// Return Instance
		return panelOptions;
	}
	// End of getPanelOptions Method
	
	
	/**
	 * This method initialises panelSelect.  Select contains all of the
	 * all of the check boxes and radio buttons.
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelSelect() {
		
		// Check for Existence
		if (panelSelect == null) {

			/*
			 * We have to create a panel to hold the Radio Buttons, grouping them
			 * together as necessary.
			 */
			
			// Create Instance
			JPanel panelRadio = new JPanel();
			
			// Establish Layout
			panelRadio.setLayout(new BoxLayout(panelRadio, BoxLayout.Y_AXIS));
			
			// Insert Elements
			panelRadio.add(getPanelLocal(), null);
			panelRadio.add(getPanelRemote(), null);
			
			
			/*
			 * The Check Box will appear to the right of the other objects.  So
			 * we create a panel for it only.
			 */
			
			// Create Instance
			JPanel panelCheck = new JPanel();
			
			// Establish Layout
			panelCheck.setLayout(new BoxLayout(panelCheck, BoxLayout.Y_AXIS));
			
			// Elements and Spacer
			panelCheck.add(getPanelService(), null);
			panelCheck.add(Box.createVerticalGlue(), null);

			
			/*
			 * Now that we have created the appropriate objects, we can now
			 * incorporate both panels into the mix.
			 */
			
			// Create Instance
			panelSelect = new JPanel();
			
			// Establish Layout
			panelSelect.setLayout(new BoxLayout(getPanelSelect(), BoxLayout.X_AXIS));

			// Add in Name and both Radio Buttons
			panelSelect.add(panelRadio);
			panelSelect.add(panelCheck);
		}
		
		// Return Instance
		return panelSelect;
	}
	// End of getPanelSelect Method
	

	/**
	 * This method initialises radioLocal.  This is the direct option to select
	 * local existence of the playlist only.	
	 * <p>
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getRadioLocal() {
		
		// Check for Existence
		if (radioLocal == null) {
			
			// Create Instance
			radioLocal = new JRadioButton();
			
			// By default this is selected
			radioLocal.setSelected(true);
			
			// Identifier and Listener
			radioLocal.setName("PLAYLIST.LOCAL");
		}
		
		// Return Instance
		return radioLocal;
	}
	// End of getRadioLocal Method

	
	/**
	 * This method initialises radioRemote.  Direct option to select and save
	 * a playlist to the remote host.	
	 * <p>
	 * @return javax.swing.JRadioButton	
	 */
	private JRadioButton getRadioRemote() {
		
		// Check for Existence
		if (radioRemote == null) {
			
			// Create Instance
			radioRemote = new JRadioButton();
			
			// By default this is selected
			radioRemote.setSelected(false);
			
			// Identifier and Listener
			radioRemote.setName("PLAYLIST.REMOTE");
		}
		
		// Return Instance
		return radioRemote;
	}
	// End of getRadioRemote Method

	
	/**
	 * This method initialises checkService.  This option permits the saving of a
	 * playlist to the Last.fm service.
	 * <p>
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getCheckService() {
		
		// Check for Existence
		if (checkService == null) {
			
			// Create Instance
			checkService = new JCheckBox();
			
			// By default this is selected
			checkService.setSelected(true);
			
			// Identifier and Listener
			checkService.setName("PLAYLIST.LASTDOTFM");
		}
		
		// Return Instance
		return checkService;
	}
	// End of getCheckService Method
	
	
	/**
	 * Returns a formatted label informing the user that this playlist will be
	 * copied to the Last.fm service.
	 * <p>
	 * @return javax.swing.Label
	 */
	private JLabel getLabelService() {
		
		// Check for Existence
		if (labelService == null) {
			
			// Return Instance
			labelService = new JLabel();
			
			// Set language and attach
			labelService.setText(Lang.get("Playlist.Option.Service"));
			labelService.setLabelFor(getCheckService());
		}
		
		// Return Instance
		return labelService;
	}
	// End of getLabelService Method
	
	
	/**
	 * This method initialises panelService.  Simply groups the Service check button
	 * and associated label.   Used mostly to add in the necessary spacers.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelService() {
		
		// Check for Existence
		if (panelService == null) {
			
			// Create Instance
			panelService = new JPanel();
			
			// Establish Layout
			panelService.setLayout(new BoxLayout(getPanelService(), BoxLayout.X_AXIS));
						
			// Insert Elements
			panelService.add(getCheckService(), null);
			panelService.add(getLabelService(), null);
			
			// Spacer to force position
			panelService.add(Box.createHorizontalGlue());
		}
		
		// Return Instance
		return panelService;
	}
	// End of getPanelService Method
	
	
	/**
	 * This method initialises panelCommand.  Contains the necessary JButton
	 * elements to decide on a course of action.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelCommand() {
		
		// Check for Existence
		if (panelCommand == null) {
			
			// Create Instance
			panelCommand = new JPanel();
			
			// Establish Layout
			panelCommand.setLayout(new BoxLayout(getPanelCommand(), BoxLayout.X_AXIS));
			
			
			/*
			 * This will essentially centre the buttons to the dialog window
			 * itself.   Which is established as the look and feel for existing
			 * dialogs.
			 */
			
			// Spacer and Save Button
			panelCommand.add(Box.createHorizontalGlue());
			panelCommand.add(getButtonSave(), null);
			
			// Main Spacer
			panelCommand.add(Box.createHorizontalStrut(Constants.getNormalGap()));
			
			/// Cancel Button and Spacer
			panelCommand.add(getButtonCancel(), null);
			panelCommand.add(Box.createHorizontalGlue());
		}
		
		// Return Instance
		return panelCommand;
	}
	// End of getPanelCommand Method

	
	/**
	 * This method initialises buttonSave.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonSave() {
		
		// Check for existence
		if (buttonSave == null) {
			
			// Create Instance
			buttonSave = new JButton();
			
			// Language and Icon
			buttonSave.setText(Lang.get("Button.Save"));
			buttonSave.setIcon(new ImageIcon(
					Lyra.getIconByMap("Button-Accept")));
			
			// Identifier and Action
			buttonSave.setName("PLAYLIST.SAVE");
			buttonSave.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonSave;
	}
	// End of getButtonSave Method
	

	/**
	 * This method initialises buttonCancel.	
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonCancel() {
		
		// Check for Existence
		if (buttonCancel == null) {
			
			// Create Instance
			buttonCancel = new JButton();
			
			// Language and Icon
			buttonCancel.setText(Lang.get("Button.Cancel"));
			buttonCancel.setIcon(new ImageIcon(
					Lyra.getIconByMap("Button-Cancel")));

			// Identifier and Listener
			buttonCancel.setName("PLAYLIST.CANCEL");
			buttonCancel.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonCancel;
	}
	// End of getButtonCancel Method

	
	/**
	 * This method initialises fieldName.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldName() {
		
		// Check for Existence
		if (fieldName == null) {
			
			// Create verification document
			RegexConstrainedDocument doc = new RegexConstrainedDocument("^[\\w-. ]{0,45}$");

			// Create Instance, with filter and sizing
			fieldName = new JTextField(doc, "", 30);
			
			// Identifier
			fieldName.setName("PLAYLIST.NAME");
		}
		
		// Return Instance
		return fieldName;
	}
	// End of getFieldName Method

	
	/**
	 * This method initialises panelLocal.  Simply groups the Local radio button
	 * and associated label.   Used mostly to add in the necessary spacers.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelLocal() {
		
		// Check for Existence
		if (panelLocal == null) {
			
			// Create Instance
			panelLocal = new JPanel();
			
			// Establish Layout
			panelLocal.setLayout(new BoxLayout(getPanelLocal(), BoxLayout.X_AXIS));
			
			/*
			 * Here we try to establish the icon as being in a left margin of
			 * sorts.  So we will always lookup its width prior to establishing
			 * our own position.
			 */
			panelLocal.add(Box.createHorizontalStrut(
					getIconMessage().getIcon().getIconWidth() +
					Constants.getIndentGap()
				));
			
			// Insert Elements
			panelLocal.add(getRadioLocal(), null);
			panelLocal.add(getLabelLocal(), null);
			
			// Spacer to force position
			panelLocal.add(Box.createHorizontalGlue());
		}
		
		// Return Instance
		return panelLocal;
	}
	// End of getPanelLocal Method

	
	/**
	 * This method initialises panelRempote.  Simply groups the Remote radio 
	 * button and associated label.   Used mostly to add in the necessary
	 * spacers.
	 * <p>	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelRemote() {
		
		// Check for Existence
		if (panelRemote == null) {
			
			// Create Instance
			panelRemote = new JPanel();
			
			// Establish Layout
			panelRemote.setLayout(new BoxLayout(getPanelRemote(), BoxLayout.X_AXIS));
			
			/*
			 * Here we try to establish the icon as being in a left margin of
			 * sorts.  So we will always lookup its width prior to establishing
			 * our own position.
			 */
			panelRemote.add(Box.createHorizontalStrut(
					getIconMessage().getIcon().getIconWidth() +
					Constants.getIndentGap()
				));
			
			// Insert Elements
			panelRemote.add(getRadioRemote(), null);
			panelRemote.add(getLabelRemote(), null);
			
			// Spacer to force position
			panelRemote.add(Box.createHorizontalGlue());
		}
		
		// Return Instance
		return panelRemote;
	}
	// End of getPanelRemote Method
	

	/**
	 * This method initialises panelName.  Name panel, used mostly to establish
	 * a certain justification in the elements.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelName() {
		
		// Check for Existence
		if (panelName == null) {
			
			// Create Instance
			panelName = new JPanel();
			
			// Establish Layout
			panelName.setLayout(new BoxLayout(getPanelName(), BoxLayout.X_AXIS));
			
			/*
			 * Here we try to establish the icon as being in a left margin of
			 * sorts.  So we will always lookup its width prior to establishing
			 * our own position.
			 */
			panelName.add(Box.createHorizontalStrut(
					getIconMessage().getIcon().getIconWidth() +
					Constants.getSectionGap()
				));
			
			// Insert Element
			panelName.add(getFieldName(), null);
			
			// Additional padding
			panelName.add(Box.createHorizontalStrut(Constants.getSectionGap()));
		}
		
		// Return Instance
		return panelName;
	}
	// End of getPanelName Method
	
	
	/**
	 * Returns a formatted image used to decorate the dialog and conform to
	 * other existing lesser dialogs used.
	 * <p>
	 * @return javax.swing.Label
	 */
	private JLabel getIconMessage() {
		
		// Check for existence
		if (iconMessage == null) {
			
			// Create existence
			iconMessage = new JLabel();
			iconMessage.setIcon(new ImageIcon(
					Lyra.getIconByMap("Prompt-Question")));
		}
		
		// Return Instance
		return iconMessage;
	}
	// End of getIconMessage Method

	
	/**
	 * Returns a formatted label used to describe the fact that this list will
	 * be saved remotely.
	 * <p>
	 * @return javax.swing.Label
	 */
	private JLabel getLabelRemote() {
		
		// Check for Existence
		if (labelRemote == null) {
			
			// Create Instance
			labelRemote = new JLabel();
			
			// Establish language and attach
			labelRemote.setText(Lang.get("Playlist.Option.Remote"));
			labelRemote.setLabelFor(getRadioRemote());
		}
		
		// Return Instance
		return labelRemote;
	}
	// End of getLabelRemote Method
	
	
	/**
	 * Returns a formatted label informing the user that this playlist will be
	 * saved locally only.
	 * <p>
	 * @return javax.swing.Label
	 */
	private JLabel getLabelLocal() {
		
		// Check for Existence
		if (labelLocal == null) {
			
			// Return Instance
			labelLocal = new JLabel();
			
			// Set language and attach
			labelLocal.setText(Lang.get("Playlist.Option.Local"));
			labelLocal.setLabelFor(getRadioLocal());
		}
		
		// Return Instance
		return labelLocal;
	}
	// End of getLabelLocal Method
	
	
	/**
	 * This method initialises panelStatus	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelStatus() {
		
		// Determine existence
		if (panelStatus == null) {

			// Create instance
			panelStatus = new JPanel();
			
			// Establish layout
			panelStatus.setLayout(new BoxLayout(getPanelStatus(), BoxLayout.X_AXIS));
			
			// Add in resizing label/icon
			panelStatus.add(Box.createHorizontalGlue());
			panelStatus.add(getIconResize(), null);
		}
		
		// Return instance
		return panelStatus;
	}
	// End of getPanelStatus
	
	
	/**
	 * This method initialises iconResize
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getIconResize() {
		
		// Verify existence of element
		if (iconResize == null) {

			// Create and establish baseline
			iconResize = new JLabel(new ResizeIcon());
			iconResize.setOpaque(false);
			
			/*
			 * Making this icon do something of use is one of the core reasons
			 * why it was added to the display itself.  Here we create an 
			 * instance and add the listener, since it is not used anywhere else
			 * we don't worry out creating an instance earlier.
			 */
			WindowSizeListener sizer = new WindowSizeListener(this);
			
			// Add in listener elements
			iconResize.addMouseListener(sizer);
			iconResize.addMouseMotionListener(sizer);
		}
		
		// Return label
		return iconResize;
	}
	// End of getIconResize Method
	
	
	/**
	 * Implements the most basic logic elements.   Since they are not readily
	 * employed anywhere else they are included in a sub-class in order to 
	 * separate code and display elements.
	 * <p>
	 * @author Martin Foster
	 */
	private class Interact implements ActionListener, WindowListener {

		
		/**
		 * Primary constructor stub
		 */
		public Interact() {}
		
		
		/*
		 * LISTENERS
		 */
		
		/* (non-Javadoc)
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent evt) {
		
			/*
			 * We only want to deal with button elements, anything else will
			 * be ignored.
			 */
			if (evt.getSource() instanceof JButton) {
				
				// Extract source
				JButton source = (JButton)evt.getSource();
				
				/*
				 * The saving button is our most important element as it will
				 * allow us to capture the information and proceed with saving
				 * the list information. 
				 */
				if (source.getName().equals(getButtonSave().getName())) {
					
					/*
					 * We like to make sure that the name element is long
					 * enough to continue with processing.
					 */
					if (getFieldName().getText().toString().length() > 4) {
						
						// Capture the name element
						Name = getFieldName().getText().toString();
						
						/*
						 * This is a remote list, which means that we will have
						 * to compare against remote entries on the server.
						 */
						if ((getRadioRemote().isSelected())
						 && (!myRemote.equals(Name)))
							Local = false;

						/*
						 * This is a local list, which means that we are 
						 * required to confirm the list does not already
						 * exist locally.
						 */
						else if ((getRadioLocal().isSelected())
						 && (!myLocal.equals(Name)))
							Local = true;


						/*
						 * The service checkbox is only used if we are in fact
						 * integrating.  Otherwise it will always return false.
						 */
						Service = (Integrate)
							? getCheckService().isSelected()
							: false;
						
						
						// Prevent additional processing
						Process = false;
						
						// Interrupt
						myThread.interrupt();
						
						// Close off the frame
						myFrame.setVisible(false);
						myFrame.dispose();
						
						// Return without incident
						return;
					}
				}
				
				/*
				 * Cancel means that we will not be saving this list and
				 * returning to the main program.
				 */
				else if (source.getName().equals(getButtonCancel().getName())) {
					
					// Prevent additional processing
					Process = false;
					
					// Cancel means null name
					Name = null;
					
					// Interrupt process
					myThread.interrupt();
					
					// Close off the frame
					myFrame.setVisible(false);
					myFrame.dispose();
					
					// Return quietly
					return;
				}
				
				
				/*
				 * The text does not match necessary criteria.  So the user
				 * will be notified and the text field will be cleared out.
				 */	
				
				// Create new instance of dialog
				RapidDialog rd = new RapidDialog(Lang);
				
				// Display message
				rd.noticeFail(myFrame, Lang.get("Playlist.Message.Fail"));
				
				// Adjust Field Name
				getFieldName().setText("");
			}
		}
		// End of actionPerformed Method


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowClosing(java.awt.event.WindowEvent)
		 */
		public void windowClosing(WindowEvent e) {
			
			// Prevent additional processing
			Process = false;
			
			// Cancel means null name
			Name = null;
			
			// Interrupt process
			myThread.interrupt();
			
			// Close the dialog
			myFrame.setVisible(false);
			myFrame.dispose();
		}
		// End of windowClosing Method
		

		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowActivated(java.awt.event.WindowEvent)
		 */
		public void windowActivated(WindowEvent arg0) {}


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowClosed(java.awt.event.WindowEvent)
		 */
		public void windowClosed(WindowEvent e) {}


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowDeactivated(java.awt.event.WindowEvent)
		 */
		public void windowDeactivated(WindowEvent e) {}


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowDeiconified(java.awt.event.WindowEvent)
		 */
		public void windowDeiconified(WindowEvent e) {}


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowIconified(java.awt.event.WindowEvent)
		 */
		public void windowIconified(WindowEvent e) {}


		/* (non-Javadoc)
		 * @see java.awt.event.WindowListener#windowOpened(java.awt.event.WindowEvent)
		 */
		public void windowOpened(WindowEvent e) {}
	
	}
	// End of Interact Class
	
	
	/**
	 * Simple all encompassing class which is used to pass on all of the
	 * necessary class details in order to process a save request.
	 * <p>
	 * @author Martin Foster
	 */
	public class PlaylistSave {
		
		// Stored elements
		private boolean myLocal;
		private boolean myService;
		private String myName;
		
		/**
		 * Stub constructor for the class.  Will not initialise any of the
		 * values in the class.
		 */
		public PlaylistSave() {}
		
		
		/**
		 * Complete initialisation constructor, which is passed all necessary
		 * elements for which to use in saving a playlist.
		 * <p>
		 * @param local Boolean indicating local.
		 * @param service Boolean indicating saving to Last.fm service.
		 * @param name String indicating the name of the list.
		 */
		public PlaylistSave(boolean local, boolean service, String name) {
			
			// Pass Up
			this.myLocal = local;
			this.myName  = name;
			this.myService  = service;
		}
		// End of Constructor Method
		
		
		/**
		 * Complete initialisation constructor, which is passed all necessary
		 * elements for which to use in saving a playlist.
		 * <p>
		 * @param local Boolean indicating local.
		 * @param name String indicating the name of the list.
		 */
		public PlaylistSave(boolean local, String name) {
			
			// Pass Up
			this.myLocal = local;
			this.myName  = name;
			this.myService  = false;
		}
		// End of Constructor Method
		
		
		/**
		 * Used to extract the value of local.   When local is true the saving
		 * of the list will be done locally, otherwise it is assumed to be
		 * remote.
		 * <p>
		 * @param local Boolean indicating local.
		 */
		public void setLocal(boolean local) {
			
			// Straight forward pass through
			this.myLocal = local;
		}
		// End of setLocal Method
		
		
		/**
		 * Used to extract the value of Save.   When Save is true a copy of 
		 * the list will be sent off to the Last.fm service.
		 * <p>
		 * @param Save Boolean indicating saving to Last.fm.
		 */
		public void setService(boolean Save) {
			
			// Straight forward pass through
			this.myService = Save;
		}
		// End of setService Method
		
		
		/**
		 * Used to pass on the value of local.   When local is true the saving
		 * of the list will be done locally, otherwise it is assumed to be
		 * remote.
		 * <p>
		 * @return Boolean indicating local.
		 */
		public boolean getLocal() {
			
			// Straight forward pass through
			return this.myLocal;
		}
		// End of setLocal Method

		
		/**
		 * Used to pass on the value of Save.  When Save is true a copy of 
		 * the list will be sent off to the Last.fm service.
		 * <p>
		 * @return Boolean indicating saving to Last.fm.
		 */
		public boolean getService() {
			
			// Straight forward pass through
			return this.myService;
		}
		// End of setService Method
		
		
		/**
		 * Used to pass on the value of name.   The name is used to later 
		 * identify the list for selection and play.
		 * <p>
		 * @param name String indicating the name of the list.
		 */
		public void setName(String name) {
			
			// Straight forward pass through
			this.myName = name;
		}
		// End of setName Method
		
		
		/**
		 * Used to extract the value of name.   The name is used to later 
		 * identify the list for selection and play.
		 * <p>
		 * @return String indicating the name of the list.
		 */
		public String getName() {
			
			// Straight forward pass through
			return this.myName;
		}
		// End of setName Method
	
		
		/**
		 * Quick check to determine if this is to be saved locally.
		 * <p>
		 * @return Boolean indicating local.
		 */
		public boolean isLocal() {
			
			// Straight return
			return myLocal;
		}
		// End of isLocal Method
		
		
		/**
		 * Quick check to determine if this is to be saved to the Last.fm 
		 * service.
		 * <p>
		 * @return Boolean indicating saving to Last.fm.
		 */
		public boolean isService() {
			
			// Straight return
			return myService;
		}
		// End of isService Method
		
		
		/**
		 * Quick check to determine if this is to be saved remotely
		 * <p>
		 * @return Boolean indicating local.
		 */
		public boolean isRemote() {
			
			// Return the opposite of return
			return !myLocal;
		}
		// End if isRemote Method
	}
	// End of PlaylistSave Class
}
