/**
 * 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.Options;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import javax.swing.JSlider;
import javax.swing.border.TitledBorder;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.JLabel;
import javax.swing.JCheckBox;

import org.lyra.Host.Status;
import org.lyra.Host.Terminus;
import org.lyra.Interface.Constants;


/**
 * Certain host features such as consume can be modified from this particular
 * panel.  While not necessarily application specific their location is
 * suitable.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 99%, %LastChangedDate, 17/03/11 8:17PM%
 */
public class OptionsPanel extends JPanel {

	// Serial Requirements
	private static final long serialVersionUID = 1L;
	
	// Visual Elements
	private JPanel panelContent = null;
	private JLabel labelConsume = null;
	private JCheckBox checkConsume = null;
	private JLabel labelCrossfade = null;
	private JSlider sliderCrossfade = null;
	private JLabel labelRandom = null;
	private JCheckBox checkRandom = null;
	private JLabel labelRepeat = null;
	private JCheckBox checkRepeat = null;
	private JLabel labelSingle = null;
	private JCheckBox checkSingle = null;

	// Package Elements
	private Status Info;
	private Map<String,String> Lang;
	private Set<String> Enabled;
	
	// Class Instances
	private Interact Interface = new Interact();
	
	
	/**
	 * This is the default constructor for Visual editors.   Establishes
	 * defaults and no other capability.
	 */
	@SuppressWarnings("serial")
	public OptionsPanel() {
		
		// Have super class do its work
		super();
		
		/*
		 * 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>(){
			{
	        	// Label Elements
				put("Options.HostOptions.Crossfade", "Crossfade");
				put("Options.HostOptions.Consume", "Consume");
				put("Options.HostOptions.Random", "Random");
				put("Options.HostOptions.Repeat", "Repeat");
				put("Options.HostOptions.Single", "Single");
				
				// Tool Tip Updates
	        	put("Tooltip.Options.Consume","When consume is activated, each song played is removed from playlist...");
	        	put("Tooltip.Options.Crossfade","Sets crossfading between songs...");
	        	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...");
			}
		};
		
		// Blanked list for successful generation
		Enabled = new TreeSet<String>();
		
		// Establish Baseline
		initFrame();
	}
	// End of Constructor Method

	
	/**
	 * Main constructor class for the class.  This will generate user elements
	 * and permit proper operation due to live values.
	 * <p>
	 * @param lang Language map for independent language handling.
	 * @param info Information class for host state and status.
	 */
	public OptionsPanel(Map<String,String> lang, Status info) {
		
		// Have super class do its work
		super();
		
		// Pass Up
		this.Lang = lang;
		this.Info = info;
		
		// List of enabled features
		this.Enabled = Info.getEnabledAsSet();
		
		// Establish Baseline
		initFrame();
	}
	// End of Constructor Method
	
	/**
	 * This method initialises the baseline elements for the user interface
	 * elements.  Establishes state elements as well.
	 */
	private void initFrame() {
		
		// Establish Layout
		this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
		
		// Insert Panel
		this.add(getPanelContent());
		
		// Insert Spacer
		this.add(Box.createVerticalGlue());
	}
	// End of initFrame Method
	
	
	private JPanel getPanelContent() {
		
		// Check for Existence
		if (panelContent == null) {
			
			// Establish layout elements
			GridLayout gridLayout = new GridLayout();
			gridLayout.setColumns(1);
			gridLayout.setRows(5);
			
			// Gaps
			gridLayout.setVgap(Constants.getRowGap());

			
			// Create Instance
			panelContent = new JPanel();
			
			// Layout and Border
			panelContent.setLayout(gridLayout);
			panelContent.setBorder(BorderFactory.createTitledBorder(null, "Host Playback Options", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, new Font("Dialog", Font.BOLD, 12), new Color(51, 51, 51)));

			/*
			 * We insert the user interface elements at this point.  Since we
			 * are inserting them as individual panels, the grid exists mostly to
			 * create spacing.
			 */
			
			// Consuming
			panelContent.add(
				getPanelRow(
					getLabelConsume(),
					getCheckConsume()
			  ));

			// Crossfading
			panelContent.add(
				getPanelRow(
					getLabelCrossfade(),
					getSliderCrossfade()
			  ));

			// Random Play
			panelContent.add(
				getPanelRow(
					getLabelRandom(),
					getCheckRandom()
			  ));
			
			// Repeat
			panelContent.add(
				getPanelRow(
					getLabelRepeat(),
					getCheckRepeat()
			  ));
			
			// Single Play
			panelContent.add(
				getPanelRow(
					getLabelSingle(),
					getCheckSingle()
			  ));
		}
		
		// Return Instance
		return panelContent;
	}
	
	

	/**
	 * This method initialises checkConsume. Sets consume state to STATE, 
	 * STATE should be 0 or 1. When consume is activated, each song played
	 * is removed from playlist. 	
	 * <p>
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getCheckConsume() {
		
		// Check for Existence
		if (checkConsume == null) {

			// Create Instance
			checkConsume = new JCheckBox();
			
			// Establish Language and Identifier
			checkConsume.setToolTipText(Lang.get("Tooltip.Options.Consume"));
			checkConsume.setName("OPTIONS.CONSUME");
			
			// Establish Enabled State
			checkConsume.setEnabled(Enabled.contains(
					Terminus.PROTOCOL.get("Mode.Consume")));
		}
		
		// Return Instance
		return checkConsume;
	}
	// End of getCheckConsume Method
	
	
	/**
	 * This method initialises labelConsume.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelConsume() {
		
		// Check Existence
		if (labelConsume == null) {
			
			// Create Instance
			labelConsume = new JLabel();
			
			// Establish Language
			labelConsume.setText(Lang.get("Options.HostOptions.Consume"));
		}
		
		// Return Instance
		return labelConsume;
	}
	// End of getLabelConsume Method
	

	/**
	 * This method initialises checkCrossfade. Sets crossfading between songs. 	
	 * <p>
	 * @return javax.swing.JSlider	
	 */
	private JSlider getSliderCrossfade() {
		
		// Check for Existence
		if (sliderCrossfade == null) {
			
			// Create Instance
			sliderCrossfade = new JSlider(0, 15);
			
			// Establish Language and Identifier
			sliderCrossfade.setToolTipText(Lang.get("Tooltip.Options.Crossfade"));
			sliderCrossfade.setName("OPTIONS.CROSSFADE");
			
			// Display text
			//Turn on labels at major tick marks.
			sliderCrossfade.setMajorTickSpacing(5);
			sliderCrossfade.setMinorTickSpacing(1);
			sliderCrossfade.setPaintTicks(true);
			sliderCrossfade.setPaintLabels(true);
			
			// Establish Enabled State
			sliderCrossfade.setEnabled(Enabled.contains(
					Terminus.PROTOCOL.get("Mode.Crossfade")));
		}
		
		// Return Instance
		return sliderCrossfade;
	}
	// End of getSliderCrossfade Method
	
	
	/**
	 * This method initialises labelCrossfade.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelCrossfade() {
		
		// Check Existence
		if (labelCrossfade == null) {
			
			// Create Instance
			labelCrossfade = new JLabel();
			
			// Establish Language
			labelCrossfade.setText(Lang.get("Options.HostOptions.Crossfade"));
		}
		
		// Return Instance
		return labelCrossfade;
	}
	// End of getLabelCrossfade Method
	

	/**
	 * This method initialises checkRandom. Sets random state to STATE, STATE 
	 * should be 0 or 1. 
	 * <p.
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getCheckRandom() {
		
		// Check for Existence
		if (checkRandom == null) {
			
			// Create Instance
			checkRandom = new JCheckBox();
			
			// Establish Language and Identifier
			checkRandom.setToolTipText(Lang.get("Tooltip.Options.Random"));
			checkRandom.setName("OPTIONS.RANDOM");
			
			// Establish Enabled State
			checkRandom.setEnabled(Enabled.contains(
					Terminus.PROTOCOL.get("Mode.Random")));
		}
		
		// Return Instance
		return checkRandom;
	}
	// End of getCheckRandom Method

	
	/**
	 * This method initialises labelRandom.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelRandom() {
		
		// Check Existence
		if (labelRandom == null) {
			
			// Create Instance
			labelRandom = new JLabel();
			
			// Establish Language
			labelRandom.setText(Lang.get("Options.HostOptions.Random"));
		}
		
		// Return Instance
		return labelRandom;
	}
	// End of getLabelRandom Method
	

	/**
	 * This method initialises checkRepeat. Sets repeat state to STATE, STATE 
	 * should be 0 or 1. 	
	 * <p>
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getCheckRepeat() {
		
		// Check for Existence
		if (checkRepeat == null) {
			
			// Create Instance
			checkRepeat = new JCheckBox();
			
			// Establish Language and Identifier
			checkRepeat.setToolTipText(Lang.get("Tooltip.Options.Repeat"));
			checkRepeat.setName("OPTIONS.REPEAT");
			
			// Establish Enabled State
			checkRepeat.setEnabled(Enabled.contains(
					Terminus.PROTOCOL.get("Mode.Repeat")));
		}
		
		// Return Instance
		return checkRepeat;
	}
	// End of getCheckRepeat Method

	
	/**
	 * This method initialises labelRepeat.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelRepeat() {
		
		// Check Existence
		if (labelRepeat == null) {
			
			// Create Instance
			labelRepeat = new JLabel();
			
			// Establish Language
			labelRepeat.setText(Lang.get("Options.HostOptions.Repeat"));
		}
		
		// Return Instance
		return labelRepeat;
	}
	// End of getLabelRepeat Method
	
	
	/**
	 * This method initialises checkSingle. Sets single state to STATE, STATE 
	 * should be 0 or 1. When single is activated, playback is stopped after 
	 * current song, or song is repeated if the 'repeat' mode is enabled. 	
	 * <p>
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getCheckSingle() {
		
		// Check for Existence
		if (checkSingle == null) {
			
			// Create Instance
			checkSingle = new JCheckBox();
			
			// Establish Language and Identifier
			checkSingle.setToolTipText(Lang.get("Tooltip.Options.Single"));
			checkSingle.setName("OPTIONS.SINGLE");
			
			// Establish Enabled State
			checkSingle.setEnabled(Enabled.contains(
					Terminus.PROTOCOL.get("Mode.Single")));
		}
		
		// Return Instance
		return checkSingle;
	}
	// End of getCheckSingle Method
	
	
	/**
	 * This method initialises labelSingle.	
	 * <p>
	 * @return javax.swing.JLabel	
	 */
	private JLabel getLabelSingle() {
		
		// Check Existence
		if (labelSingle == null) {
			
			// Create Instance
			labelSingle = new JLabel();
			
			// Establish Language
			labelSingle.setText(Lang.get("Options.HostOptions.Single"));
		}
		
		// Return Instance
		return labelSingle;
	}
	// End of getLabelSingle 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
	
	
	/**
	 * Will adjust the state elements of all checkbox and slider bars used
	 * within this panel.
	 */
	public void setState() {
		
		// Pass through
		Interface.setState();
	}
	// End of setState Method
	
	
	/**
	 * Will extract the user selected values of stateful elements within this
	 * panel. 
	 * <p>
	 * @return Map containing stateful information.
	 */
	@SuppressWarnings("serial")
	public Map<String,String> getState() {
		
		// Return Generated HashMap
		return new HashMap<String,String>(){
			{
				// Consume Song9
				if (getCheckConsume().isEnabled())
					put("Host.Options.Consume", 
						Boolean.toString(getCheckConsume().isSelected()));
				
				// Random Play
				if (getCheckRandom().isEnabled())
					put("Host.Options.Random", 
						Boolean.toString(getCheckRandom().isSelected()));
				
				// Repeat Play
				if (getCheckRepeat().isEnabled())
					put("Host.Options.Repeat", 
						Boolean.toString(getCheckRepeat().isSelected()));
				
				// Single / List Play
				if (getCheckSingle().isEnabled())
					put("Host.Options.Single", 
						Boolean.toString(getCheckSingle().isSelected()));
				
				// Crossfading
				if (getSliderCrossfade().isEnabled())
					put("Host.Options.Crossfade", 
						Integer.toString(getSliderCrossfade().getValue()));
			}
		};
	}
	// End of getState 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 {

		/**
		 * Stub Constructor 
		 */
		public Interact() {}
		
		
		/**
		 * Will adjust the state elements of all checkbox and slider bars used
		 * within this panel.
		 */
		public void setState() {
			
			// Extract current state
			Map<String,String> state = Info.getState();


			/*
			 * We establish state elements only if the value exists to be
			 * set to.  This should balance out on the protocol enabling of 
			 * buttons since the state value and command should be paired.
			 */
			
			// Consume Songs
			if (state.containsKey("consume"))
				getCheckConsume().setSelected(
					Status.fetchState(state.get("consume")));
			
			// Crossfading
			if (state.containsKey("xfade"))
				getSliderCrossfade().setValue(
					Integer.parseInt(state.get("xfade")));
			
			// Random Play
			if (state.containsKey("random"))
				getCheckRandom().setSelected(
					Status.fetchState(state.get("random")));
			
			// Repeat Play
			if (state.containsKey("repeat"))
				getCheckRandom().setSelected(
					Status.fetchState(state.get("repeat")));
			
			// Single Play
			if (state.containsKey("single"))
				getCheckSingle().setSelected(
					Status.fetchState(state.get("single")));
		}
		// End of setState Method
	}
	// End of Interact Class
	
}
