/**
 * 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.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.KeyStroke;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.lyra.HelpManager;
import org.lyra.Ledger;
import org.lyra.Lyra;
import org.lyra.Operations;
import org.lyra.Database.Comptroller;
import org.lyra.Host.Options;
import org.lyra.Host.Pendulum;
import org.lyra.Host.Playback;
import org.lyra.Host.Playlist;
import org.lyra.Host.Status;
import org.lyra.Host.Terminus;
import org.lyra.Host.UpdateEvent;
import org.lyra.Host.UpdateListener;
import org.lyra.Host.Updater;
import org.lyra.Interface.Menu.CrossfadeMenu;
import org.lyra.Interface.Menu.VolumeMenu;

import java.awt.Event;
import java.awt.Window;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;


/**
 * The menu structure can have a very complex amount of elements attached to it
 * that would invariably render the primary display a more complex code
 * element.   This separates the menu and provides direct processing of choices
 * made by users.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 147%, %LastChangedDate, 06/11/11 4:57PM%
 */
public class PlayerOrganiserMenu extends JFrame {

	// Serial Needs
	private static final long serialVersionUID = 1L;
	
	/*
	 * USER INTERFACE ELEMENTS
	 */
	private JPanel jContentPane = null;
	
	// Menu Bar Itself
	private JMenuBar menuOrganiser = null;
	
	// Main Structure
	private JMenu subMain = null;
	private JMenuItem itemMainInformation = null;
	private JMenuItem itemMainScan = null;
	private JMenuItem itemMainCompact = null;
	private JCheckBoxMenuItem checkMainSearch = null;
	private JMenuItem itemMainQuit = null;
	
	// Options Structure
	private JMenu subOptions = null;
	private JCheckBoxMenuItem checkOptionsConsume = null;
	private JCheckBoxMenuItem checkOptionsRandom = null;
	private JCheckBoxMenuItem checkOptionsRepeat = null;
	private JCheckBoxMenuItem checkOptionsSingle = null;
	private JCheckBoxMenuItem checkOptionsMute = null;
	private JMenuItem itemOptionsPreferences = null;

	
	// Playback Options
	private JMenu subPlayback = null;
	private JMenuItem itemPlaybackPrevious = null;
	private JMenuItem itemPlaybackStop = null;
	private JMenuItem itemPlaybackPlay = null;
	private JMenuItem itemPlaybackNext = null;
	private JMenuItem itemPlaybackShuffle = null;
	private JMenuItem itemPlaybackClear = null;
	
	// Help Section
	private JMenu subHelp = null;
	private JMenuItem itemHelpDisplay = null;
	private JMenuItem itemHelpFrequently = null;
	private JMenuItem itemHelpAbout = null;

	// Volume Control
	private JMenu menuVolume = null;
	private VolumeMenu controlVolume = null;

	
	// Crossfade Control
	private JMenu menuCrossfade = null;
	private CrossfadeMenu controlCrossfade = null;
	
	/*
	 * PACKAGE INSTANCES
	 */
	private Map<String,String> Lang;
	private Map<String,String> Global;
	
	private Ledger Logger;
	private Comptroller Manager;
	private Updater Watcher;
	private Pendulum Clock;
	private Status Info;
	private Options Modes;
	private Playback Control;
	private Playlist Lists;
	
	
	/*
	 * CLASS INSTANCES
	 */
	private Interact Interface;
	private JToolBar Search;
	private Window Parent;
	
	// List of enabled Commands
	private Set<String> Enabled;
	
	
	/**
	 * This is the visual editor constructor only.   The primary method for
	 * accessing the menu under normal circumstances will not revolve around
	 * this.
	 */
	@SuppressWarnings("serial")
	public PlayerOrganiserMenu() {
		
		// Super class handling
		super();
		
		/*
		 * Generate language elements for use.   These are defaults only for
		 * visual designers to use and are not language independent as the
		 * actual hash is.
		 */
		Lang = new HashMap<String,String>(){
			{
				// Titles and Sections
				put("Organizer.Menu.Sub.Main","Main");
				put("Organizer.Menu.Sub.Options","Options");
				put("Organizer.Menu.Sub.Playback","Playback");
				put("Organizer.Menu.Sub.Help","Help");
				put("Organizer.Menu.Options.Mute","Mute");
				put("Organizer.Menu.Main.Quit","Quit");
				put("Organizer.Menu.Help.About","About");
				put("Organizer.Menu.Help.Display","Help");
				put("Organizer.Menu.Playback.Previous","Previous");
				put("Organizer.Menu.Playback.Stop","Stop");
				put("Organizer.Menu.Playback.Play","Play");
				put("Organizer.Menu.Playback.Next","Next");
				put("Organizer.Menu.Options.Crossfade","Crossfade");
				put("Organizer.Menu.Options.Volume","Volume");
				put("Organizer.Menu.Playback.Shuffle","Shuffle");
				put("Organizer.Menu.Playback.Clear","Clear Playlist");
				put("Organizer.Menu.Options.Preferences","Preferences");
				put("Organizer.Menu.Main.Scan","Rescan Library");
				put("Organizer.Menu.Options.Consume","Consume");
				put("Organizer.Menu.Options.Random","Random");
				put("Organizer.Menu.Options.Repeat","Repeat");
				put("Organizer.Menu.Options.Single","Single");
				put("Organizer.Menu.Help.Frequently","Frequently Asked Questions");
				put("Organizer.Menu.Main.Switch","Switch Hosts");
				put("Organizer.Menu.Main.Information","Statistics");
				
				// Tool Tip Texts
				put("Tooltip.Options.Mute","Mute the volume or return to previous state...");
				put("Tooltip.Main.Quit","Exit the application...");
				put("Tooltip.Help.About","Display about section...");
				put("Tooltip.Help.Display","Main help display...");
				put("Tooltip.Playback.Previous","Plays previous song in the playlist...");
				put("Tooltip.Playback.Stop","Toggles pause/resumes playing...");
				put("Tooltip.Playback.Play","Begins playing the playlist...");
				put("Tooltip.Playback.Next","Plays next song in the playlist...");
				put("Tooltip.Options.Crossfade","Sets crossfading between songs...");
				put("Tooltip.Options.Volume","Adjusts the playback volume...");
				put("Tooltip.Playback.Shuffle","Shuffles the current playlist...");
				put("Tooltip.Playback.Clear","Clears the current playlist...");
				put("Tooltip.Options.Preferences","Manage interface, hosts and playback options...");
				put("Tooltip.Main.Scan","Updates the music database: find new files, remove deleted files, update modified files...");
				put("Tooltip.Options.Consume","When consume is activated, each song played is removed from playlist...");
				put("Tooltip.Options.Random","Sets random state...");
				put("Tooltip.Options.Repeat","Sets repeat state...");
				put("Tooltip.Options.Single","When single is activated, playback is stopped after current song, or song is repeated if the repeat mode is enabled...");
				put("Tooltip.Help.Frequently","Display the Frequency Asked Questions (FAQ)...");
				put("Tooltip.Main.Switch","Disconnect from current host and switch to a new entry...");
				put("Tooltip.Main.Information","Display host statistics and information...");
			}
		};
		
		// Blanked list for successful generation
		Enabled = new TreeSet<String>();
		
		// Process the creation of elements
		initFrame();
	}
	// End of Constructor Method

	
	/**
	 * Primary application constructor which is passed all of the necessary
	 * elements in order to display settings and handle interaction.
	 * <p>
	 * @param logger Ledger instances for debugging and information.
	 * @param manager Database access manager instance.
	 * @param watcher Updater class for receiving updates.
	 * @param clock Pendulum instance for time updates.
	 * @param info Host status instance.
	 * @param control Playback control instance.
	 * @param lists Playlist control.
	 * @param modes Options handling instance.
	 * @param lang Language map for independent language elements.
	 * @param global Global settings and configuration options.
	 * @param search Search tool bar used to set visibility.
	 * @param parent Parent window to block.
	 */
	public PlayerOrganiserMenu(
			Ledger logger, 
			Comptroller manager, 
			Updater watcher,
			Pendulum clock,
			Status info, 
			Playback control,
			Playlist lists,
			Options modes, 
			Map<String,String> lang, 
			Map<String,String> global, 
			JToolBar search,
			Window parent) {

		
		// Have super class do its thing
		super();
		
		// Pass up
		this.Logger  = logger;
		this.Manager = manager;
		this.Watcher = watcher;
		this.Clock   = clock;
		this.Info    = info;
		this.Control = control;
		this.Lists   = lists;
		this.Modes   = modes;
		this.Lang    = lang;
		this.Global  = global;
		this.Search  = search;
		this.Parent  = parent;
		
		// Enabled Commands
		this.Enabled = Info.getEnabledAsSet();
		
		// Create instance
		Interface = new Interact();
		
		// Initialise the Dialog
		initFrame();
		
		// Establish base elements
		Interface.setState();
		
		// Be aware of host updates
		Watcher.addUpdateListener(Interface);
	}
	// End of Constructor Class
	
	
	/**
	 * Generates all of the necessary interface elements for display.  An
	 * exclusive requirement of the visual editor usage.
	 */
	private void initFrame() {
		
		// Insert instance of menu bar
		this.setJMenuBar(getMenuOrganiser());
		
		// Create Frame Element
		this.setContentPane(getJContentPane());
	}
	// End of initFrame Method

	
	/**
	 * This method initialises jContentPane.  Returns an empty panel with a
	 * basic layout in order to satisfy a visual editors requirement.
	 * <p>
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		
		// Check for Existence
		if (jContentPane == null) {
			
			// Create instance and establish layout
			jContentPane = new JPanel();
		}
		
		// Return Instance
		return jContentPane;
	}
	// End of getJContentPane Method

	
	/**
	 * This method initialises menuOrganiser.   Will import all of the
	 * appropriate sub-menu items which are loaded in the desired order.	
	 * <p>
	 * @return javax.swing.JMenuBar	
	 */
	private JMenuBar getMenuOrganiser() {
		
		// Check for Existence
		if (menuOrganiser == null) {
			
			// Create instance
			menuOrganiser = new JMenuBar();
			
			// Insert sub-menu elements
			menuOrganiser.add(getSubMain());
			menuOrganiser.add(getSubOptions());
			menuOrganiser.add(getSubPlayback());
			menuOrganiser.add(getSubHelp());
		}
		
		// Return Instance
		return menuOrganiser;
	}
	// End of getMenuOrganiser Method

	
	/**
	 * This method initialises subMain, the main menu structure which provides
	 * the capability to exit the application, rescan the host for new songs
	 * and switch hosts.	
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getSubMain() {
		
		// Check for Existence
		if (subMain == null) {
			
			// Create Menu Element
			subMain = new JMenu();
			
			// Establish Language elements
			subMain.setText(Lang.get("Organizer.Menu.Sub.Main"));

			// Identifier
			subMain.setName("SUB.MAIN");
			
			/*
			 * The items inserted in the following section fit into two parts.
			 * The first being related to additional host capabilities not
			 * normally required and the second are application specific.
			 */
			
			// Separator and Scan Capability
			subMain.add(getItemMainInformation());
			subMain.add(getItemMainScan());
			
			// Compact View Method
			subMain.addSeparator();
			subMain.add(getItemMainCompact());
			
			// Search View
			subMain.addSeparator();
			subMain.add(getCheckMainSearch());
			
			// Application Specific
			subMain.addSeparator();
			subMain.add(getItemMainQuit());
		}
		
		// Return Instance
		return subMain;
	}
	// End of getSubMain Method

	
	/**
	 * This method initialises subOptions.  Options contains featured options
	 * for which the host provides.	
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getSubOptions() {
		
		// Check for Existence
		if (subOptions == null) {
			
			// Create Instance
			subOptions = new JMenu();
			
			// Language Elements
			subOptions.setText(Lang.get("Organizer.Menu.Sub.Options"));
			
			// Identifier
			subOptions.setName("SUB.OPTIONS");
			
			/*
			 * The elements for insertion are divided into two groups which are
			 * those which carry stateful information and those which require a
			 * more complex control.
			 */
			
			// Stateful Options
			subOptions.add(getCheckOptionsConsume());
			subOptions.add(getCheckOptionsRandom());
			subOptions.add(getCheckOptionsRepeat());
			subOptions.add(getCheckOptionsSingle());
			subOptions.add(getCheckOptionsMute());
			
			// Separator and Complex Controls
			subOptions.addSeparator();
			subOptions.add(getMenuVolume());
			subOptions.add(getMenuCrossfade());
			
			// Separator and General Preferences
			subOptions.addSeparator();
			subOptions.add(getItemOptionsPreferences());
		}
		
		// Return Instance
		return subOptions;
	}
	// End of getSubOptions Method

	
	/**
	 * This method initialises subPlayback.  Playback offers a shortcut and
	 * alternative means to access the playback features.	
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getSubPlayback() {
		
		// Check for Existence
		if (subPlayback == null) {
			
			// Create Instance
			subPlayback = new JMenu();
			
			// Language
			subPlayback.setText(Lang.get("Organizer.Menu.Sub.Playback"));
			
			// Identifier
			subPlayback.setName("SUB.PLAYBACK");
			
			
			/*
			 * The elements for insertion are split into two large components
			 * being those related to playback directly and the others related
			 * to the current playlist control.
			 */
			
			// Playback Options
			subPlayback.add(getItemPlaybackPrevious());
			subPlayback.add(getItemPlaybackStop());
			subPlayback.add(getItemPlaybackPlay());
			subPlayback.add(getItemPlaybackNext());
			
			// Separator and Playlist Options
			subPlayback.addSeparator();
			subPlayback.add(getItemPlaybackShuffle());
			subPlayback.add(getItemPlaybackClear());
		}
		
		// Return Instance
		return subPlayback;
	}
	// End of getSubPlayback method

	
	/**
	 * This method initialises subHelp.  Displays all related help elements in
	 * the menu structure.	
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getSubHelp() {
		
		// Check for Existence
		if (subHelp == null) {
			
			// Create Instance
			subHelp = new JMenu();
			
			// Language
			subHelp.setText(Lang.get("Organizer.Menu.Sub.Help"));
			
			// Identifier
			subHelp.setName("SUB.HELP");
			
			// Insert Primary Help Elements
			subHelp.add(getItemHelpDisplay());
			subHelp.add(getItemHelpFrequently());
			
			// Separator and Bragging Rights
			subHelp.addSeparator();
			subHelp.add(getItemHelpAbout());
		}
		
		// Return Instance
		return subHelp;
	}
	// End of getSubHelp Method

	
	/**
	 * This method initialises itemMainCompact.  Quick action to an information
	 * only compact display.
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemMainCompact() {
		
		// Check for Existence
		if (itemMainCompact == null) {
			
			// Create Instance
			itemMainCompact = new JMenuItem();
			
			// Language Element
			itemMainCompact.setText(Lang.get("Organizer.Menu.Main.Compact"));
			itemMainCompact.setToolTipText(Lang.get("Tooltip.Main.Compact"));
			
			// Quick Keys
			itemMainCompact.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0, false));
			itemMainCompact.setMnemonic(KeyEvent.VK_C);

			// Identifier and Listener
			itemMainCompact.setName("MAIN.COMPACT");
			itemMainCompact.addActionListener(Interface);
		}
		
		// Return Instance
		return itemMainCompact;
	}
	// End of getItemMainCompact Method
	
	
	/**
	 * This method initialises itemMainQuit.  Quick action to exit the
	 * application.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemMainQuit() {
		
		// Check for Existence
		if (itemMainQuit == null) {
			
			// Create Instance
			itemMainQuit = new JMenuItem();
			
			// Language Element
			itemMainQuit.setText(Lang.get("Organizer.Menu.Main.Quit"));
			itemMainQuit.setToolTipText(Lang.get("Tooltip.Main.Quit"));
			
			// Quick Keys
			itemMainQuit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Event.CTRL_MASK, false));
			itemMainQuit.setMnemonic(KeyEvent.VK_Q);

			// Identifier and Listener
			itemMainQuit.setName("MAIN.QUIT");
			itemMainQuit.addActionListener(Interface);

		}
		
		// Return Instance
		return itemMainQuit;
	}
	// End of getItemMainQuit Method

	
	/**
	 * This method initialises itemHelpAbout.  Display the a About section of
	 * the help page.	
	 * <p>	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemHelpAbout() {
		
		// Check for Existence
		if (itemHelpAbout == null) {
			
			// Create Instance
			itemHelpAbout = new JMenuItem();
			
			// Language Elements
			itemHelpAbout.setText(Lang.get("Organizer.Menu.Help.About"));
			itemHelpAbout.setToolTipText(Lang.get("Tooltip.Help.About"));

			// Quick Keys
			itemHelpAbout.setMnemonic(KeyEvent.VK_A);

			// Identifier and Listener
			itemHelpAbout.setName("HELP.ABOUT");
			itemHelpAbout.addActionListener(Interface);
		}
		
		// Return Instance
		return itemHelpAbout;
	}
	// End of getItemHelpAbout Method

	
	/**
	 * This method initialises itemHelpDisplay.  Main help page for the
	 * application.	
	 * <p>	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemHelpDisplay() {
		
		//  Check for Existence
		if (itemHelpDisplay == null) {
			
			// Create Instance
			itemHelpDisplay = new JMenuItem();

			// Language Elements
			itemHelpDisplay.setText(Lang.get("Organizer.Menu.Help.Display"));
			itemHelpDisplay.setToolTipText(Lang.get("Tooltip.Help.Display"));

			// Quick Keys
			itemHelpDisplay.setMnemonic(KeyEvent.VK_H);
			itemHelpDisplay.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F11, Event.CTRL_MASK, false));

			// Identifier and Listener
			itemHelpDisplay.setName("HELP.DISPLAY");
			itemHelpDisplay.addActionListener(Interface);

		}
		return itemHelpDisplay;
	}
	// End of getItemHelpDisplay Method
	

	/**
	 * This method initialises itemPlaybackPrevious.  Causes the music player
	 * daemon to move back to the previous song.
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackPrevious() {
		
		// Check for Existence
		if (itemPlaybackPrevious == null) {
			
			// Create Instance
			itemPlaybackPrevious = new JMenuItem();
			
			// Language Elements
			itemPlaybackPrevious.setText(Lang.get("Organizer.Menu.Playback.Previous"));
			itemPlaybackPrevious.setToolTipText(Lang.get("Tooltip.Playback.Previous"));

			// Quick Keys
			itemPlaybackPrevious.setMnemonic(KeyEvent.VK_R);
			itemPlaybackPrevious.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackPrevious.setName("PLAYBACK.PREVIOUS");
			itemPlaybackPrevious.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackPrevious.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Player.Previous")));
		}
		
		// Return Instance
		return itemPlaybackPrevious;
	}
	// End of getItemPlaybackPrevious Method

	
	/**
	 * This method initialises itemPlaybackStop.  Causes the music player
	 * daemon to stop.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackStop() {
		
		// Check Existence
		if (itemPlaybackStop == null) {
			
			// Create Instance
			itemPlaybackStop = new JMenuItem();
			
			// Language Elements
			itemPlaybackStop.setText(Lang.get("Organizer.Menu.Playback.Stop"));
			itemPlaybackStop.setToolTipText(Lang.get("Tooltip.Playback.Stop"));

			// Quick Keys
			itemPlaybackStop.setMnemonic(KeyEvent.VK_S);
			itemPlaybackStop.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackStop.setName("PLAYBACK.STOP");
			itemPlaybackStop.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackStop.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Player.Stop")));
		}
		
		// Return Instance
		return itemPlaybackStop;
	}
	// End of getItemPlaybackStop Method
	

	/**
	 * This method initialises itemPlaybackPlay. Causes the music player
	 * daemon to start playback.		
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackPlay() {
		
		// Check for Existence
		if (itemPlaybackPlay == null) {
			
			// Create Instance
			itemPlaybackPlay = new JMenuItem();
			
			// Language Elements
			itemPlaybackPlay.setText(Lang.get("Organizer.Menu.Playback.Play"));
			itemPlaybackPlay.setToolTipText(Lang.get("Tooltip.Playback.Play"));

			// Quick Keys
			itemPlaybackPlay.setMnemonic(KeyEvent.VK_P);
			itemPlaybackPlay.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackPlay.setName("PLAYBACK.PLAY");
			itemPlaybackPlay.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackPlay.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Player.Play")));
		}
		
		// Return Instance
		return itemPlaybackPlay;
	}
	// End of getItemPlaybackPlay

	
	/**
	 * This method initialises itemPlaybackNext.  Causes the music player
	 * daemon to move back to the next song.  	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackNext() {
		
		// Check for Existence
		if (itemPlaybackNext == null) {
			
			// Create Instance
			itemPlaybackNext = new JMenuItem();
			
			// Language Element
			itemPlaybackNext.setText(Lang.get("Organizer.Menu.Playback.Next"));
			itemPlaybackNext.setToolTipText(Lang.get("Tooltip.Playback.Next"));

			// Quick Keys
			itemPlaybackNext.setMnemonic(KeyEvent.VK_N);
			itemPlaybackNext.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackNext.setName("PLAYBACK.NEXT");
			itemPlaybackNext.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackNext.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Player.Next")));
		}
		
		// Return Instance
		return itemPlaybackNext;
	}
	// End of getItemPlaybackNext Method


	/**
	 * This method initialises itemPlaybackShuffle.  Shuffles the current
	 * playlist.  Which is different then random play from the fact that
	 * it only affects the playlist.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackShuffle() {
		
		// Check for Existence
		if (itemPlaybackShuffle == null) {
			
			// Create Instance
			itemPlaybackShuffle = new JMenuItem();
			
			// Language Element
			itemPlaybackShuffle.setText(Lang.get("Organizer.Menu.Playback.Shuffle"));
			itemPlaybackShuffle.setToolTipText(Lang.get("Tooltip.Playback.Shuffle"));

			// Quick Keys
			itemPlaybackShuffle.setMnemonic(KeyEvent.VK_S);
			itemPlaybackShuffle.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackShuffle.setName("PLAYBACK.SHUFFLE");
			itemPlaybackShuffle.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackShuffle.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Current.Shuffle")));
		}
		
		// Return Instance
		return itemPlaybackShuffle;
	}
	// End of getItemPlaybackShuffle Method

	
	/**
	 * This method initialises itemPlaybackClear.  Clear the current playlist
	 * of all songs.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemPlaybackClear() {
		
		// Check for Existence
		if (itemPlaybackClear == null) {
			
			// Create Instance
			itemPlaybackClear = new JMenuItem();
			
			// Language Element
			itemPlaybackClear.setText(Lang.get("Organizer.Menu.Playback.Clear"));
			itemPlaybackClear.setToolTipText(Lang.get("Tooltip.Playback.Clear"));
			
			// Quick Keys
			itemPlaybackClear.setMnemonic(KeyEvent.VK_C);
			itemPlaybackClear.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.ALT_MASK, false));

			// Identifier and Listener
			itemPlaybackClear.setName("PLAYBACK.CLEAR");
			itemPlaybackClear.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemPlaybackClear.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Current.Clear")));
		}
		
		// Return Instance
		return itemPlaybackClear;
	}
	// End of getItemPlaybackClear Method

	
	/**
	 * This method initialises itemOptionsPreferences.  Opens the preferences
	 * dialog for control over various aspects of the application.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemOptionsPreferences() {
		
		// Check for Existence
		if (itemOptionsPreferences == null) {
			
			// Create Instance
			itemOptionsPreferences = new JMenuItem();
			
			// Language Elements
			itemOptionsPreferences.setText(Lang.get("Organizer.Menu.Options.Preferences"));
			itemOptionsPreferences.setToolTipText(Lang.get("Tooltip.Options.Preferences"));

			// Identifier and Listener
			itemOptionsPreferences.setName("OPTIONS.PREFERENCES");
			itemOptionsPreferences.addActionListener(Interface);
		}
		
		// Return Instance
		return itemOptionsPreferences;
	}
	// End of getItemOptionsPreferences Method

	
	/**
	 * This method initialises itemMainScan.  Prompts a rescan of the music
	 * database on the host.
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemMainScan() {
		
		// Check for Existence
		if (itemMainScan == null) {
			
			// Create Instance
			itemMainScan = new JMenuItem();
			
			// Language Elements
			itemMainScan.setText(Lang.get("Organizer.Menu.Main.Scan"));
			itemMainScan.setToolTipText(Lang.get("Tooltip.Main.Scan"));

			// Quick Keys
			itemMainScan.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK, false));
			itemMainScan.setMnemonic(KeyEvent.VK_R);

			// Identifier and Listener
			itemMainScan.setName("MAIN.SCAN");
			itemMainScan.addActionListener(Interface);
			
			// Enable Interface Based on Support
			itemMainScan.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Database.Update")));
		}
		
		// Return Instance
		return itemMainScan;
	}
	// End of getItemMainScan Method
	

	/**
	 * This method initialises checkOptionsConsume.  When enabled, will remove
	 * a song once played from the current playlist.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckOptionsConsume() {
		
		// Check Existence
		if (checkOptionsConsume == null) {
			
			// Create Instance
			checkOptionsConsume = new JCheckBoxMenuItem();
			
			// Language Elements
			checkOptionsConsume.setText(Lang.get("Organizer.Menu.Options.Consume"));
			checkOptionsConsume.setToolTipText(Lang.get("Tooltip.Options.Consume"));

			// Identifier and Listener
			checkOptionsConsume.setName("OPTIONS.CONSUME");
			checkOptionsConsume.addActionListener(Interface);
			
			// Enable Interface Based on Support
			checkOptionsConsume.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Consume")));
		}
		
		// Return Instance
		return checkOptionsConsume;
	}
	// End of getCheckOptionsConsume

	
	/**
	 * This method initialises checkOptionsRandom.  Enables random play for
	 * the music.   Differs from shuffle which is a one time execution.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckOptionsRandom() {
		
		// Check for Existence
		if (checkOptionsRandom == null) {
			
			// Create Instance
			checkOptionsRandom = new JCheckBoxMenuItem();
			
			// Language Elements
			checkOptionsRandom.setText(Lang.get("Organizer.Menu.Options.Random"));
			checkOptionsRandom.setToolTipText(Lang.get("Tooltip.Options.Random"));

			// Identifier and Listener
			checkOptionsRandom.setName("OPTIONS.RANDOM");
			checkOptionsRandom.addActionListener(Interface);
			
			// Enable Interface Based on Support
			checkOptionsRandom.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Random")));
		}
		
		// Return Instance
		return checkOptionsRandom;
	}
	// End of getCheckOptionsRandom Method

	
	/**
	 * This method initialises checkOptionsRepeat.  Sets repetition based on
	 * the value selected by Single.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckOptionsRepeat() {
		
		// Check for Existence
		if (checkOptionsRepeat == null) {
			
			// Create Instance
			checkOptionsRepeat = new JCheckBoxMenuItem();
			
			// Language Elements
			checkOptionsRepeat.setText(Lang.get("Organizer.Menu.Options.Repeat"));
			checkOptionsRepeat.setToolTipText(Lang.get("Tooltip.Options.Repeat"));

			// Identifier and Listener
			checkOptionsRepeat.setName("OPTIONS.REPEAT");
			checkOptionsRepeat.addActionListener(Interface);
			
			// Enable Interface Based on Support
			checkOptionsRepeat.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Repeat")));
		}
		
		// Return Instance
		return checkOptionsRepeat;
	}
	// End of getCheckOptionsRepeat Method

	
	/**
	 * This method initialises checkOptionsSingle.  Sets single play as in will
	 * play a singular song.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckOptionsSingle() {
		
		// Check for Existence
		if (checkOptionsSingle == null) {
			
			// Create Instance
			checkOptionsSingle = new JCheckBoxMenuItem();
			
			// Language Elements
			checkOptionsSingle.setText(Lang.get("Organizer.Menu.Options.Single"));
			checkOptionsSingle.setToolTipText(Lang.get("Tooltip.Options.Single"));

			// Identifier and Listener
			checkOptionsSingle.setName("OPTIONS.SINGLE");
			checkOptionsSingle.addActionListener(Interface);
			
			// Enable Interface Based on Support
			checkOptionsSingle.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Single")));
		}
		
		// Return Instance
		return checkOptionsSingle;
	}
	// End of getCheckOptionsSingle Method

	
	/**
	 * This method initialises checkMainSearch.  Sets single play as in will
	 * play a singular song.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckMainSearch() {
		
		// Check for Existence
		if (checkMainSearch == null) {
			
			// Create Instance
			checkMainSearch = new JCheckBoxMenuItem();
			
			// Language Elements
			checkMainSearch.setText(Lang.get("Organizer.Menu.Main.Search"));
			checkMainSearch.setToolTipText(Lang.get("Tooltip.Main.Search"));

			// Quick Keys
			checkMainSearch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK, false));
			checkMainSearch.setMnemonic(KeyEvent.VK_F);

			
			// Identifier and Listener
			checkMainSearch.setName("MAIN.SINGLE");
			checkMainSearch.addActionListener(Interface);
			
			// Enabled State
			checkMainSearch.setSelected(Search.isVisible());
		}
		
		// Return Instance
		return checkMainSearch;
	}
	// End of getCheckMainSearch Method
	
	
	/**
	 * This method initialises itemHelpFrequently.  Displays the Frequently
	 * Asked Questions (FAQ) page.	
	 * <p> 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemHelpFrequently() {
		
		// Check for Existence
		if (itemHelpFrequently == null) {
			
			// Return Instance
			itemHelpFrequently = new JMenuItem();
			
			// Language Element
			itemHelpFrequently.setText(Lang.get("Organizer.Menu.Help.Frequently"));
			itemHelpFrequently.setToolTipText(Lang.get("Tooltip.Help.Frequently"));
			
			// Quick Key
			itemHelpFrequently.setMnemonic(KeyEvent.VK_F);

			// Identifier and Listener
			itemHelpFrequently.setName("HELP.FREQUENTLY");
			itemHelpFrequently.addActionListener(Interface);
		}
		
		// Return Instance
		return itemHelpFrequently;
	}
	// End of getItemHelpFrequently


	/**
	 * This method initialises itemMainSwtich.  Extracts host statistical
	 * information.	
	 * <p>
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getItemMainInformation() {
		
		// Check for Existence
		if (itemMainInformation == null) {
			
			// Create Instance
			itemMainInformation = new JMenuItem();
			
			// Language Element
			itemMainInformation.setText(Lang.get("Organizer.Menu.Main.Information"));
			itemMainInformation.setToolTipText(Lang.get("Tooltip.Main.Information"));

			// Identifier and Listener
			itemMainInformation.setName("MAIN.INFORMATION");
			itemMainInformation.addActionListener(Interface);
		}
		
		// Return Instance
		return itemMainInformation;
	}
	// End of getItemMainInformation Method
	
	
	/**
	 * This method initialises checkOptionsMute.  Quick and easy way to mute the
	 * volume of the music being played.	
	 * <p>
	 * @return javax.swing.JCheckBoxMenuItem	
	 */
	private JCheckBoxMenuItem getCheckOptionsMute() {
		
		// Check for Existence
		if (checkOptionsMute == null) {
			
			// Create Instance
			checkOptionsMute = new JCheckBoxMenuItem();
			
			// Language Element
			checkOptionsMute.setText(Lang.get("Organizer.Menu.Options.Mute"));
			checkOptionsMute.setToolTipText(Lang.get("Tooltip.Options.Mute"));

			// Key Shortcuts
			checkOptionsMute.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, Event.ALT_MASK, false));
			
			// Identifier and Listener
			checkOptionsMute.setName("OPTIONS.MUTE");
			checkOptionsMute.addActionListener(Interface);
			
			// Enable Interface Based on Support
			checkOptionsMute.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Volume")));
		}
		
		// Return Instance
		return checkOptionsMute;
	}
	// End of checkOptionsMute Method

	
	/**
	 * Crossfade control menu structure and interface.
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getMenuCrossfade() {
		
		// Check for Existence
		if (menuCrossfade == null) {
			
			// Create Instance
			controlCrossfade = new CrossfadeMenu(Lang);
			menuCrossfade = controlCrossfade.getMenuCrossfade();
			
			// Add in listener
			controlCrossfade.addChangeListener(Interface);
			
			// Enable Interface Based on Support
			controlCrossfade.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Crossfade")));
		}
		
		// Return Instance
		return menuCrossfade;
	}
	// End of getMenuCrossfade Method
	
	
	/**
	 * Volume control menu structure and interface.
	 * <p>
	 * @return javax.swing.JMenu	
	 */
	private JMenu getMenuVolume() {
		
		// Check for Existence
		if (menuVolume == null) {
			
			// Create Instance
			controlVolume = new VolumeMenu(Lang);
			menuVolume = controlVolume.getMenuVolume();
			
			// Add in listener
			controlVolume.addChangeListener(Interface);
			
			// Enable Interface Based on Support
			controlVolume.setEnabled(Enabled.contains(
				Terminus.PROTOCOL.get("Mode.Volume")));
		}
		
		// Return Instance
		return menuVolume;
	}
	// End of getMenuVolume Method
	
	
	/**
	 * The code separation from the user interface.   This Class handles all
	 * of the complexities of making the interface interactive.
	 * <p>
	 * @author Martin Foster
	 */
	private class Interact implements ActionListener, ChangeListener, UpdateListener {
		
		// Volume level taken from state
		private int VolumeLevel = Constants.getDefaultVolume();
		
		// State of host
		private Map<String,String> State;
		
		// Option windows
		private ManageOptionDialog myOptions;
		private StatisticsAndFunctionsDialog myInformation;
		private PlayerCompactFrame myCompact;
		
		// Help screen
		private JFrame myHelp;
		private HelpManager myViewer;
		
		
		/*
		 * CONSTANTS
		 */
		private static final String CLASS = "org.lyra.Interface.PlayerOrganiserMenu.Interact";
		
		
		/**
		 * Stubs constructor class. 
		 */
		public Interact() {}
		
		
		/**
		 * Will establish the state of certain elements based on the information
		 * gathered from the host.   These settings can only be stored there,
		 * which will result in unexpected changes if another client adjusts
		 * their settings differently.
		 */
		public void setState() {
			
			// Fetch the information state
			State = Info.getState();
			
			// Update all Elements
			stateOptions();
			stateMixer();
			statePlayer();
		}
		// End of setState Method
		
		
		/**
		 * Updates elements associated with host options updates.
		 */
		private void stateOptions() {
			
			/*
			 * We establish the state of all check box elements from this point
			 * forward.   The state extracted will contain the states from the
			 * daemon, all we will need to do is update to match.
			 */
			
			// Consumption of songs
			if (State.containsKey("consume"))
				getCheckOptionsConsume().setSelected(
					Status.fetchState(State.get("consume")));
			
			// Random Play
			if (State.containsKey("random"))
				getCheckOptionsRandom().setSelected(
					Status.fetchState(State.get("random")));
			
			// Repeat Song / Playlist
			if (State.containsKey("repeat"))
				getCheckOptionsRepeat().setSelected(
					Status.fetchState(State.get("repeat")));
			
			// Single Play
			if (State.containsKey("single"))
				getCheckOptionsSingle().setSelected(
					Status.fetchState(State.get("single")));
			
			/*
			 * Crossfading and Volume control both work on the same basis.
			 * Which is assigning an Integer value to the value and extract
			 * it when necessary.
			 */

			// Crossfade adjustment
			if (State.containsKey("xfade"))
				controlCrossfade.setState(Integer.parseInt(State.get("xfade")));
		}
		// End of stateOptions Method
		
		
		/**
		 * Adjusts entries associated mixer elements.
		 */
		private void stateMixer() {
		
			// Safety Mechanism
			if (!State.containsKey("volume"))
				return;
			
			/*
			 * Muting of volume for some odd reason is not an automatic 
			 * feature on the host.  So we implement it manually by keeping
			 * track of the elements.
			 */
			
			// Initialise as true
			Boolean flag = true;
			
			// Capture current volume level
			if (!State.get("volume").equals("0")) {

				// Capture existing level
				VolumeLevel = Integer.parseInt(State.get("volume"));
	
				// We are above zero are not zero
				flag = false;
			}
			
			// Adjust state of mute
			getCheckOptionsMute().setSelected(flag);
			
			
			/*
			 * Crossfading and Volume control both work on the same basis.
			 * Which is assigning an Integer value to the value and extract
			 * it when necessary.
			 */
			
			// Volume adjustment
			controlVolume.setState(
					Integer.parseInt(State.get("volume")));
		}
		// End of stateOptions Method
		
		
		/**
		 * Adjusts the state for elements related to Player events.
		 */
		private void statePlayer() {
			
			/*
			 * We make use of the same quick action key and menu item for
			 * both Pause and Play.  They vary based on the state of the
			 * player.
			 */
			
			// Determine Text
			String text = (State.get("state").equals(Terminus.STATE_PLAY))
				? Lang.get("Organizer.Menu.Playback.Pause")
				: Lang.get("Organizer.Menu.Playback.Play");
			
			// 	Adjust Text
			getItemPlaybackPlay().setText(text);
		}
		// End of statePlayer Method
		
		
		/**
		 * Handles the popup and display of help documents.  
		 * <p>
		 * @param title String title for the window display.
		 * @param filename String filename containing PDF to open.
		 */
		private void popupHelp(String title, String filename) {
			
			/*
			 * If the viewer instance has never been created then we will
			 * create the base using the information we have above.
			 */
			if (myViewer == null)
				myViewer = new HelpManager(
					Lang, 
					title, 
					tempHelp(filename)
				  );
			
			
			/*
			 * We can create a new instance of the frame if necessary.  Since
			 * it did not exist prior we can assume that it will take current
			 * values directly.
			 */
			if (myHelp == null) {
				
				// Setup frame
				myHelp = new JFrame();
				myHelp.getContentPane().add(myViewer.getViewerPanel());

				// Adjust title
				myHelp.setTitle(title);
				
				myHelp.setIconImage(new ImageIcon(
						getClass().getResource(
								Lyra.IMG_SOURCE + Lyra.LYRA)).getImage());
				
				// Show the component
				myHelp.pack();
				myHelp.setDefaultCloseOperation(
						WindowConstants.HIDE_ON_CLOSE);
			}

			/*
			 * There has been a change in the filenames associated with the
			 * viewer.  Time to replace values.
			 */
			if (!title.equals(myViewer.getName())) {
				
				// Replace manager
				myViewer = new HelpManager(
						Lang, 
						title, 
						tempHelp(filename)
					  );
				
				// Adjust Contents
				myHelp.getContentPane().removeAll();
				myHelp.getContentPane().add(myViewer.getViewerPanel());

				// Adjust title
				myHelp.setTitle(title);
			}
			
			// Display element
			myHelp.setVisible(true);
		}
		// End of popupHelp Method
		
		
		/**
		 * Creates a temporary copy of the file for reading by the PDF viewer.
		 * <p>
		 * @param filename String containing filename to resource.
		 * @return String containing path to temporary file.
		 */
		private String tempHelp(String filename) {

			// File Operations
			Operations ops = new Operations(Logger);
			
			// Filename
			File rootDoc = new File(filename);
			
			// Help file located in JAR file
			try {
				
				// Create a temporary file
				rootDoc = File.createTempFile("lyra-docs-", ".pdf");
				

				// Copy file to usable location
				ops.jarCopy(
					filename,
					rootDoc
				 );
				
				// Record for later removal
				Lyra.addTempFile(rootDoc);
			} 
			
			// This should rarely happen
			catch (IOException e2) {
				
				// Failure to create a file
				Logger.Severe(CLASS, "popupHelp", e2);
			}
				
			// Return Instance
			return rootDoc.getPath();
		}
		// End of tempHelp Method
		
		
		/* (non-Javadoc)
		 * @see org.lyra.Host.UpdateListener#hostUpdated(org.lyra.Host.UpdateEvent)
		 */
		public void hostUpdated(UpdateEvent update) {
			
			// Fetch the information state
			State = Info.getState();
			
			/*
			 * Since this update can come from another thread we have no choice
			 * but to ensure that the update happens within the Swing thread.
			 * 
			 * Adjusts to separate the update tasks into individual sections
			 * in order to reduce UI updates at any given time.
			 */
			
			// Options Updates
			if (update.isOptions())
				SwingUtilities.invokeLater(new Runnable() {
					
					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						stateOptions();
					}
				});
			
			// Player Updates
			if (update.isPlayer())
				SwingUtilities.invokeLater(new Runnable() {
					
					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						statePlayer();
					}
				});
			
			// Mixer Updates
			if (update.isMixer())
				SwingUtilities.invokeLater(new Runnable() {
					
					/* (non-Javadoc)
					 * @see java.lang.Runnable#run()
					 */
					public void run() {
						stateMixer();
					}
				});
		}
		// End of hostUpdated Method


		/* (non-Javadoc)
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent evt) {
			
			/*
			 * JCheckBoxMenuItem are checked for explicitly since it is 
			 * required to extract state information from the instances.
			 */
			if (evt.getSource() instanceof JCheckBoxMenuItem) {
				
				// Extract Element
				JCheckBoxMenuItem source = 
					(JCheckBoxMenuItem)evt.getSource();
				
				// Extracted name
				String ident = source.getName();
				
				/*
				 * Log the triggering of a state change.  This will also be
				 * logged once the host sends notification.
				 */
				Logger.Info(
					CLASS,
					"actionPerformed",
					Lang.get("Menu.Log.State")
						.replaceAll("MCHOICE",ident)
				  );
				
				/*
				 * Straight forward operations which take their values directly
				 * from the elements that have been set by the user.
				 */
				
				// Song Consumption
				if (ident.equals(getCheckOptionsConsume().getName()))
					Modes.setConsume(source.isSelected());
				
				// Random Play
				else if (ident.equals(getCheckOptionsRandom().getName()))
					Modes.setRandom(source.isSelected());
				
				// Repeat Play
				else if (ident.equals(getCheckOptionsRepeat().getName()))
					Modes.setRepeat(source.isSelected());
				
				// Single Song Selection
				else if (ident.equals(getCheckOptionsSingle().getName()))
					Modes.setSingle(source.isSelected());
				
				
				/*
				 * We establish the visibility of the tool bar based on the
				 * toggling of this checkbox.  Changing the feature results
				 * in a change in visibility.
				 */
				else if (ident.equals(getCheckMainSearch().getName()))
					Search.setVisible(getCheckMainSearch().isSelected());
				
				/*
				 * The mute operation is a more complex operation.   It
				 * requires the knowledge of a volume level prior to its
				 * use in order to determine how it should be set to when
				 * volume is restored.
				 */
				else if (ident.equals(getCheckOptionsMute().getName())) {
					
					// What should it be
					int volume = (source.isSelected())
						? 0
						: VolumeLevel;
					
					// Set volume
					Modes.setVolume(volume);
				}
			}
			
			/*
			 * The rest of the items should all be JMenuItems as there are no
			 * other types of elements this listener should be subscribed to.
			 */
			else if (evt.getSource() instanceof JMenuItem) {
				
				// Extract Element
				String ident = ((JMenuItem)evt.getSource()).getName();
				
				
				/*
				 * Actions are more straight forward.  We are essentially
				 * looking for a triggering of a menu item.
				 */
				Logger.Info(
					CLASS,
					"actionPerformed",
					Lang.get("Menu.Log.Action")
						.replaceAll("MCHOICE",ident)
				  );
				
				
				/*
				 * MAIN STRUCTURE
				 * 
				 * Main structure carry elements such as exiting the
				 * application and switching hosts.
				 */
				
				// Close the Application
				if (ident.equals(getItemMainQuit().getName()))
					Lyra.closeApplication(Parent);
				
				// Database update
				else if (ident.equals(getItemMainScan().getName()))
					Modes.fireUpdate();
				
				// Statistics Listing
				else if (ident.equals(getItemMainInformation().getName())) {
					
					// Create new Instance if necessary
					if (myInformation == null)
						myInformation = new StatisticsAndFunctionsDialog(
								Info, 
								Lang, 
								Parent
							);
					
					// Set to visible
					myInformation.setVisible(true);
				}
				
				// Compact View
				else if (ident.equals(getItemMainCompact().getName())) {
					
					// Check for Existence
					if (myCompact == null) {
						
						// Create new Instance
						myCompact = new PlayerCompactFrame(
								Info, 
								Control, 
								Modes, 
								Watcher, 
								Clock, 
								Lang, 
								Global,
								Parent
							);
						
						// Pass on instance
						LyraTrayIcon.setCompact(myCompact);
					}
					
					// Set to visible
					myCompact.setVisible(true);
					
					// Hide the main organiser
					Parent.setVisible(false);
				}
				
				
				/*
				 * OPTIONS
				 * 
				 * Most of the options are included in the previous section
				 * due to the stateful nature of their choice.
				 */
				
				// Managing of options
				else if (ident.equals(getItemOptionsPreferences().getName())) {
					
					// Check for Existence
					if (myOptions == null) {
						
						// Create Instance
						myOptions = new ManageOptionDialog(
								Logger, 
								Manager, 
								Info, 
								Modes,
								Lang, 
								Global, 
								Parent
							);
					}
					
					// Make Visible
					myOptions.setVisible(true);
				}
				
				
				/*
				 * PLAYBACK
				 * 
				 * Playback mostly exists to provide a shortcut for common
				 * playback features such as playing Next, Previous songs as
				 * well as Starting and Halting playback.
				 */
				
				// Move to next song
				else if (ident.equals(getItemPlaybackNext().getName()))
					Control.playNext();
				
				// Start Playback or Pause Depending on State
				else if (ident.equals(getItemPlaybackPlay().getName())) {
					
					/*
					 * State is a large factor when we get to this point.
					 * Since the same button is responsible for pause and play
					 * it becomes necessary to be aware of where we are at 
					 * prior to making the change.
					 */
					
					// We pause the playback
					if (State.get("state").equals(Terminus.STATE_PLAY))
						Control.playPause();
					
					// Resume playback
					else if (State.get("state").equals(Terminus.STATE_PAUSE))
						Control.playResume();
					
					// Start playback from the first element of the list
					else if (State.get("state").equals(Terminus.STATE_STOP))
						Control.playStart();
				}
				
				// Move to previous song
				else if (ident.equals(getItemPlaybackPrevious().getName()))
					Control.playPrevious();
				
				// Stop the playback
				else if (ident.equals(getItemPlaybackStop().getName()))
					Control.playStop();
				
				// Clear the current playlist of all options
				else if (ident.equals(getItemPlaybackClear().getName()))
					Lists.clearCurrent();
				
				// Shuffle the current playlist
				else if (ident.equals(getItemPlaybackShuffle().getName()))
					Lists.shuffleCurrent();
				
				
				/*
				 * HELP SECTION
				 * 
				 * Help section says it all.   Will load up a basic PDF
				 * viewer into a separate window for display.
				 */
				
				// About the Program
				else if (ident.equals(getItemHelpAbout().getName()))
					popupHelp(
						Lang.get("Help.Window.About"), 
						Lyra.getDocsByMap("Help-About")
					  );
 				
				// Frequently Asked Questions
				else if (ident.equals(getItemHelpFrequently().getName()))
					popupHelp(
						Lang.get("Help.Window.Frequently"), 
						Lyra.getDocsByMap("Help-Frequently")
					  );
					
				// General Help
				else if (ident.equals(getItemHelpDisplay().getName()))
					popupHelp(
						Lang.get("Help.Window.Help"), 
						Lyra.getDocsByMap("Help-Display")
					  );
			}
		}

		
		/* (non-Javadoc)
		 * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
		 */
		public void stateChanged(ChangeEvent evt) {
			
			// Generic Level for Compare
			if (evt.getSource() instanceof JComponent) {
				
				// Extract Identifier
				String ident = ((JComponent)evt.getSource()).getName();
				
				// Volume Control
				if (ident.equals(controlVolume.getName())) {
						
					// We only want a settled value
					if (!controlVolume.getValueIsAdjusting())
						Modes.setVolume(controlVolume.getState());
				}
				
				// Crossfade
				else if (ident.equals(controlCrossfade.getName())) {
					
					// We only want a settled value
					if (!controlCrossfade.getValueIsAdjusting())
						Modes.setCrossfade(controlCrossfade.getState());
				}
			}
		}
		// End of stateChanged Method
		
	}
	// End of Interact Class
	
}
