/**
 * 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.util.Set;
import java.util.TreeSet;

import javax.swing.table.DefaultTableModel;

import org.lyra.Database.Comptroller;

/**
 * This is a wrapper to the DefaultTableModel which alters behaviour to 
 * match our needs in certain areas.   Primarily we need a table model that
 * will prevent users from making modifications unless explicitly desired.
 * <p>
 * @author Martin Foster
 * @version 1.0.4, %Revision, 99%, %LastChangedDate, 17/03/11 8:17PM%
 */
public class OrganiserTableModel extends DefaultTableModel {
	
	// Serial Dealings
	private static final long serialVersionUID = 1L;
	
	/*
	 * PACKAGE INSTANCES
	 */
	
	private Comptroller Manager = null;
	
	
	/*
	 * CLASS INSTANCES
	 */
	
	// Container which tracks edit ready rows
	private Set<Integer> myEdits = new TreeSet<Integer>();
	
	// Filtering elements
	private String myFilter = "";
	
	
	/*
	 * CONSTANTS
	 */
	private static final int FILE_COLUMN = 8;
	
	/**
	 * Default constructor for the class which has an option of allowing
	 * edits on certain rows if they are passed on.
	 * <p>
	 * @param manager Comptroller instance to track changes.
	 * @param dataset Multi-dimension array containing the entire data set.
	 * @param cols Columns that exist within this data set.
	 * @param editable Integer containing columns that can be edited.
	 */
	public OrganiserTableModel(Comptroller manager, Object[][] dataset, int cols, Integer...editable) {

		// Let the default table set do its thing
		super(dataset, new Object[cols]);
		
		// Pass Up
		this.Manager = manager;
		
		/*
		 * Loop through and add in column designators which can be
		 * modified by a user.
		 */
		for (int count=0; count < editable.length; count++)
			myEdits.add(editable[count]);
	}
	// End of Constructor Method
	
	
	/**
	 * Constructor which will provide all the information necessary in order to
	 * display a proper header.
	 * <p>
	 * @param manager Comptroller instance to track changes.
	 * @param dataset Multi-dimension array containing the entire data set.
	 * @param titles Array containing all of the column titles.
	 * @param editable Integer containing columns that can be edited.
	 */
	public OrganiserTableModel(Comptroller manager, Object[][] dataset, Object[] titles, Integer...editable) {

		// Let the default table set do its thing
		super(dataset, titles);
		
		// Pass Up
		this.Manager = manager;
		
		/*
		 * Loop through and add in column designators which can be
		 * modified by a user.
		 */
		for (int count=0; count < editable.length; count++)
			myEdits.add(editable[count]);
	}
	// End of OrganiserTableModel Method
	
	
	/**
	 * Default constructor for the class which has an option of allowing
	 * edits on certain rows if they are passed on.
	 * <p>
	 * @param dataset Multi-dimension array containing the entire data set.
	 * @param cols Columns that exist within this data set.
	 * @param editable Integer containing columns that can be edited.
	 */
	public OrganiserTableModel(Object[][] dataset, int cols, Integer...editable) {

		// Let the default table set do its thing
		super(dataset, new Object[cols]);
		
		/*
		 * Loop through and add in column designators which can be
		 * modified by a user.
		 */
		for (int count=0; count < editable.length; count++)
			myEdits.add(editable[count]);
	}
	// End of Constructor Method
	
	
	/**
	 * Constructor which will provide all the information necessary in order to
	 * display a proper header.
	 * <p>
	 * @param dataset Multi-dimension array containing the entire data set.
	 * @param titles Array containing all of the column titles.
	 * @param editable Integer containing columns that can be edited.
	 */
	public OrganiserTableModel(Object[][] dataset, Object[] titles, Integer...editable) {

		// Let the default table set do its thing
		super(dataset, titles);
		
		/*
		 * Loop through and add in column designators which can be
		 * modified by a user.
		 */
		for (int count=0; count < editable.length; count++)
			myEdits.add(editable[count]);
	}
	// End of OrganiserTableModel Method
	
	
	/**
	 * Add in a new column which will be enabled for editing.
	 * <p>
	 * @param column Column number to enable editing.
	 */
	public void addCellEditable(int column) {
		
		// Pass through
		myEdits.add(column);
	}
	// End of addEditable Method
	
	
	/**
	 * Remove a column which was formerly enabled for editing.
	 * <p>
	 * @param column Column number to disable editing.
	 */
	public void removeCellEditable(int column) {
		
		// Pass-Through
		myEdits.remove(column);
	}
	// End of removeEditable Method
	
	
	/* (non-Javadoc)
	 * @see javax.swing.table.DefaultTableModel#isCellEditable(int, int)
	 */
	public boolean isCellEditable(int row, int column) {

		// Straight pass-through
		return myEdits.contains(column);
	}
	// End of isCellEditable Method
	
	
	/* (non-Javadoc)
	 * @see javax.swing.table.DefaultTableModel#setValueAt(java.lang.Object, int, int)
	 */
	public void setValueAt(Object aValue, int row, int column) {
		
		// Conduct Normal Operation
		super.setValueAt(aValue, row, column);
			
		/*
		 * We cheat a bit in this area, as we know ahead of time that only one
		 * table will be returning the necessary element to be modified and that
		 * is the main song table.
		 */
		if ((Manager != null)
		 && (aValue != null)
		 && (getValueAt(row, FILE_COLUMN) != null)) {
			
			// We need the filename
			String file = getValueAt(row, FILE_COLUMN).toString();

			// With both we can now make modifications
			Manager.updateSongRating(file, aValue.toString());
		}
	}
	// End of setValueAt Method
	
	
	/**
	 * Is used to find the first row that contains information like that we
	 * have at the moment.
	 * <p>
	 * @param col Column to search against.
	 * @param key Character to add to the selection.
	 * @return Integer matching the row to move too.
	 */
	public int charFindFirst(int col, char key) {
		
		// Append the Character
		myFilter = myFilter + key;
		
		// Pass on value
		int pass = 0;
		
		// Search through the rows
		for (int row=0; row < this.getRowCount(); row++) {
			
			// Make sure we can work with the row
			if (getValueAt(row, col) != null) {
				
				// Capture value
				String current = getValueAt(row, col).toString();
				
				// Compare entries
				if (current.toUpperCase()
					.startsWith(myFilter.toUpperCase())) {
					
					// Record position
					pass = row;
					
					// Break out of loop
					break;
				}
			}
		}
		
		/*
		 * This is a safety element to make sure that the user is not dropping
		 * in an abnormally large string for whatever reason.  If we fail to 
		 * find anything we will reset the search pattern as well.
		 */
		if (pass == 0)
			myFilter = "";
		
		// Pass on value
		return pass;
	}
	// End of charFindFirst Method
	
	
	/**
	 * Removes the last character from the filter and searches based on that.
	 * <p>
	 * @param col Column to search against.
	 * @return Integer matching the row to move too.
	 */
	public int chopFindFirst(int col) {
		
		// Pass on value
		int pass = 0;
		
		// Can only truncate if we know ahead of time
		if (myFilter.length() > 0) {
			
			// Truncate string
			myFilter = myFilter.substring(0, myFilter.length() -1);

			// Search through the rows
			for (int row=0; row < this.getRowCount(); row++) {
				
				// Make sure we can work with the row
				if (getValueAt(row, col) != null) {
					
					// Capture value
					String current = getValueAt(row, col).toString();
					
					// Compare entries
					if (current.toUpperCase()
						.startsWith(myFilter.toUpperCase())) {
						
						// Record position
						pass = row;
						
						// Break out of loop
						break;
					}
				}
			}
		}
		
		// Return discovered row
		return pass;
	}
	// End of chopFindFirst Method
	
	
	/**
	 * Clear out the filtering for another pass.  Triggered when we make use
	 * of the escape character.
	 */
	public void clearFindFirst() {
		
		// Revert entry back to base
		myFilter = "";
	}
	// End of clearFindFirstMethod
	
}
// End of OrganiserTableModel Class
