/*
 * File:     LinguisticTypeTableModel.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.type;

import mpi.eudico.client.annotator.ElanLocale;

import mpi.eudico.server.corpora.clomimpl.type.Constraint;
import mpi.eudico.server.corpora.clomimpl.type.LinguisticType;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.table.AbstractTableModel;


/**
 * A TableModel for a table displaying linguistic type information.
 *
 * @author Han Sloetjes
 */
public class LinguisticTypeTableModel extends AbstractTableModel {
    /** table column and label identifiers */
    public static final String LABEL_PREF = "EditTypeDialog.Label.";

    /** name of the type */
    public static final String NAME = "Type";

    /** the stereotype */
    public static final String STEREOTYPE = "Stereotype";

    /** time-alignable */
    public static final String TIME_ALIGNABLE = "TimeAlignable";

    /** graphics reference */
    public static final String GRAPHICS = "Graphics";

    /** the controlled vocabulary name */
    public static final String CV_NAME = "CV";

    /** selected state */
    public static final String SELECT = "Selected";

    /** empty or not applicable value */
    public static final String N_A = "-";
    private ArrayList types;
    private int[] currentShownStereoTypes;
    private List columnIds;
    private List data;
    private List classes;

    /**
     * Creates a new LinguisticTypeTableModel instance
     */
    public LinguisticTypeTableModel() {
        this(null);
    }

    /**
     * Creates a new LinguisticTypeTableModel instance
     *
     * @param allTypes the Vector of Linguistic Types
     */
    public LinguisticTypeTableModel(Vector allTypes) {
        this.types = (allTypes != null) ? new ArrayList(allTypes)
                                        : new ArrayList(0);

        columnIds = new ArrayList();

        //replaced
        //columnIds.add(ElanLocale.getString(LABEL_PREF + NAME));
        columnIds.add(SELECT);
        columnIds.add(NAME);
        columnIds.add(STEREOTYPE);
        columnIds.add(CV_NAME);
        columnIds.add(TIME_ALIGNABLE);
        columnIds.add(GRAPHICS);

        classes = new ArrayList(columnIds.size());
        classes.add(Boolean.class);
        classes.add(String.class);
        classes.add(String.class);
        classes.add(String.class);
        classes.add(Boolean.class);
        classes.add(Boolean.class);

        initData();
    }

    /**
     * Creates a new LinguisticTypeTableModel instance
     *
     * @param allTypes the Vector of Linguistic Types
     * @param columns the column identifiers
     */
    public LinguisticTypeTableModel(Vector allTypes, String[] columns) {
        this.types = (allTypes != null) ? new ArrayList(allTypes)
                                        : new ArrayList(0);

        columnIds = new ArrayList();
        classes = new ArrayList();

        if (columns != null) {
            for (int i = 0; i < columns.length; i++) {
                if (columns[i].equals(SELECT)) {
                    columnIds.add(SELECT);
                    classes.add(Boolean.class);
                } else if (columns[i].equals(NAME)) {
                    // replaced
                    // columnIds.add(ElanLocale.getString(LABEL_PREF + NAME));
                    columnIds.add(NAME);
                    classes.add(String.class);
                } else if (columns[i].equals(STEREOTYPE)) {
                    columnIds.add(STEREOTYPE);
                    classes.add(String.class);
                } else if (columns[i].equals(CV_NAME)) {
                    columnIds.add(CV_NAME);
                    classes.add(String.class);
                } else if (columns[i].equals(TIME_ALIGNABLE)) {
                    columnIds.add(TIME_ALIGNABLE);
                    classes.add(Boolean.class);
                } else if (columns[i].equals(GRAPHICS)) {
                    columnIds.add(GRAPHICS);
                    classes.add(Boolean.class);
                }
            }
        } else {
            columnIds.add(SELECT);
            columnIds.add(NAME);
            columnIds.add(STEREOTYPE);
            columnIds.add(CV_NAME);
            columnIds.add(TIME_ALIGNABLE);
            columnIds.add(GRAPHICS);

            classes.add(Boolean.class);
            classes.add(String.class);
            classes.add(String.class);
            classes.add(String.class);
            classes.add(Boolean.class);
            classes.add(Boolean.class);
        }

        initData();
    }

    /**
     * @see #initData(int[])
     */
    private void initData() {
        //data = new ArrayList(types.size());
        initData(currentShownStereoTypes);
    }

    /**
     * Add rows to the model's data list, eventually limiting the list to the
     * Linguistic Types  of the stereotypes that are specified by argument
     * <code>theseTypes</code>.
     *
     * @param theseTypes the types as int's (as specified in
     *        <code>mpi.eudico.server.corpora.clomimpl.type.Constraint</code>)
     */
    private void initData(int[] theseTypes) {
        data = new ArrayList(types.size());

        LinguisticType type;

        for (int i = 0; i < types.size(); i++) {
            type = (LinguisticType) types.get(i);

            boolean add = (theseTypes == null); // add if theseTypes == null

            if (!add) {
                int stereotype = -1; //no constraints

                if (type.hasConstraints()) {
                    stereotype = type.getConstraints().getStereoType();
                }

                for (int j = 0; j < theseTypes.length; j++) {
                    if (stereotype == theseTypes[j]) {
                        add = true;

                        break;
                    }
                }
            }

            if (add) {
                addRowData(type);
            }
        }

        fireTableDataChanged();
    }

    /**
     * Adds information taken from a LinguisticType object to the model. Only
     * information for the visible columns is used.
     *
     * @param type the LinguisticType to add
     */
    private void addRowData(LinguisticType type) {
        if (type == null) {
            return;
        }

        ArrayList rowData = new ArrayList(getColumnCount());

        if (columnIds.indexOf(SELECT) > -1) {
            rowData.add(columnIds.indexOf(SELECT), Boolean.FALSE);
        }

        if (columnIds.indexOf(NAME) > -1) {
            rowData.add(columnIds.indexOf(NAME), type.getLinguisticTypeName());
        }

        if (columnIds.indexOf(STEREOTYPE) > -1) {
            String stereoTypeName = N_A;

            if (type.hasConstraints()) {
                stereoTypeName = Constraint.stereoTypes[type.getConstraints()
                                                            .getStereoType()];
            }

            rowData.add(columnIds.indexOf(STEREOTYPE), stereoTypeName);
        }

        if (columnIds.indexOf(CV_NAME) > -1) {
            String cvName = N_A;

            if (type.isUsingControlledVocabulary()) {
                cvName = type.getControlledVocabylaryName();
            }

            rowData.add(columnIds.indexOf(CV_NAME), cvName);
        }

        if (columnIds.indexOf(TIME_ALIGNABLE) > -1) {
            rowData.add(columnIds.indexOf(TIME_ALIGNABLE),
                new Boolean(type.isTimeAlignable()));
        }

        if (columnIds.indexOf(GRAPHICS) > -1) {
            rowData.add(columnIds.indexOf(GRAPHICS),
                new Boolean(type.hasGraphicReferences()));
        }

        data.add(rowData);
    }

    /**
     * Sets which linguistic types to add to the table model.
     *
     * @param theseTypes an array of stereotypes identifiers
     */
    public void showOnlyStereoTypes(int[] theseTypes) {
        currentShownStereoTypes = theseTypes;
        initData();
    }

    /**
     * Returns the number of rows (== the size of the data list).
     *
     * @see javax.swing.table.TableModel#getRowCount()
     */
    public int getRowCount() {
        return data.size();
    }

    /**
     * Returns the number of columns (== the size of the list of column id's).
     *
     * @see javax.swing.table.TableModel#getColumnCount()
     */
    public int getColumnCount() {
        return columnIds.size();
    }

    /**
     * Finds the ArrayList of the specified row and returns the value at the
     * column index.
     *
     * @see javax.swing.table.TableModel#getValueAt(int, int)
     */
    public Object getValueAt(int rowIndex, int columnIndex) {
        if ((rowIndex < 0) || (rowIndex >= data.size()) || (columnIndex < 0) ||
                (columnIndex >= columnIds.size())) {
            return null;
        }

        ArrayList row = (ArrayList) data.get(rowIndex);

        return row.get(columnIndex);
    }

    /**
     * Returns false regardless of parameter values. The values are not  to be
     * edited directly in the table.
     *
     * @param row the row
     * @param column the column
     *
     * @return false
     */
    public boolean isCellEditable(int row, int column) {
        return false;
    }

    /**
     * Finds the column index for the specified identifier.
     *
     * @param columnName the name/identifier of the column
     *
     * @return the index, or -1 if not found
     */
    public int findColumn(String columnName) {
        return columnIds.indexOf(columnName);
    }

    /**
     * Returns the class of the data in the specified column. Note: returns
     * null instead of throwing an ArrayIndexOutOfBoundsException
     *
     * @param columnIndex the column
     *
     * @return the <code>class</code> of the objects in column
     *         <code>columnIndex</code>
     */
    public Class getColumnClass(int columnIndex) {
        if ((columnIndex < 0) || (columnIndex >= classes.size())) {
            return null;
        }

        return (Class) classes.get(columnIndex);
    }

    /**
     * Returns the identifier of the column. Note: returns empty String when
     * the column cannot be found
     *
     * @param columnIndex the column
     *
     * @return the id of the column or empty stringl
     */
    public String getColumnName(int columnIndex) {
        if ((columnIndex < 0) || (columnIndex >= columnIds.size())) {
            return "";
        }

        //return (String) columnIds.get(columnIndex);
        return ElanLocale.getString(LABEL_PREF +
            (String) columnIds.get(columnIndex));
    }

    /**
     * Note: silently returns instead of throwing an
     * ArrayIndexOutOfBoundsException
     *
     * @param rowIndex the row to remove
     */
    public void removeRow(int rowIndex) {
        if ((rowIndex >= 0) && (rowIndex < data.size())) {
            data.remove(rowIndex);
            types.remove(rowIndex);
            fireTableDataChanged();
        }
    }

    /**
     * Adds a row with the data of the LinguisticType to the model, if it's
     * stereotype is in the list of 'stereotypes to show'
     *
     * @param type the new LinguisticType
     *
     * @see #addLinguisticType(LinguisticType)
     */
    public void addRow(LinguisticType type) {
        if ((type == null) || types.contains(type)) {
            return;
        }

        types.add(type);

        if (currentShownStereoTypes == null) {
            addRowData(type);
            fireTableDataChanged();
        } else {
            int stereotype = -1; //no constraints

            if (type.hasConstraints()) {
                stereotype = type.getConstraints().getStereoType();
            }

            for (int j = 0; j < currentShownStereoTypes.length; j++) {
                if (stereotype == currentShownStereoTypes[j]) {
                    addRowData(type);
                    fireTableDataChanged();

                    break;
                }
            }
        }
    }

    /**
     * Adds a Linguistic Type to the Vector of Linguistic Types.
     *
     * @param type the new Linguistic Type
     *
     * @see #addRow(Linguistic Type)
     */
    public void addLinguisticType(LinguisticType type) {
        addRow(type);
    }

    /**
     * Notification that the data in some Linguistic Type has been changed so
     * the row value list should be updated.
     */
    public void rowDataChanged() {
        initData();
        fireTableDataChanged();
    }
}
