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

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.table.TableCellEditor;

import org.lyra.Lyra;
import org.lyra.Interface.Constants;


/**
 * General widget with control methods allowing the instantiation of the rating
 * bar.  Two modes are enabled, allowing for editing and the other for straight
 * display and no interaction.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 147%, %LastChangedDate, 06/11/11 4:57PM%
 * @since 1.0.2
 */
public class RatingBar extends JPanel {

	/*
	 * INTERFACE ELEMENTS
	 */
	
	// Serial Requirements
	private static final long serialVersionUID = 1L;
	
	// Individual Elements
	private JLabel labelZero;
	private JLabel labelFirst;
	private JLabel labelSecond;
	private JLabel labelThird;
	private JLabel labelFourth;
	private JLabel labelFifth;
	
	// Icon Groups
	private ImageIcon iconFull  = new ImageIcon(
			getClass().getResource(
					Lyra.getIconByMap("Rating-Full")));
	private ImageIcon iconEmpty = new ImageIcon(
			getClass().getResource(
					Lyra.getIconByMap("Rating-Empty")));
	private ImageIcon iconZero = new ImageIcon(
			getClass().getResource(
					Lyra.getIconByMap("Rating-None")));
	
	/*
	 * CLASS INSTANCES
	 */
	
	// String containing actual value
	private String myValue         = null;
	
	// Actions Handler
	private Interact Interface     = null;
	
	// Generation of Interactivity
	private boolean Interactive    = false;
	
	// Editor to stop
	private TableCellEditor Editor = null;
	
	
	/**
	 * Base constructor for the class.   Returns a working instance of the
	 * class established for either straight display or modification.
	 * <p>
	 * @param interactive Flag for instanced interactivity.
	 * @param level Level the rating will take initially.
	 */
	private RatingBar(boolean interactive, int level) {
		
		// Pass Up
		
		// Interactivity
		this.Interactive = interactive;
		
		// Interface element
		this.Interface = (interactive)
			? new Interact()
			: null;
	
		// Establish components
		initPanel();
		
		// Set level to appropriate level
		updateRating(level);
	}
	// End of constructor method
	
	
	/**
	 * Generates a new instance of the rating bar without any interactivity.
	 * This is typically used for static display purposes only.
	 * <p>
	 * @return Generated instance of RatingBar.
	 */
	public static RatingBar getStatic() {
		
		// Pass on a newly created without the creation of interaction
		return new RatingBar(false, 0);
	}
	// End of getStatic Method
	
	
	/**
	 * Generates a new instance of the rating bar without any interactivity.
	 * This is typically used for static display purposes only.
	 * <p>
	 * @param level Integer containing level.
	 * @return Generated instance of RatingBar.
	 */
	public static RatingBar getStatic(int level) {
		
		// Pass on a newly created without the creation of interaction
		return new RatingBar(false, level);
	}
	// End of getStatic Method
	
	/**
	 * Generates a new instance of the rating bar with the ability to change
	 * the displayed value.  Used in the editor process and also for searching. 
	 * <p>
	 * @return Generated instance of RatingBar.
	 */
	public static RatingBar getDynamic() {
		
		// Pass on a newly created instance with the interaction instance
		return new RatingBar(true, 0);
	}
	// End of getDynamic Method
	
	
	/**
	 * Generates a new instance of the rating bar with the ability to change
	 * the displayed value.  Used in the editor process and also for searching. 
	 * <p>
	 * @param level Integer containing level.
	 * @return Generated instance of RatingBar.
	 */
	public static RatingBar getDynamic(int level) {
		
		// Pass on a newly created instance with the interaction instance
		return new RatingBar(true, level);
	}
	// End of getDynamic Method
	
	
	/**
	 * This method initialises panelRating.  Provides a search capability
	 * for rating elements which are displayed as stars.  	
	 */
	private void initPanel() {
		
		// Establish Layout
		this.setLayout(
			new BoxLayout(this, BoxLayout.X_AXIS));
		
		/*
		 * The element below is the only optional element.  It will only be
		 * shown when and if the rating bar can be modified.
		 */
		if (Interactive)
			this.add(getLabelZero(), null);
		
		/*
		 * All of these elements are consistent across the interface and will
		 * bring about enhanced operation.
		 */
		this.add(getLabelFirst(),  null);
		this.add(getLabelSecond(), null);
		this.add(getLabelThird(),  null);
		this.add(getLabelFourth(), null);
		this.add(getLabelFifth(),  null);
	}
	// End of getPanelRating Method

	
	/**
	 * Initialises the labelZero element.  The Zero element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelZero() {
		
		// Check for Existence
		if (labelZero == null) {
			
			// Create Instance
			labelZero = new JLabel(iconZero);
			
			// Identifier and Listener
			labelZero.setName("0");
			labelZero.addMouseListener(Interface);
		}
		
		// Return instance
		return labelZero;
	}
	// End of getLabelZero method
	
	
	/**
	 * Initialises the labelFirst element.  The first element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelFirst() {
		
		// Check for Existence
		if (labelFirst == null) {
			
			// Create Instance
			labelFirst = new JLabel(iconEmpty);
			
			// Identifier and Listener
			labelFirst.setName("1");
			labelFirst.addMouseListener(Interface);
		}
		
		// Return instance
		return labelFirst;
	}
	// End of getLabelFirst method
	
	
	/**
	 * Initialises the labelSecond element.  The Second element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelSecond() {
		
		// Check for Existence
		if (labelSecond == null) {
			
			// Create Instance
			labelSecond = new JLabel(iconEmpty);
			
			// Identifier and Listener
			labelSecond.setName("2");
			labelSecond.addMouseListener(Interface);
		}
		
		// Return instance
		return labelSecond;
	}
	// End of getLabelSecond method
	
	
	/**
	 * Initialises the labelThird element.  The Third element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelThird() {
		
		// Check for Existence
		if (labelThird == null) {
			
			// Create Instance
			labelThird = new JLabel(iconEmpty);
			
			// Identifier and Listener
			labelThird.setName("3");
			labelThird.addMouseListener(Interface);
		}
		
		// Return instance
		return labelThird;
	}
	// End of getLabelThird method
	
	
	/**
	 * Initialises the labelFourth element.  The Fourth element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelFourth() {
		
		// Check for Existence
		if (labelFourth == null) {
			
			// Create Instance
			labelFourth = new JLabel(iconEmpty);
			
			// Identifier and Listener
			labelFourth.setName("4");
			labelFourth.addMouseListener(Interface);
		}
		
		// Return instance
		return labelFourth;
	}
	// End of getLabelFourth method
	
	
	/**
	 * Initialises the labelFift element.  The fifth element in the rating
	 * series used to rate a song.
	 * <p>
	 * @return javax.swing.JLabel
	 */
	private JLabel getLabelFifth() {
		
		// Check for Existence
		if (labelFifth == null) {
			
			// Create Instance
			labelFifth = new JLabel(iconEmpty);
			
			// Identifier and Listener
			labelFifth.setName("5");
			labelFifth.addMouseListener(Interface);
		}
		
		// Return instance
		return labelFifth;
	}
	// End of getLabelFifth method
	
	
	
	/**
	 * Under some circumstances the rating bar will be used in an environment
	 * where it will have to indicate that its done with editing.  When
	 * provided with an instance the message will be passed on.
	 * <p>
	 * @param editor Instance of TableCellEditor to end.
	 */
	public void updateEditor(TableCellEditor editor) {
		
		// Pass up
		this.Editor = editor;
	}
	// End of updateEditor Method
	
	
	/**
	 * Ratings have to be updated visually otherwise the interface seems a bit
	 * less then authentic.
	 * <p>
	 * @param rating Number indicating rating level.
	 */
	public void updateRating(int rating) {

		// Create an Array of Image Icons
		ImageIcon[] state = new ImageIcon[Constants.getMaxRating() + 1];
		
		// Loop through and populate array
		for (int count=1; count <= Constants.getMaxRating(); count++) {
		
			// Generate icon based on context
			state[count] = (count <= rating)
				? iconFull
				: iconEmpty;
		}
		
		// Individually Set Elements
		getLabelFirst().setIcon(state[1]);
		getLabelSecond().setIcon(state[2]);
		getLabelThird().setIcon(state[3]);
		getLabelFourth().setIcon(state[4]);
		getLabelFifth().setIcon(state[5]);
	}
	// End of updateRating Method
	
	
	/**
	 * Fetches the currently set value to whatever is desired.
	 * <p>
	 * @return Integer containing rating value.
	 */
	public int fetchRating() {
		
		// Straight return
		return Integer.parseInt(myValue);
	}
	// End of fetchRating 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 MouseListener {

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
		 */
		public void mouseClicked(MouseEvent evt) {

			// Confirm this is the correct element
			if (evt.getSource() instanceof JLabel) {
				
				// Make a copy
				JLabel source = (JLabel)evt.getSource();
				
				// Assign as necessary
				if (source.getName() != null)
					myValue = source.getName();
				
				// Pass on the kill signal
				if (Editor != null)
					Editor.stopCellEditing();
			}
		}
		// End of mouseClicked Method
		
		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
		 */
		public void mouseEntered(MouseEvent evt) {
			
			// Confirm we are dealing with labels
			if (evt.getSource() instanceof JLabel) {
				
				// Update Source Elements
				JLabel source = (JLabel)evt.getSource();
				
				// Update user interface
				updateRating(
					Integer.parseInt(source.getName()));
			}
		}
		// End of mouseEntered Method

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
		 */
		public void mouseExited(MouseEvent evt) {}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
		 */
		public void mousePressed(MouseEvent evt) {}

		/* (non-Javadoc)
		 * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
		 */
		public void mouseReleased(MouseEvent evt) {}
		
	}
	// End of Interact Class
}
