/**
 * 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 java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;

import javax.swing.Box;
import javax.swing.JToolBar;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.BoxLayout;

import org.lyra.Ledger;
import org.lyra.Widgets.RatingBar;
import org.lyra.Widgets.RegexConstrainedDocument;


/**
 * A more advanced search capability that can be enabled by users of Lyra.  
 * Permits the user to move it about and also hide if so desired.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 109%, %LastChangedDate, 18/04/11 10:49PM%
 * @since 1.0.2
 */
public class PlayerSearchToolBar extends JToolBar {

	// Serial Madness
	private static final long serialVersionUID = 1L;
	
	/*
	 * VISUAL ELEMENTS
	 */
	
	// Main Holder
	private JPanel panelSearch = null;
	
	// Field Selection
	private JLabel labelField = null;
	private JComboBox comboField = null;
	
	// Criteria Selection
	private JLabel labelCriteria = null;
	private JTextField fieldCriteria = null;
	
	// Rating based criteria
	private RatingBar panelRating = null;
	
	
	// Search Action
	private JButton buttonSearch = null;

	
	/*
	 * PACKAGE ELEMENTS
	 */
	
	// Display of results handler
	private org.lyra.Interface.PlayerOrganiserFrame.Interact Display;
	
	// Logger Instance
	private Ledger Logger;
	
	// Language Element
	private Map<String,String> Lang;
	
	
	/*
	 * CLASS INSTANCES 
	 */
	
	// Holder elements
	private String[] Criteria;
	private Map<String,String> Converter;

	// Interaction capability
	private Interact Interface = new Interact();
	
	
	/**
	 * Primary constructor for visual editors.  Contains all of the necessary
	 * elements to create a non-interactive interface. 
	 */
	@SuppressWarnings("serial")
	public PlayerSearchToolBar() {
		
		// Have super class initialise
		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>(){
			{
				// Tab Options
				put("Search.Field","From");
				put("Search.Criteria","For");
				
	        	// Generic features found elsewhere
	        	put("Button.Search","Find");
			}
		};
		
		// Insert Interface Elements
		this.add(getPanelSearch());
	}
	// End of Constructor Method
	
	
	/**
	 * Primary constructor for the class.   Will provide complete interaction
	 * capability and cannot be readily used by a visual editor.  
	 * <p>
	 * @param logger Ledger instance for logging of actions.
	 * @param lang Language independent component.
	 * @param display Interact instance from PlayerOrganiserFrame for visual update.
	 * @see PlayerOrganiserFrame
	 */
	@SuppressWarnings("serial")
	public PlayerSearchToolBar(Ledger logger, final Map<String,String> lang, org.lyra.Interface.PlayerOrganiserFrame.Interact display) {
		
		// Have super class initialise
		super();
		
		/*
		 * The selection list and how it displays information is one of the 
		 * more important elements to this search capability.  We establish 
		 * the important information here for later processing.
		 */
		
		// Group Search Capability
		final String group = lang.get("Search.Group")
						         .replaceAll("MALBUM",  lang.get("Organizer.Table.Song.Album"))
						         .replaceAll("MARTIST", lang.get("Organizer.Table.Song.Artist"))
						         .replaceAll("MTITLE",  lang.get("Organizer.Table.Song.Title"));
		
		// String containing widget choices
		String[] criteria = {
			group,
			lang.get("Organizer.Table.Song.Album"),
			lang.get("Organizer.Table.Song.Artist"),
			lang.get("Organizer.Table.Song.Genre"),
			lang.get("Organizer.Table.Song.Title"),
			lang.get("Organizer.Table.Song.Track"),
			lang.get("Organizer.Table.Song.Date"),
			lang.get("Organizer.Table.Song.Rating"),
			lang.get("Organizer.Table.Song.File")
		};
		
		// Permits to translate to the database rows to do
		this.Converter = new HashMap<String,String>(){
			{
				put(group, "Artist,Album,Title");
				put(lang.get("Organizer.Table.Song.Album"),  "Album");
				put(lang.get("Organizer.Table.Song.Artist"), "Artist");
				put(lang.get("Organizer.Table.Song.Genre"),  "Genre");
				put(lang.get("Organizer.Table.Song.Title"),  "Title");
				put(lang.get("Organizer.Table.Song.Track"),  "Track");
				put(lang.get("Organizer.Table.Song.Date"),   "Date");
				put(lang.get("Organizer.Table.Song.Rating"), "Rating");
				put(lang.get("Organizer.Table.Song.File"),   "File");
			}
		};
		
		// Pass Up
		this.Display  = display;
		this.Logger   = logger;
		this.Lang     = lang;
		this.Criteria = criteria;


		// Insert Interface Elements
		this.add(getPanelSearch());

		/*
		 * The JToolBar element does not seem to be able to correctly asses
		 * the space it requires to display cleanly.   We have to add a 
		 * buffer in order to ensure the line is shown.
		 */
		
		// Capture panel size
		Dimension current   = getPanelSearch().getPreferredSize();
		
		// Force as JToolBar preferred
		this.setPreferredSize(
			new Dimension(
				current.width, 
				current.height + Constants.getSearchBuffer())
		  );	
	}
	// End of Constructor Method

	
	/**
	 * This method initialises panelSearch.  Contains all of the visual
	 * elements related to the search capability.
	 * <p>
	 * @return javax.swing.JPanel	
	 */
	private JPanel getPanelSearch() {
		
		// Check for Existence
		if (panelSearch == null) {

			// Create Instance
			panelSearch = new JPanel();
			panelSearch.setLayout(
				new BoxLayout(getPanelSearch(), BoxLayout.X_AXIS));

			/*
			 * Visual elements are a simple affair with this capability.  All
			 * that we need to account for is additional spacing.
			 */
			
			// Initial Spacer
			panelSearch.add(Box.createHorizontalStrut(
					Constants.getSmallGap()), null);
			
			// Field
			panelSearch.add(getLabelField(), null);
			panelSearch.add(getComboField(), null);
			panelSearch.add(Box.createHorizontalStrut(
				Constants.getNormalGap()), null);
			
			// Criteria
			panelSearch.add(getLabelCriteria(), null);
			panelSearch.add(getFieldCriteria(), null);
			panelSearch.add(getPanelRating(), null);
			
			// Search Action
			panelSearch.add(Box.createHorizontalStrut(
					Constants.getNormalGap()), null);
			panelSearch.add(getButtonSearch(), null);
		}
		
		// Return Instance
		return panelSearch;
	}
	// End of getPanelSearch Method

	
	/**
	 * This method initialises panelRating.  Contains all of the visual
	 * elements related to the Rating capability.
	 * <p>
	 * @return org.lyra.Widgets.RatingBar	
	 */
	private RatingBar getPanelRating() {
		
		// Check for Existence
		if (panelRating == null) {

			// Create Instance
			panelRating = RatingBar.getDynamic();
			
			// Hide by default
			panelRating.setVisible(false);
		}
		
		// Return Instance
		return panelRating;
	}
	// End of getPanelRating Method
	
	
	/**
	 * This method initialises labelCriteria.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelCriteria() {

		// Check for Instance
		if (labelCriteria == null) {
			
			// Create Instance and Set Language
			labelCriteria = new JLabel();
			labelCriteria.setText(
				Lang.get("Search.Criteria"));
		}
		
		// Return Instance
		return labelCriteria;
	}
	// End of getLabelCriteria Method
	
	
	/**
	 * This method initialises labelField.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelField() {

		// Check for Existence
		if (labelField == null) {
			
			// Create Instance and Set Language
			labelField = new JLabel();
			labelField.setText(
				Lang.get("Search.Field"));
		}
		
		// Return Instance
		return labelField;
	}
	// End of getLabelField Method
	

	/**
	 * This method initialises comboField.  Contains search criteria for all
	 * of the rows one can search against.	
	 * <p>
	 * @return javax.swing.JComboBox	
	 */
	private JComboBox getComboField() {
		
		// Check for Existence
		if (comboField == null) {
			
			// Create Instance
			comboField = (Criteria != null)
				? new JComboBox(Criteria)
				: new JComboBox();
			
			// Identifier
			comboField.setName("SEARCH.CRITERIA");
			comboField.addActionListener(Interface);
		}
		
		// Return Instance
		return comboField;
	}
	// End of getComboField Method

	
	/**
	 * This method initialises fieldCriteria.  Free-form text element allowing
	 * the user to specify search criteria.	
	 * <p>
	 * @return javax.swing.JTextField	
	 */
	private JTextField getFieldCriteria() {
		
		// Check for Existence
		if (fieldCriteria == null) {
			
			/*
			 * Create a document capable of limiting the amount of choices that
			 * will be encountered 
			 */
			RegexConstrainedDocument doc = 
				new RegexConstrainedDocument("^[\\w%-. ]{0,50}$");

			// Create Instance
			fieldCriteria = new JTextField(doc, "", 25);
			
			// Identifier and Listener
			fieldCriteria.setName("SEARCH.CRITERIA");
		}
		
		// Return Instance
		return fieldCriteria;
	}
	// End of getFieldCriteria Method

	
	/**
	 * This method initialises buttonSearch.  Triggers the search capability
	 * and will display results in the Player/Organiser panel.
	 * <p>
	 * @return javax.swing.JButton	
	 */
	private JButton getButtonSearch() {
		
		// Check for Existence
		if (buttonSearch == null) {
			
			// Create Instance and Setup Language
			buttonSearch = new JButton();
			buttonSearch.setText(
				Lang.get("Button.Search"));
			
			// Identifier and Listener
			buttonSearch.setName("SEARCH.NOW");
			buttonSearch.addActionListener(Interface);
		}
		
		// Return Instance
		return buttonSearch;
	}
	// End of getButtonSearch 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 {

		// Class Identifier
		private static final String CLASS = "org.lyra.Interface.PlayerSearchToolBar.Interact";
		
		
		/* (non-Javadoc)
		 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
		 */
		public void actionPerformed(ActionEvent evt) {
			
			// Extract the selected text
			String selected = 
				getComboField().getSelectedItem().toString();
			
			/*
			 * JComboBox actions will enable the panel to correct which 
			 * amplifying information is displayed.
			 */
			if (evt.getSource() instanceof JComboBox) {
				
				// Confirm visibility
				boolean visible = (!selected.equals(Lang.get("Organizer.Table.Song.Rating")))
					? true
					: false;

				// Adjust visibility
				getFieldCriteria().setVisible(visible);
				getPanelRating().setVisible(!visible);
				
				// Log the event
				Logger.Info(
					CLASS,
					"actionPerformed",
					Lang.get("Search.Log.Field")
					    .replaceAll("MFIELD",selected));
			}
			
			
			/*
			 * The only button we have in this interface exists exclusively to
			 * trigger a search capability.
			 */
			else if (evt.getSource() instanceof JButton) { 
				
				// Extract proper search field(s)
				String field = Converter.get(
						getComboField().getSelectedItem().toString());
			
				// Holder Element
				String criteria;
				
				/*
				 * Normal elements will all make use of the criteria field
				 * which the user specifies its input.
				 */
				if ((!selected.equals(Lang.get("Organizer.Table.Song.Rating")))) {
					
					// Extract text element
					criteria = getFieldCriteria().getText();
					
					// Safety
					if (criteria.isEmpty()) return;
					
					/*
					 * We like to confirm that there are searching elements
					 * thrown into the mix.  If there are none, we
					 * automatically add them in.
					 */
					if (criteria.indexOf('%') == -1)
						criteria = '%' + criteria + '%';
				}
				
				/*
				 * Rating elements make use the common editor and will query 
				 * based on the elements,
				 */
				else {
					
					// Extract the specific element
					criteria = Integer.toString(
							getPanelRating().fetchRating());
				}
				
				// Log the event
				Logger.Info(
					CLASS,
					"actionPerformed",
					Lang.get("Search.Log.Trigger")
					    .replaceAll("MFIELD",    field)
					    .replaceAll("MCRITERIA", criteria));
				
				/*
				 * Perform an update of the main song listing after a search
				 * has been done against the database.
				 */
				Display.updateLibrary(field, criteria);
			}
		}
		// End of actionPerformed Method
		
	}
	// End of Interact Class
}
