/*
 * File:     EditTierDialog2.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.gui;

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

import mpi.eudico.client.annotator.commands.Command;
import mpi.eudico.client.annotator.commands.ELANCommandFactory;

import mpi.eudico.client.annotator.tier.TierTableModel;

import mpi.eudico.client.annotator.timeseries.TrackTableModel;

import mpi.eudico.client.annotator.util.ElanFileFilter;
import mpi.eudico.client.annotator.util.FileExtension;

import mpi.eudico.client.im.ImUtil;

import mpi.eudico.server.corpora.clom.Tier;
import mpi.eudico.server.corpora.clom.Transcription;

import mpi.eudico.server.corpora.clomimpl.abstr.TierImpl;
import mpi.eudico.server.corpora.clomimpl.abstr.TranscriptionImpl;
import mpi.eudico.server.corpora.clomimpl.type.Constraint;
import mpi.eudico.server.corpora.clomimpl.type.LinguisticType;

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.io.File;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;


/**
 * A dialog to create, change, delete or import tiers. This is an extended
 * version of EditTierDialog. An information table has been added showing info
 * on the current tiers. Tabs have been introduced to switch between add,
 * change, delete or import  mode. The import mode is also new: it enables the
 * import of tiers (with associated  linguistic types and cv's, but without
 * annotations) from an .eaf or .etf.
 *
 * @author Han Sloetjes
 * @version 2.0
 */
public class EditTierDialog2 extends JDialog implements ActionListener,
    ItemListener, ChangeListener, ListSelectionListener {
    /** the add mode */
    public static final int ADD = 0;

    /** the change mode */
    public static final int CHANGE = 1;

    /** the delete mode */
    public static final int DELETE = 2;

    /** the import mode */
    public static final int IMPORT = 3;

    /** value for no parent */
    final private String none = "none";

    //private Frame frame;
    private TranscriptionImpl transcription;
    private TierImpl tier = null;
    private TierImpl oldParentTier;
    private String oldTierName;
    private String oldParentTierName;
    private String oldParticipant;
    private LinguisticType oldLingType;
    private Locale oldLocale;
    private Locale[] langs;
    private int mode = ADD;
    private boolean singleEditMode = false;
    private Vector tiers;

    // ui
    private JLabel titleLabel;
    private JPanel tablePanel;
    private JTable tierTable;
    private TierTableModel model;
    private JTabbedPane tabPane;

    // ui elements for edit panel
    private JPanel editPanel;
    private JLabel selectTierLabel;
    private JLabel tierNameLabel;
    private JComboBox currentTiersComboBox;
    private JTextField tierNameTextField;
    private JLabel participantLabel;
    private JTextField participantTextField;
    private JLabel lingTypeLabel;
    private JComboBox lingTypeComboBox;
    private JLabel parentLabel;
    private JComboBox parentComboBox;
    private JLabel languageLabel;
    private JComboBox languageComboBox;

    // import panel
    private JPanel importPanel;
    private JLabel importSourceLabel;
    private JTextField importSourceTF;
    private JButton importSourceButton;
    private JButton changeButton;
    private JButton cancelButton;
    private JPanel buttonPanel;

    /**
     * Creates a new EditTierDialog2 instance
     *
     * @param parentFrame the parent ELAN frame
     * @param modal the modal flag: true
     * @param theTranscription the transcription to work on
     * @param editMode the edit mode: ADD, CHANGE, DELETE or IMPORT
     * @param tier the tier to select in the ui initially
     */
    public EditTierDialog2(Frame parentFrame, boolean modal,
        Transcription theTranscription, int editMode, TierImpl tier) {
        super(parentFrame, modal);

        //frame = parentFrame;
        transcription = (TranscriptionImpl) theTranscription;

        if ((editMode >= ADD) && (editMode <= IMPORT)) {
            mode = editMode;
        }

        initComponents();
        extractCurrentTiers();

        if (tier != null) {
            this.tier = tier;

            String name = tier.getName();
            singleEditMode = true;

            if (currentTiersComboBox != null) {
                currentTiersComboBox.setSelectedItem(name);
            }
        }

        updateLanguageComboBox();
        updateLocale();
        updateForMode();
        updateUIForTier((String) currentTiersComboBox.getSelectedItem());
        postInit();

        if (editMode == ADD) {
            tierNameTextField.requestFocus();
        }
    }

    /**
     * Initializes the ui components.
     */
    private void initComponents() {
        langs = ImUtil.getLanguages(this);

        getContentPane().setLayout(new GridBagLayout());

        Insets insets = new Insets(2, 6, 2, 6);

        titleLabel = new JLabel();
        titleLabel.setFont(titleLabel.getFont().deriveFont((float) 16));
        titleLabel.setHorizontalAlignment(SwingConstants.CENTER);

        tablePanel = new JPanel();
        tablePanel.setLayout(new GridBagLayout());
        model = new TierTableModel(transcription.getTiers());
        tierTable = new JTable(model);
        tierTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        JScrollPane tableScrollPane = new JScrollPane(tierTable);
        Dimension size = new Dimension(300, 120);
        tableScrollPane.setMinimumSize(size);
        tableScrollPane.setPreferredSize(size);

        tabPane = new JTabbedPane();

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.insets = new Insets(6, 6, 6, 6);
        gbc.weightx = 1.0;
        getContentPane().add(titleLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.BOTH;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = insets;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        tablePanel.add(tableScrollPane, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 1;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = insets;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        getContentPane().add(tablePanel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 2;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = new Insets(10, 6, 6, 6);
        gbc.weightx = 1.0;

        //gbc.weighty = 1.0;
        getContentPane().add(tabPane, gbc);

        // edit panel
        editPanel = new JPanel(new GridBagLayout());
        selectTierLabel = new JLabel();
        currentTiersComboBox = new JComboBox();
        tierNameLabel = new JLabel();
        tierNameTextField = new JTextField();
        participantLabel = new JLabel();
        participantTextField = new JTextField();
        lingTypeLabel = new JLabel();
        lingTypeComboBox = new JComboBox();
        parentLabel = new JLabel();
        parentComboBox = new JComboBox();
        languageLabel = new JLabel();
        languageComboBox = new JComboBox();

        GridBagConstraints lgbc = new GridBagConstraints();
        lgbc.anchor = GridBagConstraints.NORTHWEST;
        lgbc.insets = insets;
        editPanel.add(selectTierLabel, lgbc);

        GridBagConstraints rgbc = new GridBagConstraints();
        rgbc.gridx = 1;
        rgbc.fill = GridBagConstraints.HORIZONTAL;
        rgbc.anchor = GridBagConstraints.NORTHWEST;
        rgbc.insets = insets;
        rgbc.weightx = 1.0;
        editPanel.add(currentTiersComboBox, rgbc);
        lgbc.gridy = 1;
        editPanel.add(tierNameLabel, lgbc);
        rgbc.gridy = 1;
        editPanel.add(tierNameTextField, rgbc);
        lgbc.gridy = 2;
        editPanel.add(participantLabel, lgbc);
        rgbc.gridy = 2;
        editPanel.add(participantTextField, rgbc);
        lgbc.gridy = 3;
        editPanel.add(parentLabel, lgbc);
        rgbc.gridy = 3;
        editPanel.add(parentComboBox, rgbc);
        lgbc.gridy = 4;
        editPanel.add(lingTypeLabel, lgbc);
        rgbc.gridy = 4;
        editPanel.add(lingTypeComboBox, rgbc);
        lgbc.gridy = 5;
        editPanel.add(languageLabel, lgbc);
        rgbc.gridy = 5;
        editPanel.add(languageComboBox, rgbc);

        // import panel
        importPanel = new JPanel(new GridBagLayout());
        importSourceLabel = new JLabel();
        importSourceTF = new JTextField();
        importSourceTF.setEditable(false);
        importSourceButton = new JButton();
        importSourceButton.addActionListener(this);

        gbc = new GridBagConstraints();
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = insets;
        gbc.weightx = 1.0;
        importPanel.add(importSourceLabel, gbc);

        gbc = new GridBagConstraints();
        gbc.gridy = 1;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = insets;
        gbc.weightx = 1.0;
        importPanel.add(importSourceTF, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.fill = GridBagConstraints.NONE;
        gbc.anchor = GridBagConstraints.NORTHWEST;
        gbc.insets = insets;
        importPanel.add(importSourceButton, gbc);

        tabPane.addTab(ElanLocale.getString("Button.Add"), null);
        tabPane.addTab(ElanLocale.getString("Button.Change"), null);
        tabPane.addTab(ElanLocale.getString("Button.Delete"), null);
        tabPane.addTab(ElanLocale.getString("Button.Import"), importPanel);

        if (mode < IMPORT) {
            tabPane.setComponentAt(mode, editPanel);
        } else {
            tabPane.setComponentAt(0, editPanel);
        }

        tabPane.setSelectedIndex(mode);
        tabPane.addChangeListener(this);

        // buttons
        buttonPanel = new JPanel(new GridLayout(1, 2, 6, 2));
        changeButton = new JButton();
        changeButton.addActionListener(this);
        cancelButton = new JButton();
        cancelButton.addActionListener(this);
        buttonPanel.add(changeButton);
        buttonPanel.add(cancelButton);

        gbc = new GridBagConstraints();
        gbc.gridy = 3;
        gbc.anchor = GridBagConstraints.SOUTH;
        gbc.insets = insets;
        getContentPane().add(buttonPanel, gbc);
    }

    /**
     * Pack, size and set location.
     */
    private void postInit() {
        pack();

        int w = 550;
        int h = 400;
        setSize((getSize().width < w) ? w : getSize().width,
            (getSize().height < h) ? h : getSize().height);
        setLocationRelativeTo(getParent());

        //setResizable(false);
    }

    /**
     * Applies localized strings to the ui elements.
     */
    private void updateLocale() {
        tablePanel.setBorder(new TitledBorder(ElanLocale.getString(
                    "EditTierDialog.Label.CurrentTiers")));
        tierNameLabel.setText(ElanLocale.getString(
                "EditTierDialog.Label.TierName"));
        participantLabel.setText(ElanLocale.getString(
                "EditTierDialog.Label.Participant"));
        lingTypeLabel.setText(ElanLocale.getString(
                "EditTierDialog.Label.LinguisticType"));
        parentLabel.setText(ElanLocale.getString("EditTierDialog.Label.Parent"));
        languageLabel.setText(ElanLocale.getString(
                "EditTierDialog.Label.Language"));
        cancelButton.setText(ElanLocale.getString("Button.Close"));
        importSourceLabel.setText("<html>" +
            ElanLocale.getString("EditTierDialog.Label.ImportSource") +
            "</html>");
        importSourceButton.setText(ElanLocale.getString("Button.Browse"));
    }

    /**
     * Updates texts and enables/disables components for the current  edit
     * mode.
     */
    private void updateForMode() {
        switch (mode) {
        case ADD:
            setTitle(ElanLocale.getString("EditTierDialog.Title.Add"));
            selectTierLabel.setText("");

            currentTiersComboBox.setEnabled(false);

            //currentTiersComboBox.setVisible(false);
            changeButton.setText(ElanLocale.getString("Button.Add"));
            parentComboBox.setEnabled(true);
            tierNameTextField.setEditable(true);
            participantTextField.setEditable(true);
            lingTypeComboBox.setEnabled(true);
            languageComboBox.setEnabled(true);
            tierNameTextField.setText("");
            participantTextField.setText("");

            break;

        case CHANGE:
            setTitle(ElanLocale.getString("EditTierDialog.Title.Change"));
            selectTierLabel.setText(ElanLocale.getString(
                    "EditTierDialog.Label.ChangeTier"));
            selectTierLabel.setVisible(true);
            currentTiersComboBox.setEnabled(true);
            currentTiersComboBox.setVisible(true);
            currentTiersComboBox.requestFocus();
            changeButton.setText(ElanLocale.getString("Button.Change"));
            parentComboBox.setEnabled(false);
            tierNameTextField.setEditable(true);
            participantTextField.setEditable(true);
            lingTypeComboBox.setEnabled(true);
            languageComboBox.setEnabled(true);

            break;

        case DELETE:
            setTitle(ElanLocale.getString("EditTierDialog.Title.Delete"));
            selectTierLabel.setText(ElanLocale.getString(
                    "EditTierDialog.Label.DeleteTier"));
            changeButton.setText(ElanLocale.getString("Button.Delete"));
            tierNameTextField.setEditable(false);
            participantTextField.setEditable(false);
            parentComboBox.setEnabled(false);
            lingTypeComboBox.setEnabled(false);
            languageComboBox.setEnabled(false);
            selectTierLabel.setVisible(true);
            currentTiersComboBox.setEnabled(true);
            currentTiersComboBox.setVisible(true);
            currentTiersComboBox.requestFocus();

            break;

        case IMPORT:
            setTitle(ElanLocale.getString("EditTierDialog.Title.Import"));
            changeButton.setText(ElanLocale.getString("Button.Import"));

            break;
        }

        titleLabel.setText(getTitle());
    }

    /**
     * Fill the tiers combobox with the currently present tiers.
     */
    private void extractCurrentTiers() {
        currentTiersComboBox.removeItemListener(this);
        currentTiersComboBox.removeAllItems();
        tiers = transcription.getTiers();

        if (tiers == null) {
            tiers = new Vector();

            return;
        }

        Iterator tierIt = tiers.iterator();

        while (tierIt.hasNext()) {
            TierImpl t = (TierImpl) tierIt.next();
            currentTiersComboBox.addItem(t.getName());
        }

        if (currentTiersComboBox.getItemCount() > 0) {
            currentTiersComboBox.setSelectedIndex(0);
            tier = (TierImpl) tiers.get(0);
        }

        currentTiersComboBox.addItemListener(this);
    }

    /**
     * Again extract the tiers from the transcription after an add,  change or
     * delete operation.
     */
    private void reextractTiers() {
        extractCurrentTiers();

        if (currentTiersComboBox.getItemCount() > 0) {
            currentTiersComboBox.setSelectedIndex(0);
        } else {
            tierNameTextField.setText("");
            participantTextField.setText("");
        }

        if (mode == ADD) {
            tierNameTextField.setText("");
            participantTextField.setText("");
        }

        if (model != null) {
            tierTable.getSelectionModel().removeListSelectionListener(this);
            model.removeAllRows();

            Iterator tierIt = tiers.iterator();

            while (tierIt.hasNext()) {
                TierImpl t = (TierImpl) tierIt.next();
                model.addRow(t);
            }

            tierTable.getSelectionModel().addListSelectionListener(this);
        }
    }

    /**
     * Empties and refills the Linguistic Type menu with types that are not
     * excluded by the current parent tier choice.
     */
    private void fillLingTypeMenu() {
        lingTypeComboBox.removeItemListener(this);
        lingTypeComboBox.removeAllItems();

        TierImpl parentTier = (TierImpl) (transcription.getTierWithId((String) parentComboBox.getSelectedItem()));

        boolean excludeTimeSubDiv = false;

        Constraint parentConstraint = null;

        if (parentTier != null) {
            parentConstraint = parentTier.getLinguisticType().getConstraints();

            if (parentConstraint != null) {
                if ((parentConstraint.getStereoType() == Constraint.SYMBOLIC_SUBDIVISION) ||
                        (parentConstraint.getStereoType() == Constraint.SYMBOLIC_ASSOCIATION)) {
                    excludeTimeSubDiv = true;
                }
            }
        }

        Enumeration e = transcription.getLinguisticTypes().elements();

        while (e.hasMoreElements()) {
            LinguisticType lt = (LinguisticType) e.nextElement();
            String ltName = lt.getLinguisticTypeName();

            if (excludeTimeSubDiv && (lt.getConstraints() != null) &&
                    ((lt.getConstraints().getStereoType() == Constraint.TIME_SUBDIVISION) ||
                    (lt.getConstraints().getStereoType() == Constraint.INCLUDED_IN))) {
                continue;
            }

            if (parentTier == null) { // only unconstrained types

                if (lt.getConstraints() != null) {
                    continue;
                }
            }

            if (parentTier != null) { // only constrained types

                if (lt.getConstraints() == null) {
                    continue;
                }
            }

            lingTypeComboBox.addItem(ltName);
        }

        // set selected the current type of the selected tier
        String tierName = (String) currentTiersComboBox.getSelectedItem();

        if (tierName != null) {
            TierImpl tier = (TierImpl) transcription.getTierWithId(tierName);

            if (tier != null) {
                LinguisticType type = tier.getLinguisticType();

                if (type != null) {
                    lingTypeComboBox.setSelectedItem(type.getLinguisticTypeName());
                }
            }
        }

        lingTypeComboBox.addItemListener(this);

        if (lingTypeComboBox.getModel().getSize() <= 0) {
            changeButton.setEnabled(false);
        } else {
            changeButton.setEnabled(true);
        }
    }

    /**
     * Fills the parent tier combobox with the potential parent tiers for the
     * specified tier.
     */
    private void fillParentComboBox() {
        parentComboBox.removeItemListener(this);
        parentComboBox.removeAllItems();
        parentComboBox.addItem(none);

        if ((tier != null) && (mode != ADD)) {
            Vector candidateParents = transcription.getCandidateParentTiers(tier);
            Iterator pIter = candidateParents.iterator();

            while (pIter.hasNext()) {
                parentComboBox.addItem(((Tier) pIter.next()).getName());
            }

            if (tier.hasParentTier()) {
                parentComboBox.setSelectedItem(tier.getParentTier().getName());
            }
        } else if (mode == ADD) {
            Iterator tierIt = tiers.iterator();

            while (tierIt.hasNext()) {
                TierImpl t = (TierImpl) tierIt.next();
                parentComboBox.addItem(t.getName());
            }

            parentComboBox.setSelectedItem(none);
        }

        parentComboBox.addItemListener(this);
    }

    /**
     * Gets the Locale of the currently selected tier and tries to set this
     * Locale as the selected item in the language combo box.<br>
     * If the tier has a Locale that is not in the list, add it to the list.
     */
    private void updateLanguageComboBox() {
        languageComboBox.removeAllItems();

        if (langs != null) {
            for (int i = 0; i < langs.length; i++) {
                languageComboBox.addItem(langs[i].getDisplayName());
            }
        }

        if (tier != null) {
            Locale l = tier.getDefaultLocale();

            if (l != null) {
                /*
                   List al = Arrays.asList(langs);
                   if (!al.contains(l)) {
                       languageComboBox.addItem(l.getDisplayName());
                   }
                 */
                languageComboBox.setSelectedItem(l.getDisplayName());
            } else {
                languageComboBox.setSelectedIndex(0);
            }
        }
    }

    /**
     * Updates ui elements for a certain selected tier, e.g. after committing a
     * change to the set of tiers.
     *
     * @param name the name of the selected tier
     */
    private void updateUIForTier(String name) {
        if (name != null) {
            tier = (TierImpl) transcription.getTierWithId(name);

            if (tier != null) {
                if (currentTiersComboBox.getSelectedItem() != tier.getName()) {
                    currentTiersComboBox.setSelectedItem(name);
                }

                oldParentTier = (TierImpl) tier.getParentTier();

                if (oldParentTier != null) {
                    oldParentTierName = tier.getParentTier().getName();
                } else {
                    oldParentTierName = none;
                }

                oldLingType = tier.getLinguisticType();
                oldLocale = tier.getDefaultLocale();
                oldTierName = tier.getName();
                oldParticipant = tier.getParticipant();

                if (mode != ADD) {
                    tierNameTextField.setText(oldTierName);
                    participantTextField.setText(oldParticipant);

                    if (mode == CHANGE) {
                        if (tier.getAnnotations().size() == 0) {
                            parentComboBox.setEnabled(true);
                        }
                    }
                }
            }

            fillParentComboBox();
            fillLingTypeMenu();
            updateLanguageComboBox();

            // update table
            if (model != null) {
                tierTable.getSelectionModel().removeListSelectionListener(this);

                int col = model.findColumn(TierTableModel.NAME);

                for (int i = 0; i < model.getRowCount(); i++) {
                    if (name.equals(model.getValueAt(i, col))) {
                        tierTable.getSelectionModel().setLeadSelectionIndex(i);

                        break;
                    }
                }

                tierTable.getSelectionModel().addListSelectionListener(this);
            }
        } else {
            fillParentComboBox();
            fillLingTypeMenu();
        }
    }

    /**
     * Adds a new tier to the transcription.
     *
     * @param tierName the name of the new tier
     * @param parentTier the parent tier, can be null
     * @param lingType the Linguistic Type name
     * @param participant the participant value for the tier
     * @param locale the default language for the tier
     */
    private void doAdd(String tierName, Tier parentTier, String lingType,
        String participant, Locale locale) {
        Command c = ELANCommandFactory.createCommand(transcription,
                ELANCommandFactory.ADD_TIER);

        Object receiver = transcription;
        Object[] args = new Object[5];
        args[0] = tierName;
        args[1] = parentTier;
        args[2] = lingType;
        args[3] = participant;
        args[4] = locale;

        c.execute(receiver, args);

        // update the dialog ui
        reextractTiers();
        updateUIForTier(null);

        // warn if this is the first tier with a linguistic type 
        // allowing graphic refs.
        TierImpl tt = (TierImpl) transcription.getTierWithId(tierName);

        if ((tt != null) && tt.getLinguisticType().hasGraphicReferences()) {
            checkGraphics(tt);
        }
    }

    /**
     * Changes properties of a tier.
     *
     * @param tierName new name of the tier
     * @param parentTier the parent tier
     * @param lingType the linguistic type
     * @param participant the participant
     * @param locale the locale
     */
    private void doChange(String tierName, Tier parentTier, String lingType,
        String participant, Locale locale) {
        // double check on parent and type
        if (tier.getAnnotations().size() > 0) {
            if (((parentTier != null) && (oldParentTier == null)) ||
                    ((parentTier == null) && (oldParentTier != null)) ||
                    (parentTier != oldParentTier)) {
                parentTier = oldParentTier;
            }

            if (getStereoTypeForTypeName(lingType) != getStereoTypeForType(
                        oldLingType)) {
                lingType = oldLingType.getLinguisticTypeName();
            }
        }

        // check whether something has changed
        if (!tierName.equals(oldTierName) ||
                ((parentTier != null) && (oldParentTier != null) &&
                (parentTier != oldParentTier)) ||
                !lingType.equals(oldLingType.getLinguisticTypeName()) ||
                !participant.equals(oldParticipant) ||
                ((locale != null) && (locale != oldLocale))) {
            Command c = ELANCommandFactory.createCommand(transcription,
                    ELANCommandFactory.CHANGE_TIER);

            Object receiver = tier;
            Object[] args = new Object[5];
            args[0] = tierName;
            args[1] = parentTier;
            args[2] = lingType;
            args[3] = participant;
            args[4] = locale;

            c.execute(receiver, args);

            // notify if this is the first tier allowing graphic references
            if (tier.getLinguisticType().hasGraphicReferences() &&
                    !lingType.equals(oldLingType.getLinguisticTypeName())) {
                checkGraphics(tier);
            }

            if (singleEditMode) {
                // dispose
                dispose();
            } else {
                // update the dialog ui
                reextractTiers();
            }
        } else {
            //System.out.println("no change");
        }
    }

    /**
     * Actually deletes the tier.
     */
    private void doDelete() {
        if (tier != null) {
            Vector depTiers = tier.getDependentTiers();
            StringBuffer mesBuf = new StringBuffer();
            mesBuf.append(ElanLocale.getString(
                    "EditTierDialog.Message.ConfirmDelete"));
            mesBuf.append("  ");
            mesBuf.append(oldTierName);
            mesBuf.append(" ?\n");

            if ((depTiers != null) && (depTiers.size() > 0)) {
                mesBuf.append(ElanLocale.getString(
                        "EditTierDialog.Message.AlsoDeleted"));

                Iterator depIt = depTiers.iterator();

                while (depIt.hasNext()) {
                    Tier t = (Tier) depIt.next();
                    mesBuf.append("\n-   ");

                    mesBuf.append(t.getName());
                }
            }

            int option = JOptionPane.showConfirmDialog(this, mesBuf.toString(),
                    ElanLocale.getString("Message.Warning"),
                    JOptionPane.YES_NO_OPTION);

            if (option == JOptionPane.YES_OPTION) {
                Object[] args = new Object[] { tier };
                Command c = ELANCommandFactory.createCommand(transcription,
                        ELANCommandFactory.DELETE_TIER);
                c.execute(transcription, args);

                if (singleEditMode) {
                    // dispose
                    dispose();
                } else {
                    // update the dialog ui
                    reextractTiers();
                }
            }
        }
    }

    /**
     * Imports tiers (without annotations) from an eaf or etf.
     */
    private void doImport() {
        String fileName = importSourceTF.getText();

        if (!isValidFile(fileName)) {
            JOptionPane.showMessageDialog(this,
                ElanLocale.getString("EditTierDialog.Message.SelectValid"),
                ElanLocale.getString("Message.Error"), JOptionPane.ERROR_MESSAGE);

            return;
        }

        Command c = ELANCommandFactory.createCommand(transcription,
                ELANCommandFactory.IMPORT_TIERS);
        c.execute(transcription, new Object[] { fileName });

        reextractTiers();
        updateUIForTier(null);
    }

    /**
     * Prompts the user to browse to an eaf or etf file, checks a little  and
     * updates the ui.
     */
    private void promptForImportFile() {
        String eafDir = (String) Preferences.get("LastUsedEAFDir", null);

        if (eafDir == null) {
            eafDir = System.getProperty("user.dir");
        }

        JFileChooser chooser = new JFileChooser();

        // the file chooser is not really part of the localisation
        chooser.setApproveButtonText("Select");
        chooser.setCurrentDirectory(new File(eafDir));
        chooser.setDialogTitle(ElanLocale.getString(
                "EditTierDialog.Title.Select"));

        File eafFile = null;
        FileFilter filter = ElanFileFilter.createFileFilter(ElanFileFilter.EAF_TYPE);
        chooser.setAcceptAllFileFilterUsed(false);
        chooser.setFileFilter(filter);
        chooser.addChoosableFileFilter(ElanFileFilter.createFileFilter(
                ElanFileFilter.TEMPLATE_TYPE));

        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            File curDir = chooser.getCurrentDirectory();

            if (curDir != null) {
                Preferences.set("LastUsedEAFDir", curDir.getAbsolutePath(), null);
            }

            eafFile = chooser.getSelectedFile();

            if (eafFile != null) {
                String name = eafFile.getAbsolutePath();

                if (isValidFile(name)) {
                    importSourceTF.setText(name);
                }
            }
        }
    }

    /**
     * Checks if a filename points to an exisitng .eaf or .etf file.
     *
     * @param fileName a String representation of a file
     *
     * @return true if the file exists and is an .eaf or .rtf, false otherwise
     */
    private boolean isValidFile(String fileName) {
        if (fileName == null) {
            return false;
        }

        File f = new File(fileName);

        if (!f.exists()) {
            return false;
        }

        String lowerPathName = fileName.toLowerCase();

        String[] exts = FileExtension.EAF_EXT;

        for (int i = 0; i < exts.length; i++) {
            if (lowerPathName.endsWith("." + exts[i])) {
                return true;
            }
        }

        exts = FileExtension.TEMPLATE_EXT;

        for (int i = 0; i < exts.length; i++) {
            if (lowerPathName.endsWith("." + exts[i])) {
                return true;
            }
        }

        return false;
    }

    /**
     * Checks whether this is the first tier with a linguistic type that allows
     * graphic annotations. Shows a warning message if so.
     *
     * @param ti the changed or new tier
     */
    private void checkGraphics(TierImpl ti) {
        int numGraphicTiers = 1;

        Vector tiers = transcription.getTiers();
        Iterator tierIt = tiers.iterator();
        TierImpl t;

        while (tierIt.hasNext()) {
            t = (TierImpl) tierIt.next();

            if ((t != ti) && t.getLinguisticType().hasGraphicReferences()) {
                numGraphicTiers++;
            }
        }

        if ((numGraphicTiers == 1) &&
                ((transcription.getSVGFile() == null) ||
                (transcription.getSVGFile().length() == 0))) {
            JOptionPane.showMessageDialog(this,
                ElanLocale.getString("EditTierDialog.Message.Graphics"),
                ElanLocale.getString("Message.Warning"),
                JOptionPane.WARNING_MESSAGE);
        }
    }

    /**
     * Returns the stereotype for a linguistic type. When the linguistic type
     * has no constraints -1 is returned.
     *
     * @param name type name
     *
     * @return the stereotype or -1
     */
    private int getStereoTypeForTypeName(String name) {
        LinguisticType type = null;

        Vector types = transcription.getLinguisticTypes();
        LinguisticType tempType = null;

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

            if (tempType.getLinguisticTypeName().equals(name)) {
                type = tempType;

                break;
            }
        }

        return getStereoTypeForType(type);
    }

    /**
     * Returns the stereotype for a linguistic type. When the linguistic type
     * has no constraints -1 is returned.
     *
     * @param type type
     *
     * @return the stereotype or -1
     */
    private int getStereoTypeForType(LinguisticType type) {
        if ((type == null) || (type.getConstraints() == null)) {
            return -1;
        } else {
            return type.getConstraints().getStereoType();
        }
    }

    /* (non-Javadoc)
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    public void actionPerformed(ActionEvent event) {
        if (event.getSource() == changeButton) {
            if (mode == DELETE) {
                doDelete();

                return;
            } else if (mode == IMPORT) {
                doImport();

                return;
            } else {
                String tierName = tierNameTextField.getText();
                tierName.replace('\n', ' ');
                tierName.trim();

                if (tierName.length() == 0) {
                    tierNameTextField.requestFocus();
                    JOptionPane.showMessageDialog(this,
                        ElanLocale.getString("EditTierDialog.Message.TierName"),
                        ElanLocale.getString("Message.Error"),
                        JOptionPane.ERROR_MESSAGE);

                    return;
                }

                if (transcription.getTierWithId(tierName) != null) {
                    if ((mode == ADD) ||
                            ((mode == CHANGE) && !tierName.equals(oldTierName))) {
                        tierNameTextField.requestFocus();
                        JOptionPane.showMessageDialog(this,
                            ElanLocale.getString(
                                "EditTierDialog.Message.Exists"),
                            ElanLocale.getString("Message.Error"),
                            JOptionPane.ERROR_MESSAGE);

                        return;
                    }
                }

                String participant = participantTextField.getText();
                String lingType = (String) lingTypeComboBox.getSelectedItem();
                Tier parentTier = transcription.getTierWithId((String) parentComboBox.getSelectedItem());

                String localeName = (String) languageComboBox.getSelectedItem();
                Locale locale = null;

                if (langs != null) {
                    for (int i = 0; i < langs.length; i++) {
                        if (langs[i].getDisplayName().equals(localeName)) {
                            locale = langs[i];

                            break;
                        }
                    }
                }

                if (locale == null) {
                    locale = oldLocale;
                }

                switch (mode) {
                case ADD:
                    doAdd(tierName, parentTier, lingType, participant, locale);

                    break;

                case CHANGE:
                    doChange(tierName, parentTier, lingType, participant, locale);

                    break;
                }
            }

            //dispose();
        } else if (event.getSource() == importSourceButton) {
            promptForImportFile();
        } else {
            dispose();
        }
    }

    /**
     * ComboBox selection changes.
     *
     * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent)
     */
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            if (e.getSource() == currentTiersComboBox) {
                String name = (String) currentTiersComboBox.getSelectedItem();
                updateUIForTier(name);
            } else if ((e.getSource() == lingTypeComboBox) && (mode == CHANGE)) {
                if ((tier != null) && (tier.getNumberOfAnnotations() > 0)) {
                    // warn if more than 0 annotations and stereotype is different
                    String newTypeName = (String) e.getItem();
                    boolean stereoTypeChanged = false;
                    int newStereoType = getStereoTypeForTypeName(newTypeName);
                    int oldStereoType = getStereoTypeForType(oldLingType);

                    if (newStereoType != oldStereoType) {
                        stereoTypeChanged = true;
                    }

                    if (!oldLingType.getLinguisticTypeName().equals(newTypeName) &&
                            stereoTypeChanged) {
                        StringBuffer buf = new StringBuffer(ElanLocale.getString(
                                    "EditTierDialog.Message.RecommendType"));
                        buf.append("\n");
                        buf.append(ElanLocale.getString(
                                "EditTierDialog.Message.Corrupt"));

                        JOptionPane.showMessageDialog(this, buf.toString(),
                            ElanLocale.getString("Message.Warning"),
                            JOptionPane.WARNING_MESSAGE);

                        // HS sep-04 prevent changing the lin. type when there are annotations   
                        lingTypeComboBox.setSelectedItem(oldLingType.getLinguisticTypeName());
                    }
                }
            } else if (e.getSource() == parentComboBox) {
                if ((mode == CHANGE) && (tier != null) &&
                        (tier.getNumberOfAnnotations() > 0)) {
                    if (!(oldParentTierName.equals((String) e.getItem()))) {
                        StringBuffer buf = new StringBuffer(ElanLocale.getString(
                                    "EditTierDialog.Message.RecommendParent"));
                        buf.append("\n");
                        buf.append(ElanLocale.getString(
                                "EditTierDialog.Message.Corrupt"));
                        JOptionPane.showMessageDialog(this, buf.toString(),
                            ElanLocale.getString("Message.Warning"),
                            JOptionPane.WARNING_MESSAGE);

                        // HS sep-04 prevent changing the parent when there are annotations    
                        parentComboBox.setSelectedItem(oldParentTierName);
                    }
                } else {
                    // suggest the participant name from the parent...
                    String partiName = participantTextField.getText();

                    if ((partiName == null) ||
                            (partiName.trim().length() == 0)) {
                        TierImpl parent = (TierImpl) transcription.getTierWithId((String) e.getItem());

                        if (parent != null) {
                            participantTextField.setText(parent.getParticipant());
                        }
                    }
                }

                fillLingTypeMenu();
            }
        }
    }

    /**
     * Add the editpanel to the selected tab
     *
     * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
     */
    public void stateChanged(ChangeEvent e) {
        tabPane.removeChangeListener(this);
        mode = tabPane.getSelectedIndex();
        tabPane.removeAll();
        tabPane.addTab(ElanLocale.getString("Button.Add"), null);
        tabPane.addTab(ElanLocale.getString("Button.Change"), null);
        tabPane.addTab(ElanLocale.getString("Button.Delete"), null);
        tabPane.addTab(ElanLocale.getString("Button.Import"), importPanel);

        if (mode < IMPORT) {
            tabPane.setComponentAt(tabPane.getSelectedIndex(), editPanel);
        } else {
            tabPane.setComponentAt(1, editPanel);
        }

        tabPane.setSelectedIndex(mode);
        updateForMode();

        //editPanel.revalidate();
        tabPane.revalidate();
        tabPane.addChangeListener(this);

        if ((mode == CHANGE) || (mode == DELETE)) {
            if (currentTiersComboBox.getItemCount() > 0) {
                String name = (String) currentTiersComboBox.getSelectedItem();
                updateUIForTier(name);
            }
        } else if (mode == ADD) {
            if (parentComboBox.getItemCount() > 0) {
                parentComboBox.setSelectedIndex(0);
            }
        }
    }

    public void valueChanged(ListSelectionEvent e) {
        if (mode == ADD) {
            return;
        }

        int row = tierTable.getSelectedRow();

        if (row > -1) {
            int column = model.findColumn(TierTableModel.NAME);
            updateUIForTier((String) model.getValueAt(row, column));
        }
    }
}
