/*
 * File:     AnnotationTable.java
 * Project:  MPI Linguistic Application
 * Date:     03 April 2006
 *
 * Copyright (C) 2001-2006  Max Planck Institute for Psycholinguistics
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package mpi.eudico.client.annotator.grid;

import mpi.eudico.client.annotator.Constants;
import mpi.eudico.client.annotator.ElanLocale;

import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;


/**
 * JTable for showing Annotations
 * Extracted from GridViewer on Jun 29, 2004
 * @author Alexander Klassmann
 * @version Jun 29, 2004
 */
public class AnnotationTable extends JTable {
    private static int MIN_COLUMN_WIDTH = 15; // swing default

    /** stores default minimal widths of columns */
    private static HashMap preferredWidths = new HashMap();

    /** stores default maximal widths of columns */
    private static HashMap maxWidths = new HashMap();

    //annotation columns don't have a default width; cf adjustAnnotationColumns()
    static {
        preferredWidths.put(GridViewerTableModel.TIMEPOINT, new Integer(15));
        preferredWidths.put(GridViewerTableModel.COUNT, new Integer(40));
        preferredWidths.put(GridViewerTableModel.FILENAME, new Integer(100));
        preferredWidths.put(GridViewerTableModel.TIERNAME, new Integer(80));
        preferredWidths.put(GridViewerTableModel.LEFTCONTEXT, new Integer(100));
        preferredWidths.put(GridViewerTableModel.RIGHTCONTEXT, new Integer(100));
        preferredWidths.put(GridViewerTableModel.BEGINTIME, new Integer(80));
        preferredWidths.put(GridViewerTableModel.ENDTIME, new Integer(80));
        preferredWidths.put(GridViewerTableModel.DURATION, new Integer(80));
        maxWidths.put(GridViewerTableModel.TIMEPOINT, new Integer(15));
        maxWidths.put(GridViewerTableModel.COUNT, new Integer(40));
        maxWidths.put(GridViewerTableModel.BEGINTIME, new Integer(120));
        maxWidths.put(GridViewerTableModel.ENDTIME, new Integer(120));
        maxWidths.put(GridViewerTableModel.DURATION, new Integer(120));
    }

    /** stores width of parent component */
    private int width = -1;

    /**
     *
     * @see javax.swing.JTable#JTable(TableModel)
     */
    public AnnotationTable(TableModel dataModel) {
        super(dataModel);

        setFont(Constants.DEFAULTFONT);
        setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
        setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS);

        for (int i = 0; i < dataModel.getColumnCount(); i++) {
            TableColumn column = getColumnModel().getColumn(i);
            String columnName = dataModel.getColumnName(i);
            column.setIdentifier(columnName);
            setColumnVisible(columnName, true);
        }

        getColumn(GridViewerTableModel.TIMEPOINT).setResizable(false);
        getColumn(GridViewerTableModel.COUNT).setResizable(false);

        getTableHeader().setReorderingAllowed(false);

        //after this component gets visible, add component listener to parent component		
        addComponentListener(new ComponentAdapter() {
                public void componentResized(ComponentEvent e) {
                    //table is visible ?
                    if (getWidth() > 0) {
                        //component listener to parent component (e.g. viewport of scrollpane)
                        getParent().addComponentListener(new ComponentAdapter() {
                                //adjust columns if width of viewport changes
                                public void componentResized(ComponentEvent e) {
                                    if (getParent().getWidth() != width) {
                                        width = getParent().getWidth();
                                        adjustAnnotationColumns();
                                    }
                                }
                            });
                        removeComponentListener(this);
                        adjustAnnotationColumns();
                    }
                }
            });
    }

    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e,
        int condition, boolean pressed) {
        return false;
    }

    public Dimension getPreferredScrollableViewportSize() {
        return new Dimension(640, 200);
    }

    /**
     * Restores the default row height after an inline edit operation.
     *
     * @param e the change event
     */
    public void editingStopped(ChangeEvent e) {
        super.editingStopped(e);
        setRowHeight(getRowHeight());
    }

    /**
     * Restores the default row height after an inline edit operation.
     *
     * @param e the change event
     */
    public void editingCanceled(ChangeEvent e) {
        super.editingCanceled(e);
        setRowHeight(getRowHeight());
    }

    /**
     * sets column width default value if true, to 0 if false
     * @param columnName
     * @param visible
     */
    public void setColumnVisible(String columnName, boolean visible) {
        TableColumn column = null;

        try {
            column = getColumn(columnName);

            // attention: don't change order of settings! A.K.
            if (visible) {
                column.setResizable(true);
                column.setMaxWidth(getDefaultMaxWidth(columnName));
                column.setMinWidth(MIN_COLUMN_WIDTH);
                column.setPreferredWidth(getDefaultPreferredWidth(columnName));
            } else {
                column.setMinWidth(0);
                column.setMaxWidth(0); // -> preferredWidth = 0
                column.setResizable(false);
            }
        } catch (IllegalArgumentException e) {
            System.out.println("Warning : no column with name " + columnName);
        }
    }

    /**
     * distributes remaining table space among annotation columns
     * to be called after other columns had been set/removed or component resized
     */
    protected void adjustAnnotationColumns() {
        if (getParent() != null) {
            Set visibleAnnotationColumns = new HashSet();
            int sumOfOtherWidths = 0;

            for (int i = 0; i < getColumnCount(); i++) {
                TableColumn column = getColumnModel().getColumn(i);

                if (!preferredWidths.containsKey(column.getIdentifier()) &&
                        (column.getMaxWidth() > 0)) {
                    visibleAnnotationColumns.add(column);
                } else {
                    sumOfOtherWidths += column.getPreferredWidth();
                }
            }

            int remainingSpace = getParent().getWidth() - sumOfOtherWidths;

            if (remainingSpace > 0) {
                for (Iterator iter = visibleAnnotationColumns.iterator();
                        iter.hasNext();) {
                    ((TableColumn) iter.next()).setPreferredWidth(remainingSpace / visibleAnnotationColumns.size());
                }
            }
        }
    }

    /**
     * returns if column is visible (e.g. width>0)
     * @param columnName
     * @return boolean
     */
    public boolean isColumnVisible(String columnName) {
        TableColumn column = null;

        try {
            column = getColumn(columnName);
        } catch (IllegalArgumentException e) {
            System.out.println("Warning : no column with name " + columnName);
        }

        return (column != null) ? (column.getWidth() > 0) : false;
    }

    /**
     * Returns default width for columns.
     * @param columnName the column name
     * @return the default preferred width
     */
    private int getDefaultPreferredWidth(String columnName) {
        return preferredWidths.containsKey(columnName)
        ? ((Integer) preferredWidths.get(columnName)).intValue()
        : MIN_COLUMN_WIDTH;

        //swing default
    }

    /**
     * Returns default width for columns.
     * @param columnName the column name
     * @return the default max width
     */
    private int getDefaultMaxWidth(String columnName) {
        return maxWidths.containsKey(columnName)
        ? ((Integer) maxWidths.get(columnName)).intValue() : Integer.MAX_VALUE;

        //swing default
    }

    /**
      * method from ElanLocaleListener not implemented in AbstractViewer
      */
    public void updateLocale() {
        for (int i = 0; i < dataModel.getColumnCount(); i++) {
            int index = getColumnModel().getColumnIndex(dataModel.getColumnName(
                        i));
            getColumnModel().getColumn(index).setHeaderValue(ElanLocale.getString(
                    "Frame.GridFrame." + dataModel.getColumnName(i)));
        }
    }

    public void setFontSize(int fontSize) {
        setFont(Constants.DEFAULTFONT.deriveFont((float) fontSize));
        setRowHeight((((fontSize + 2) < 16) ? 16 : (fontSize + 3)));
    }

    public int getFontSize() {
        return getFont().getSize();
    }

    /**
     * toggles string representation of time (HH:MM:SS.sss versus milliseconds)
     */
    public void toggleTimeFormat() {
        if (dataModel instanceof GridViewerTableModel) {
            String timeFormat = GridViewerTableModel.HHMMSSsss.equals(((GridViewerTableModel) dataModel).getTimeFormat())
                ? GridViewerTableModel.MILLISECONDS
                : GridViewerTableModel.HHMMSSsss;
            ((GridViewerTableModel) dataModel).setTimeFormat(timeFormat);

            repaint();
        }
    }

    /**
     * invokes adjustment of annotation columns
     * @see javax.swing.event.TableModelListener#tableChanged(TableModelEvent)
     */
    public void tableChanged(TableModelEvent e) {
        super.tableChanged(e);
        adjustAnnotationColumns();
    }
}
