package tim.prune.load;

import javax.swing.table.AbstractTableModel;

import tim.prune.I18nManager;
import tim.prune.data.Field;
import tim.prune.data.FieldCustom;

/**
 * Class to hold the table model for the field selection table
 */
public class FieldSelectionTableModel extends AbstractTableModel
{
	private int _numRows = 0;
	private Field[] _fieldArray = null;
	private final String _customText;

	/**
	 * Constructor
	 */
	public FieldSelectionTableModel()
	{
		// Cache the custom text for the table so it doesn't
		// have to be looked up so often
		_customText = I18nManager.getText("fieldname.custom");
	}


	/**
	 * @return the column count
	 */
	public int getColumnCount() {
		return 3;
	}

	/** @return the language-specific name defining any custom field */
	public String getCustomText() {
		return _customText;
	}

	/**
	 * @param inColNum column number
	 * @return name of the column
	 */
	public String getColumnName(int inColNum)
	{
		if (inColNum == 0) {
			return I18nManager.getText("dialog.load.table.field");
		}
		else if (inColNum == 1) {
			return I18nManager.getText("dialog.load.table.datatype");
		}
		return I18nManager.getText("dialog.load.table.description");
	}


	/**
	 * @return the row count
	 */
	public int getRowCount() {
		return _fieldArray == null ? 2 : _numRows;
	}


	/**
	 * @param inRowIndex row index
	 * @param inColumnIndex column index
	 * @return the value of the specified cell
	 */
	public Object getValueAt(int inRowIndex, int inColumnIndex)
	{
		if (_fieldArray == null) {
			return "";
		}
		if (inColumnIndex == 0) {
			return ("" + (inRowIndex+1));
		}
		Field field = _fieldArray[inRowIndex];
		if (inColumnIndex == 1)
		{
			// Field name - take name from built-in fields
			return field.isBuiltIn() ? field.getName() : _customText;
		}
		// description column - builtin fields don't have one
		return field.isBuiltIn() ? "" : field.getName();
	}


	/**
	 * Make sure only second and third columns are editable
	 * @param inRowIndex row index
	 * @param inColumnIndex column index
	 * @return true if cell editable
	 */
	public boolean isCellEditable(int inRowIndex, int inColumnIndex)
	{
		if (inColumnIndex <= 1) {
			return (inColumnIndex == 1);
		}
		// Column is 2 so only edit non-builtin field names
		Field field = _fieldArray[inRowIndex];
		return !field.isBuiltIn();
	}


	/**
	 * Update the data
	 * @param inData 2-dimensional Object array containing the data
	 */
	public void updateData(Field[] inData)
	{
		_fieldArray = inData;
		_numRows = _fieldArray == null ? 0 : _fieldArray.length;
		fireTableStructureChanged();
	}


	/**
	 * React to edits to the table data
	 * @param inValue value to set
	 * @param inRowIndex row index
	 * @param inColumnIndex column index
	 */
	public void setValueAt(Object inValue, int inRowIndex, int inColumnIndex)
	{
		super.setValueAt(inValue, inRowIndex, inColumnIndex);
		if (inColumnIndex == 1)
		{
			Field field = _fieldArray[inRowIndex];
			if (!field.getName().equals(inValue.toString())) {
				manageFieldChange(inRowIndex, inValue.toString());
			}
		}
		else if (inColumnIndex == 2)
		{
			// change description if it's custom
			Field field = _fieldArray[inRowIndex];
			field.setName(inValue.toString());
		}
	}


	/**
	 * Move the selected item up one place
	 * @param inIndex index of item to move
	 */
	public void moveUp(int inIndex)
	{
		if (inIndex > 0) {
			swapItems(inIndex-1, inIndex);
		}
	}


	/**
	 * Move the selected item down one place
	 * @param inIndex index of item to move
	 */
	public void moveDown(int inIndex)
	{
		if (inIndex > -1 && inIndex < (_numRows - 1)) {
			swapItems(inIndex, inIndex+1);
		}
	}


	/**
	 * Swap the specified items in the array
	 * @param inIndex1 index of first item
	 * @param inIndex2 index of second item (higher than inIndex1)
	 */
	private void swapItems(int inIndex1, int inIndex2)
	{
		Field temp = _fieldArray[inIndex1];
		_fieldArray[inIndex1] = _fieldArray[inIndex2];
		_fieldArray[inIndex2] = temp;
		fireTableRowsUpdated(inIndex1, inIndex2);
	}


	/**
	 * React to a requested change to one of the fields
	 * @param inRow row number of change
	 * @param inValue new string value
	 */
	private void manageFieldChange(int inRow, String inValue)
	{
		// check if it's lat or long - don't allow changes to these fields
		Field field = _fieldArray[inRow];
		if (field == Field.LATITUDE || field == Field.LONGITUDE) {
			return;
		}
		if (inValue.equals(I18nManager.getText("fieldname.latitude"))
		  || inValue.equals(I18nManager.getText("fieldname.longitude"))) {
			return;
		}

		// Changes to custom field need to be handled differently
		boolean changeToCustom = inValue.equals(_customText);
		if (changeToCustom)
		{
			if (field.isBuiltIn())
			{
				String customPrefix = I18nManager.getText("fieldname.prefix") + " ";
				int index = inRow + 1;
				while (hasField(customPrefix + index)) {
					index++;
				}
				_fieldArray[inRow] = new FieldCustom(customPrefix + index);
			}
			// ignore custom to custom changes
		}
		else
		{
			// Change to a fixed field - check we've not already got it
			if (!hasField(inValue))
			{
				// Change is ok - find new Field object corresponding to text
				_fieldArray[inRow] = Field.getField(inValue);
			}
		}
		// fire change
		fireTableRowsUpdated(inRow, inRow);
	}


	/**
	 * @return array of Field objects
	 */
	public Field[] getFieldArray() {
		return _fieldArray;
	}


	/**
	 * @param inName Name of field to find
	 * @return true if this field is already present
	 */
	private boolean hasField(String inName)
	{
		if (_fieldArray == null || inName == null) {
			return false;
		}
		for (int i=0; i<_numRows; i++)
		{
			if (_fieldArray[i].getName().equals(inName)) {
				return true;
			}
		}
		return false;
	}
}
