/**
 * 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.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;

import javax.swing.JDialog;
import javax.swing.BorderFactory;
import javax.swing.border.TitledBorder;
import java.awt.Font;
import java.awt.Color;
import javax.swing.JScrollPane;
import java.awt.FlowLayout;
import javax.swing.JList;
import java.awt.GridLayout;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;

import org.lyra.Lyra;
import org.lyra.Host.Status;
import org.lyra.Listener.WindowSizeListener;
import org.lyra.Widgets.ResizeIcon;

/**
 * A rather simplistic interface to the Statistical and Functions capability
 * of our connection to the host.  No interactive capability, per sey, but will
 * provide one method of gaining knowledge of the host for debugging purposes.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 147%, %LastChangedDate, 06/11/11 4:57PM%
 */
public class StatisticsAndFunctionsDialog extends JDialog {

	// Serial Requirements
	private static final long serialVersionUID = 1L;

	/*
	 * VISUAL ELEMENTS
	 */
	private JPanel jContentPane = null;

	// Base Information
	private JPanel panelStatistics = null;
	private JLabel labelArtists = null;
	private JTextField fieldArtists = null;
	private JLabel labelAlbums = null;
	private JTextField fieldAlbums = null;
	private JLabel labelSongs = null;
	private JTextField fieldSongs = null;
	private JLabel labelUptime = null;
	private JTextField fieldUptime = null;
	private JLabel labelPlaytime = null;
	private JTextField fieldPlaytime = null;
	private JLabel labelDbPlaytime = null;
	private JTextField fieldDbPlaytime = null;
	private JLabel labelDbUpdate = null;
	private JTextField fieldDbUpdate = null;

	// Enabled Host Commands
	private JPanel panelEnabled = null;
	private JScrollPane scrollEnabled = null;
	private JList listEnabled = null;

	// Disabled Host Commands
	private JPanel panelDisabled = null;
	private JScrollPane scrollDisabled = null;
	private JList listDisabled = null;

	// Window Management
	private JPanel panelStatus = null;
	private JLabel iconResize = null;
	

	/*
	 * PACKAGE ELEMENTS
	 */
	private Status State;
	private Map<String,String> Lang;
	
	/*
	 * CLASS INSTANCES
	 */
	private Interact Interface;
	
	
	/**
	 * Base constructor for visual editors.   Has the necessary components
	 * to display information properly, however no dynamic content.
	 * <p>
	 * @param owner Owner window to block.
	 */
	@SuppressWarnings("serial")
	public StatisticsAndFunctionsDialog(Window owner) {
		
		// Initialise super constructor
		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>(){
			{
				// Base Language Elements
				put("Statistics.Window.Title","Statistics and Functions");
				put("Statistics.Info.Artists","Artists");
				put("Statistics.Info.Albums","Albums");
				put("Statistics.Info.Songs","Songs");
				put("Statistics.Info.Uptime","Uptime");
				put("Statistics.Info.Playtime","Playtime");
				put("Statistics.Info.DbPlaytime","Database Playtime");
				put("Statistics.Info.DbUpdate","Database Update");
				put("Statistics.Section.Statistics","Host Statistics");
				put("Statistics.Section.Enabled","Enabled Commands");
				put("Statistics.Section.Disabled","Disabled Commands");
			}
		};
		
		// Insertion of elements
		initDialog(owner);
	}
	// End of Constructor Class

	
	/**
	 * Main constructor for the class which will establish base elements based
	 * on live information instead of static information.
	 * <p>
	 * @param state Status handle which holds host information.
	 * @param lang Language map for independence.
	 * @param owner Window instance to block.
	 */
	public StatisticsAndFunctionsDialog(Status state, Map<String,String> lang, Window owner) {
		
		// Have super class do its business
		super(owner);
		
		// Pass Up
		this.State = state;
		this.Lang  = lang;
		
		// Create Instance
		Interface = new Interact();
		
		// Establish Elements
		initDialog(owner);
	}
	// End of Constructor Method
	
	
	/**
	 * This method initialises this.  Establishes the base norms for the
	 * window element and display.
	 * <p>
	 * @param owner Window to block and set position relative to.
	 */
	private void initDialog(Window owner) {
		
		// Block behaviour
		this.setModal(true);
		
		// Establish Language Elements
		this.setTitle("Statistics and Functions");

		// Adjust Icon
		this.setIconImage(new ImageIcon(
				getClass().getResource(
						Lyra.IMG_SOURCE + Lyra.LYRA)).getImage());
		
		// Addition of Interface
		this.setContentPane(getJContentPane());
		
		// Adjust Size
		this.pack();
	
		/*
		// Safety
		if (this.getSize().getWidth() < 500)
			this.setSize(
				500,
				(int) this.getSize().getHeight()
			  );
		*/
		// Prevent going to small
		this.setMinimumSize(this.getSize());

		// Position Accordingly
		this.setLocationRelativeTo(owner);
	}
	// End of initDialog Method

	
	/**
	 * This method initialises a JPanel component providing row elements. 
	 * <p>
	 * @param label JLabel instance which will be resized.
	 * @param widget JComponent instance which will be added as-is.
	 * @return JPanel containing the elements properly established.
	 */
	private JPanel getPanelRow(JLabel label, JComponent widget) {
		
		/*
		 * In order to create a consistent look and feel with our use of 
		 * BoxLayout we will resize all the elements from this point.  However
		 * we maintain Height as to adjust if there are font changes.
		 */
		label.setPreferredSize(
			new Dimension(
				Constants.getLabelWidth(),
				label.getPreferredSize().height)
		  );
		
		/*
		 * Here we adjust the maximum values to be suitable for our needs.  
		 * Essentially we are not looking for text fields to grow exponentially
		 * when a user resizes itself.
		 */
		widget.setMaximumSize(
			new Dimension(
				widget.getMaximumSize().width,
				widget.getPreferredSize().height + Constants.getPaddingHeight())
		  );
		
		/*
		 * We create the element at this point.  We essentially throw all
		 * of the instances into a newly created section and call it the
		 * end of a day.
		 */
		
		// Create instance
		JPanel row = new JPanel();

		// Establish layout
		row.setLayout(new BoxLayout(row, BoxLayout.X_AXIS));
		
		// Insertion of the label and spacer
		row.add(label);
		row.add(Box.createHorizontalStrut(Constants.getLabelSpacer()));
		
		// Insertion of the widget and end spacer
		row.add(widget);
		row.add(Box.createHorizontalGlue());
		
		// Pass on row
		return row;
	}
	// End of getPanelRow Method
	
	
	/**
	 * This method initialises jContentPane.  Main interface component.
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {

		// Check for Existence
		if (jContentPane == null) {
			
			// Create Instance
			jContentPane = new JPanel();
			
			// Establish Layout
			jContentPane.setLayout(new BorderLayout());
			
			// Insert Elements

			jContentPane.add(getPanelStatus(), BorderLayout.SOUTH);
			
			// Determine validity
			if (getListDisabled().getModel().getSize() < 1) {

				// We choose a side by side look and feel
				jContentPane.add(getPanelStatistics(), BorderLayout.CENTER);

				// Always display Enabled
				jContentPane.add(getPanelEnabled(), BorderLayout.EAST);
			}
			
			// We choose a stacked look and feel
			else {

				// We choose a side by side look and feel
				jContentPane.add(getPanelStatistics(), BorderLayout.NORTH);

				// Establish Both Elements
				jContentPane.add(getPanelEnabled(), BorderLayout.WEST);
				jContentPane.add(getPanelDisabled(), BorderLayout.EAST);
			}
		}
		
		// Return Instance
		return jContentPane;
	}
	// End of getJContentPane Method

	
	/**
	 * This method initialises panelStatistics.  Main holder in a key/pair
	 * value.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelStatistics() {
		if (panelStatistics == null) {

			// Establish Layout details
			GridLayout gridLayout = new GridLayout();
			gridLayout.setRows(7);
			gridLayout.setVgap(Constants.getRowGap());

			// Create Instance
			panelStatistics = new JPanel();
			
			// Setup Layout and Border
			panelStatistics.setLayout(gridLayout);
			panelStatistics.setBorder(
				BorderFactory.createTitledBorder(
					null, 
					Lang.get("Statistics.Section.Statistics"), 
					TitledBorder.DEFAULT_JUSTIFICATION, 
					TitledBorder.DEFAULT_POSITION, 
					new Font("Dialog", Font.BOLD, 12), 
					new Color(51, 51, 51)
				 )
			  );
			
			// Insert Elements
			panelStatistics.add(
				getPanelRow(
					getLabelArtists(),
					getFieldArtists())
			  );
			
			// Albums
			panelStatistics.add(
				getPanelRow(
					getLabelAlbums(),
					getFieldAlbums())
		 	  );
				
			// Songs
			panelStatistics.add(
				getPanelRow(
					getLabelSongs(),
					getFieldSongs())
			  );
			
			// Uptime
			panelStatistics.add(
				getPanelRow(
					getLabelUptime(),
					getFieldUptime())
			  );
			
			// Total play time
			panelStatistics.add(
				getPanelRow(
					getLabelPlaytime(),
					getFieldPlaytime())
			  );

			// Database Play Time
			panelStatistics.add(
				getPanelRow(
					getLabelDbPlaytime(),
					getFieldDbPlaytime())
			  );
			
			// Database Last Update
			panelStatistics.add(
				getPanelRow(
					getLabelDbUpdate(), 
					getFieldDbUpdate())
			  );
		}
		
		// Return Instance
		return panelStatistics;
	}
	// End of getPanelStatistics Methods
	

	/**
	 * This method initialises panelEnabled.	
	 * <p>	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelEnabled() {
		
		// Check for Existence
		if (panelEnabled == null) {
			
			// New Instance
			panelEnabled = new JPanel();
			
			// Layout and Border
			panelEnabled.setLayout(new FlowLayout());
			panelEnabled.setBorder(
				BorderFactory.createTitledBorder(
					null, 
					Lang.get("Statistics.Section.Enabled"), 
					TitledBorder.DEFAULT_JUSTIFICATION, 
					TitledBorder.DEFAULT_POSITION, 
					new Font("Dialog", Font.BOLD, 12), 
					new Color(51, 51, 51)
				  )
			  );

			// Insert Elements
			panelEnabled.add(getScrollEnabled(), null);
		}
		
		// Return Instance
		return panelEnabled;
	}
	// End of getPanelEnabled Method
	

	/**
	 * This method initialises panelDisabled.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelDisabled() {
		
		// Check for Existence
		if (panelDisabled == null) {
			
			// Create Instance
			panelDisabled = new JPanel();
			
			// Layout and Border
			panelDisabled.setLayout(new FlowLayout());
			panelDisabled.setBorder(
				BorderFactory.createTitledBorder(
					null, 
					Lang.get("Statistics.Section.Disabled"), 
					TitledBorder.DEFAULT_JUSTIFICATION, 
					TitledBorder.DEFAULT_POSITION, 
					new Font("Dialog", Font.BOLD, 12), 
					new Color(51, 51, 51)
				  )
			  );

			// Insert Elements
			panelDisabled.add(getScrollDisabled(), null);
		}
		
		// Return Instance
		return panelDisabled;
	}
	// End of getPanelDisabled Method

	
	/**
	 * This method initialises panelWindow.	
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelStatus() {
		
		// Check for 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 Method

	
	/**
	 * 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
	
	
	/**
	 * This method initialises scrollEnabled	
	 * <p>
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollEnabled() {
		
		// Check for Existence
		if (scrollEnabled == null) {
			
			// Create Instance
			scrollEnabled = new JScrollPane();
			
			// Always enabled vertical bar
			scrollEnabled.setVerticalScrollBarPolicy(
					JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

			// Insert Elements
			scrollEnabled.setViewportView(getListEnabled());
		}
		
		// Return Instance
		return scrollEnabled;
	}
	// End of getScrollEnabled Method

	
	/**
	 * This method initialises scrollDisabled.	
	 * <p>
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getScrollDisabled() {
		
		// Check for Existence
		if (scrollDisabled == null) {
			
			// Create Instance
			scrollDisabled = new JScrollPane();
			
			// Always enable vertical bar
			scrollDisabled.setVerticalScrollBarPolicy(
					JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);

			// Insert Contents
			scrollDisabled.setViewportView(getListDisabled());
		}
		
		// Return Instance
		return scrollDisabled;
	}
	// End of getScrollDisabled Method

	/**
	 * This method initialises listEnabled.
	 * <p>
	 * @return javax.swing.JList	
	 */
	private JList getListEnabled() {
		
		// Check for Existence
		if (listEnabled == null) {
			
			// Create Base List
			if (State == null) {
				
				// Blank slate
				listEnabled = new JList();				
			}
			
			// Create Populated List
			else {
				
				// Populated List
				listEnabled = new JList(
						State.getEnabled().toArray(new String[0]));
			}
		}
		
		// Return Instance
		return listEnabled;
	}
	// End of getListEnabled Method

	
	/**
	 * This method initialises listDisabled.
	 * <p>
	 * @return javax.swing.JList	
	 */
	private JList getListDisabled() {
		
		// Check for Existence
		if (listDisabled == null) {
			
			// Create Base List
			if (State == null) {
				
				// Blank slate
				listDisabled = new JList();				
			}
			
			// Create Populated List
			else {
				
				// Populated List
				listDisabled = new JList(
						State.getDisabled().toArray(new String[0]));
			}

		}
		
		// Return Instance
		return listDisabled;
	}
	// End of getListDisabled Method

	
	/**
	 * This method initialises fieldArtists.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldArtists() {
		
		// Check for Existence
		if (fieldArtists == null) {
			
			// Create Instance
			fieldArtists = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldArtists.setEditable(false);
			fieldArtists.setOpaque(false);
			fieldArtists.setBorder(
					BorderFactory.createEmptyBorder());
			fieldArtists.setBackground(new Color(0,0,0,0));
		}
		
		// Return Instance
		return fieldArtists;
	}
	// End of getFieldArtists Method

	
	/**
	 * This method initialises fieldAlbums.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldAlbums() {
		
		// Check for Existence
		if (fieldAlbums == null) {
			
			// Create Instance
			fieldAlbums = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldAlbums.setEditable(false);
			fieldAlbums.setOpaque(false);
			fieldAlbums.setBorder(
					BorderFactory.createEmptyBorder());
			fieldAlbums.setBackground(new Color(0,0,0,0));
		}
		
		// Return Instance
		return fieldAlbums;
	}
	// End of getFieldAlbums Method

	
	/**
	 * This method initialises fieldSongs.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldSongs() {
		
		// Check for Existence
		if (fieldSongs == null) {
			
			// Create Instance
			fieldSongs = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldSongs.setEditable(false);
			fieldSongs.setOpaque(false);
			fieldSongs.setBorder(
					BorderFactory.createEmptyBorder());
			fieldSongs.setBackground(new Color(0,0,0,0));
		}
		
		// Return Instance
		return fieldSongs;
	}
	// End of getFieldSongs Method

	
	/**
	 * This method initialises fieldUptime.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldUptime() {
		
		// Check for Existence
		if (fieldUptime == null) {
			
			// Create Instance
			fieldUptime = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldUptime.setEditable(false);
			fieldUptime.setOpaque(false);
			fieldUptime.setBorder(
					BorderFactory.createEmptyBorder());
			fieldUptime.setBackground(new Color(0,0,0,0));
			
			// Never yield
			fieldUptime.setMinimumSize(
					fieldUptime.getPreferredSize());
		}
		
		// Return Instance
		return fieldUptime;
	}
	// End of getFieldUptime Method

	
	/**
	 * This method initialises fieldPlaytime.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldPlaytime() {
		
		// Check for Existence
		if (fieldPlaytime == null) {
			
			// Create Instance
			fieldPlaytime = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldPlaytime.setEditable(false);
			fieldPlaytime.setOpaque(false);
			fieldPlaytime.setBorder(
					BorderFactory.createEmptyBorder());
			fieldPlaytime.setBackground(new Color(0,0,0,0));
			
			// Never yield
			fieldPlaytime.setMinimumSize(
					fieldPlaytime.getPreferredSize());
		}
		
		// Return Instance
		return fieldPlaytime;
	}
	// End of getFieldPlaytime Method

	
	/**
	 * This method initialises fieldDbPlaytime.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldDbPlaytime() {
		
		// Check for Existence
		if (fieldDbPlaytime == null) {
			
			// Create Instance
			fieldDbPlaytime = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldDbPlaytime.setEditable(false);
			fieldDbPlaytime.setOpaque(false);
			fieldDbPlaytime.setBorder(
					BorderFactory.createEmptyBorder());
			fieldDbPlaytime.setBackground(new Color(0,0,0,0));
			
			// Never yield
			fieldDbPlaytime.setMinimumSize(
					fieldDbPlaytime.getPreferredSize());
		}
		
		// Return Instance
		return fieldDbPlaytime;
	}
	// End of getFieldDbPlaytime Method

	
	/**
	 * This method initialises fieldDbUpdate.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldDbUpdate() {
		
		// Check for Existence
		if (fieldDbUpdate == null) {
			
			// Create Instance
			fieldDbUpdate = new JTextField(
					Interface.fetchColumns());
			
			// Force element to behave like a JLabel
			fieldDbUpdate.setEditable(false);
			fieldDbUpdate.setOpaque(false);
			fieldDbUpdate.setBorder(
					BorderFactory.createEmptyBorder());
			fieldDbUpdate.setBackground(new Color(0,0,0,0));
		}
		
		// Return Instance
		return fieldDbUpdate;
	}
	// End of getFieldDbUpdate Method
	
	
	/**
	 * This method initialises labelArtists.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelArtists() {
		
		// Check for Existence
		if (labelArtists == null) {
			
			// Create Instance
			labelArtists = new JLabel();
			
			// Establish Language
			labelArtists.setText(Lang.get("Statistics.Info.Artists"));
		}
		
		// Return Instance
		return labelArtists;
	}
	// End of getLabelArtists Method
	
	
	/**
	 * This method initialises labelAlbums.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelAlbums() {
		
		// Check for Existence
		if (labelAlbums == null) {
			
			// Create Instance
			labelAlbums = new JLabel();
			
			// Establish Language
			labelAlbums.setText(Lang.get("Statistics.Info.Albums"));
		}
		
		// Return Instance
		return labelAlbums;
	}
	// End of getLabelAlbums Method
	
	
	/**
	 * This method initialises labelSongs.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelSongs() {
		
		// Check for Existence
		if (labelSongs == null) {
			
			// Create Instance
			labelSongs = new JLabel();
			
			// Establish Language
			labelSongs.setText(Lang.get("Statistics.Info.Songs"));
		}
		
		// Return Instance
		return labelSongs;
	}
	// End of getLabelSongs Method
	
	
	/**
	 * This method initialises labelUptime.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelUptime() {
		
		// Check for Existence
		if (labelUptime == null) {
			
			// Create Instance
			labelUptime = new JLabel();
			
			// Establish Language
			labelUptime.setText(Lang.get("Statistics.Info.Uptime"));
		}
		
		// Return Instance
		return labelUptime;
	}
	// End of getLabelUptime Method
	
	
	/**
	 * This method initialises labelPlaytime.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelPlaytime() {
		
		// Check for Existence
		if (labelPlaytime == null) {
			
			// Create Instance
			labelPlaytime = new JLabel();
			
			// Establish Language
			labelPlaytime.setText(Lang.get("Statistics.Info.Playtime"));
		}
		
		// Return Instance
		return labelPlaytime;
	}
	// End of getLabelPlaytime Method
	
	
	/**
	 * This method initialises labelDbPlaytime.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelDbPlaytime() {
		
		// Check for Existence
		if (labelDbPlaytime == null) {
			
			// Create Instance
			labelDbPlaytime = new JLabel();
			
			// Establish Language
			labelDbPlaytime.setText(Lang.get("Statistics.Info.DbPlaytime"));
		}
		
		// Return Instance
		return labelDbPlaytime;
	}
	// End of getLabelDbPlaytime Method
	
	
	/**
	 * This method initialises labelDbUpdate.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelDbUpdate() {
		
		// Check for Existence
		if (labelDbUpdate == null) {
			
			// Create Instance
			labelDbUpdate = new JLabel();
			
			// Establish Language
			labelDbUpdate.setText(Lang.get("Statistics.Info.DbUpdate"));
		}
		
		// Return Instance
		return labelDbUpdate;
	}
	// End of getLabelDbUpdate Method
	
	
	/* (non-Javadoc)
	 * @see java.awt.Dialog#setVisible(boolean)
	 */
	public void setVisible(boolean state) {
		
		/*
		 * We only update the state information in the interface if the 
		 * window is going to be visible.   If it will not be visible then there
		 * is no need to causing a user interface change.
		 */
		if (state)
			Interface.setState();
		
		// Have super class do its business
		super.setVisible(state);
	}
	// End of setVisible Method
	
	
	/**
	 * Provides a separation of code and interface.  This specific instance
	 * contains elements necessary to establish the state.
	 * <p>
	 * @author Martin Foster
	 */
	private class Interact {
		
		// Number Formatting
		private NumberFormat nFormat = NumberFormat.getInstance();
		
		// Column integer
		private Integer nColumns = 0;
		
		/**
		 * Stub Constructor.
		 */
		public Interact() {};
		
		
		/**
		 * Format a string based on a large number that needs to be split
		 * into the individual components.
		 * <p>
		 * @param time String containing elapsed time.
		 * @return String formatted with elapsed time.
		 */
		private String formatElapsed(String time) {
						
			// Quick number crunching
			long total  = Long.parseLong(time);
			long idays  = TimeUnit.SECONDS.toDays(total);
			long ihrs   = TimeUnit.SECONDS.toHours(total);
			long imin   = TimeUnit.SECONDS.toMinutes((long)(total - (ihrs * Math.pow(60, 2))));
			long isec   = TimeUnit.SECONDS.toSeconds((long)(total - (ihrs * Math.pow(60, 2)) - (imin * 60)));
			
			// Initialise passage string
			String pass = (idays > 0)
				? Lang.get("Statistcs.Runtime.Days")
				: Lang.get("Statistics.Runtime.Hours");
			
			// Formatting of string
			pass = pass.replaceAll(
					"MDAYS", nFormat.format(idays));
				
			// Format the hours section
			String shrs = nFormat.format(ihrs - (idays * 24));
			pass = pass.replaceAll("MHRS", shrs);
			
			// Format the minutes appending 0 if necessary
			String smin = (imin < 10) 
			  ? "0" + Long.toString(imin)  
			  : Long.toString(imin);
			pass = pass.replaceAll("MMIN", smin);
			
			// Format the seconds appending 0 if necessary
			String ssec = (isec < 10) 
			  ? "0" + Long.toString(isec)  
			  : Long.toString(isec);
			pass = pass.replaceAll("MSEC", ssec);
			
			// Return formatted string
			return pass;
		}
		// Return formatElapsed Method
		

		/**
		 * Will take an Epoch date and transform it into a human readable
		 * format.
		 * <p>
		 * @param epoch String containing the epoch time in seconds.
		 * @return String formatted for human readable dates.
		 */
		private String formatEpoch(String epoch) {
			
			// Take in the date and format
			String date = new SimpleDateFormat(Lang.get("Statistics.Date.Format"))
				.format(new Date(Long.parseLong(epoch)*1000));
			
			// Return formatted date
			return date;
		}
		// End of formatEpoch Method
		
		
		/**
		 * In order to reserve space for the written elements, we establish
		 * the amount of columns the element will take.
		 * <p>
		 * @return Integer containing the amount of columns to expect.
		 */
		public int fetchColumns() {
			
			// We only complete this operation once
			if (nColumns <= 0)
				nColumns = Lang.get("Statistcs.Runtime.Days").length();

			/*
			 * It is unknown why columns do not match up with the characters
			 * being placed in the JTextField, however it we send out the full
			 * size the window would be abnormally large, so we halve and it
			 * seems to work well.
			 */
			return nColumns/2;
		}
		// End of fetchColumns Method
		
		
		/**
		 * Establishes the base information components used for the dialog
		 * itself.  Elements such as enabled/disabled commands and so forth.
		 */
		public void setState() {

			/*
			 * The first elements in the Statistics section are fed exclusively
			 * from the Statistics map.   Extract the information and convert as
			 * necessary.
			 */
			
			// Extract statistics
			Map<String,String> stats = State.getStatistics();
			
			// Artist Number Display
			getFieldArtists().setText(
				nFormat.format(Integer.parseInt(stats.get("artists")))	
			  );
			
			// Album Number Display
			getFieldAlbums().setText(
				nFormat.format(Integer.parseInt(stats.get("albums")))	
			  );
			
			// Song Number Display
			getFieldSongs().setText(
				nFormat.format(Integer.parseInt(stats.get("songs")))	
			  );
			
			
			/*
			 * Elapsed time components require more processing which is
			 * accomplished using the included function.
			 */
			
			// Uptime of Daemon
			getFieldUptime().setText(
				formatElapsed(stats.get("uptime"))
			  );

			
			// Total play time of Songs
			getFieldPlaytime().setText(
				formatElapsed(stats.get("playtime"))
			  );
			
			// Database play time
			getFieldDbPlaytime().setText(
				formatElapsed(stats.get("db_playtime"))
			  );
			
			
			/*
			 * Lastly we have a date element that is included in Unix time
			 * only.  Due to this we have to first translate it and then 
			 * format it accordingly.
			 */
			
			// Database Last Update Time
			getFieldDbUpdate().setText(
				formatEpoch(stats.get("db_update"))
			  );
		}
		// End of setState method
	}
	// End of Interact Class
	
}  //  @jve:decl-index=0:visual-constraint="10,10"
