/*
 * Created on 25.02.2011
 *
 */
package org.jdesktop.swingx.table;

import java.awt.Component;
import java.util.Collections;
import java.util.List;

import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

/**
 * Collection of utility methods for J/X/Table.
 * 
 * @author Jeanette Winzenburg, Berlin
 */
public class TableUtilities {
    
    
    /**
     * Returns a boolean indication whether the event represents a
     * dataChanged type.
     * 
     * @param e the event to examine.
     * @return true if the event is of type dataChanged, false else.
     */
    public static boolean isDataChanged(TableModelEvent e) {
        if (e == null)
            return false;
        return e.getType() == TableModelEvent.UPDATE && e.getFirstRow() == 0
                && e.getLastRow() == Integer.MAX_VALUE;
    }

    /**
     * Returns a boolean indication whether the event represents a
     * update type.
     * 
     * @param e the event to examine.
     * @return true if the event is a true update, false
     *         otherwise.
     */
    public static boolean isUpdate(TableModelEvent e) {
        if (isStructureChanged(e))
            return false;
        return e.getType() == TableModelEvent.UPDATE
                && e.getLastRow() < Integer.MAX_VALUE;
    }

    /**
     * Returns a boolean indication whether the event represents a
     * insert type.
     * 
     * @param e the event to examine
     * @return true if the event is of type insert, false otherwise.
     */
    public static boolean isInsert(TableModelEvent e) {
        if (e == null) return false;
        return TableModelEvent.INSERT == e.getType();
    }


    /**
     * Returns a boolean indication whether the event represents a
     * structureChanged type.
     * 
     * @param e the event to examine.
     * @return true if the event is of type structureChanged or null, false
     *         else.
     */
    public static boolean isStructureChanged(TableModelEvent e) {
        return e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW;
    }



    /**
     * Returns the preferred height for the given row. It loops
     * across all visible columns and returns the maximal pref height of
     * the rendering component. Falls back to the table's base rowheight, i
     * f there are no columns or the renderers
     * max is zeor.<p>
     * 
     * @param table the table which provides the renderers, must not be null
     * @param row the index of the row in view coordinates
     * @return the preferred row height of
     * @throws NullPointerException if table is null.
     * @throws IndexOutOfBoundsException if the row is not a valid row index
     */
    public static int getPreferredRowHeight(JTable table, int row) {
        int pref = 0;
        for (int column = 0; column < table.getColumnCount(); column++) {
            TableCellRenderer renderer = table.getCellRenderer(row, column);
            Component comp = table.prepareRenderer(renderer, row, column);
            pref = Math.max(pref, comp.getPreferredSize().height);
        }
        return pref > 0 ? pref : table.getRowHeight();
    }
    
    /**
     * 
     * @param table the table which provides the renderers, must not be null
     * @param row the index of the row in view coordinates
     * @throws NullPointerException if table is null.
     * @throws IndexOutOfBoundsException if the row is not a valid row index
     */
    public static void setPreferredRowHeight(JTable table, int row) {
        int prefHeight = getPreferredRowHeight(table, row);
        table.setRowHeight(row, prefHeight);
    }

    /**
     * Sets preferred row heights for all visible rows. 
     * 
     * @param table the table to set row heights to
     * @throws NullPointerException if no table installed.
     */
    public static void setPreferredRowHeights(JTable table) {
        // care about visible rows only
        for (int row = 0; row < table.getRowCount(); row++) {
            setPreferredRowHeight(table, row);
        }
    }
    
    /**
     * Returns an array containing the ordinals of the given values of an Enum.<p>
     * 
     * Convience for clients which define TableColumns as Enums (Issue #1304-swingx).
     * 
     * @param values the enums to map to its ordinals
     * @return an array of ordinals, guaranteed to be not null
     */
    public static int[] ordinalsOf(Enum<?>... values) {
        int[] cols = new int[values.length];
        for (int i = 0; i < values.length; i++) {
            cols[i] = values[i].ordinal();
        }
        return cols;
    }
    

    /**
     * Removes all columns of the given column model. Includes hidden
     * columns as indicated by the includesHidden flag, the flag has no 
     * effect if the model is not of type TableColumnModelExt.<p>
     * 
     * @param model the column model to remove all columns from.
     * @param includeHidden indicates whether hidden columns should be 
     *   removed as well, has no effect if model is not of type TableColumnModelExt.
     */
    /*
     * Stand-in instead of fixing of issue http://java.net/jira/browse/SWINGX-1407
     */
    public static void clear(TableColumnModel model, boolean includeHidden) {
        if (model instanceof TableColumnModelExt) {
            clear(model, ((TableColumnModelExt) model).getColumns(includeHidden));
        } else {
            clear(model, Collections.list(model.getColumns()));
        }
    }

    private static void clear(TableColumnModel model, List<TableColumn> columns) {
        for (TableColumn tableColumn : columns) {
            model.removeColumn(tableColumn);
        }
    }

    private TableUtilities() {}
}
