/*
 * File:     SingleTierViewerPanel.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.viewer;

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

import mpi.eudico.client.annotator.grid.*;

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

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

import mpi.eudico.server.corpora.util.ACMEditEvent;
import mpi.eudico.server.corpora.util.ACMEditListener;

import java.awt.BorderLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JPanel;


/**
 * A panel that consist mainly of a ComboBox containing the names of tiers.
 * When the user selects a tier the connected
 * <code>SingleTierViewerPanel</code> receives a notification by the
 * ViewerManager.
 */
public class SingleTierViewerPanel extends JPanel implements ACMEditListener,
    ElanLocaleListener, ItemListener {
    /** This means no tier has been selected! */
    private final static String EMPTY_ITEM = "Empty";
    private ViewerManager2 viewerManager;
    private SingleTierViewer viewer;
    private JComboBox tierComboBox;
    private JCheckBox multiCheckBox;
    private Hashtable tierTable;
    private Tier currentTier;

    /**
     * Creates a new SingleTierViewerPanel instance
     *
     * @param viewerManager the ViewerManager
     */
    public SingleTierViewerPanel(ViewerManager2 viewerManager) {
        this.viewerManager = viewerManager;
        tierComboBox = new JComboBox();
        tierComboBox.setMaximumRowCount(Constants.COMBOBOX_VISIBLE_ROWS);
        multiCheckBox = new JCheckBox();
        tierTable = new Hashtable();
        fillComboBox();
        tierComboBox.addItemListener(this);
        multiCheckBox.addItemListener(this);
        setLayout(new BorderLayout());
    }

    /**
     * Connects a <code>SingleTierViewer</code> to this panel.
     *
     * @param viewer the connected viewer
     */
    public void setViewer(SingleTierViewer viewer) { // take care of removing an existing viewer?
        this.viewer = viewer;
        viewerManager.setTierForViewer(viewer, currentTier);

        if (viewer instanceof GridViewer) {
            JPanel jp = new JPanel();
            jp.setLayout(new BorderLayout());
            jp.add(multiCheckBox, BorderLayout.WEST);
            jp.add(tierComboBox, BorderLayout.CENTER);
            add(jp, BorderLayout.NORTH);
        } else {
            add(tierComboBox, BorderLayout.NORTH);
        }

        add((AbstractViewer) viewer, BorderLayout.CENTER);
    }

    /**
     * Getter for the viewer on this panel.
     *
     * @return the viewer
     *
     * @since oct 04 HS: for storage of preferences
     */
    public SingleTierViewer getViewer() {
        return viewer;
    }

    private void fillComboBox() {
        tierComboBox.removeAllItems();
        tierTable.clear();

        // add the empty tier
        tierComboBox.addItem(EMPTY_ITEM);

        try {
            Vector tiers = viewerManager.getTranscription().getTiers();
            int tiers_size = tiers.size();

            for (int i = 0; i < tiers_size; i++) {
                Tier tier = (Tier) tiers.elementAt(i);
                String tierName = tier.getName();
                tierComboBox.addItem(tierName);
                tierTable.put(tierName, tier);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Responds only to tier related operation.
     *
     * @param e the ACMEditEvent
     */
    public void ACMEdited(ACMEditEvent e) {
        switch (e.getOperation()) {
        case ACMEditEvent.ADD_TIER:

        // fallthrough		
        case ACMEditEvent.REMOVE_TIER:
            updateComboBox();

            break;

        case ACMEditEvent.CHANGE_TIER:

            if (e.getInvalidatedObject() instanceof TierImpl) {
                tierChanged((TierImpl) e.getInvalidatedObject());
            }

            break;

        default:
            return;
        }
    }

    /**
     * Action following a CHANGE_TIER ACMEditEvent. Update the combo box if
     * a tier name has been changed.
     *
     * @param tier the invalidated tier
     */
    private void tierChanged(TierImpl tier) {
        // if the current tier's name has changed, update the combo box
        if (tier == currentTier) {
            String curName = (String) tierComboBox.getSelectedItem();

            String newTierName = tier.getName();

            if (!newTierName.equals(curName)) {
                tierTable.remove(curName);
                tierTable.put(newTierName, currentTier);

                for (int i = 0; i < tierComboBox.getItemCount(); i++) {
                    if (tierComboBox.getItemAt(i).equals(curName)) {
                        tierComboBox.removeItemAt(i);
                        tierComboBox.insertItemAt(newTierName, i);
                        tierComboBox.setSelectedItem(newTierName);

                        break;
                    }
                }
            }
        } else {
            String newTierName = tier.getName();

            if (!tierTable.containsKey(newTierName)) {
                Iterator nameIt = tierTable.keySet().iterator();
                String name;

                while (nameIt.hasNext()) {
                    name = (String) nameIt.next();

                    if (tierTable.get(name) == tier) {
                        tierTable.remove(name);
                        tierTable.put(newTierName, tier);

                        for (int i = 0; i < tierComboBox.getItemCount(); i++) {
                            if (tierComboBox.getItemAt(i).equals(name)) {
                                tierComboBox.removeItemAt(i);
                                tierComboBox.insertItemAt(newTierName, i);

                                break;
                            }
                        }

                        break;
                    }
                }
            }
        }

        if (multiCheckBox.isSelected()) {
            // the tier hierarchy might have been changed, only relevant in 
            // multi tier mode
            updateComboBox();
        }
    }

    private void updateComboBox() {
        try {
            String strComboItem = (String) tierComboBox.getSelectedItem();

            if (!multiCheckBox.isSelected()) {
                fillComboBox();

                if (!tierTable.containsKey(strComboItem)) {
                    strComboItem = EMPTY_ITEM;
                }
            } else {
                fillComboBoxMulti();
                strComboItem = swapComboItemIfNecessary(strComboItem);
            }

            tierComboBox.setSelectedItem(strComboItem);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    private String swapComboItemIfNecessary(String strComboItem) {
        if (tierTable.containsKey(strComboItem)) {
            return strComboItem;
        }

        String tierName;
        TierImpl tier;

        Vector tiers = viewerManager.getTranscription().getTiers();
        int tiers_size = tiers.size();

        for (int i = 0; i < tiers_size; i++) {
            tier = (TierImpl) tiers.elementAt(i);
            tierName = tier.getName();

            if (strComboItem.equals(tierName)) {
                Tier tierParent = tier.getParentTier();

                if (tierParent != null) {
                    String parentName = tierParent.getName();

                    return parentName;
                }
            }
        }

        return EMPTY_ITEM;
    }

    /**
     * ItemListener method. Handles selection changes in the tier combobox and
     * the multi tier checkbox, if present.
     *
     * @param e the item event
     */
    public void itemStateChanged(ItemEvent e) {
        Object objSource = e.getSource();

        if (objSource == tierComboBox) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
                String selectedTierName = (String) tierComboBox.getSelectedItem();
                Tier tier = null;

                if (!selectedTierName.equals(EMPTY_ITEM)) {
                    tier = (Tier) tierTable.get(selectedTierName);
                }

                viewerManager.setTierForViewer(viewer, tier);
                currentTier = tier;
            }
        } else if (objSource == multiCheckBox) {
            if (viewer instanceof GridViewer) {
                if (!multiCheckBox.isSelected()) {
                    ((GridViewer) viewer).setMode(GridViewer.SINGLE_TIER_MODE);
                } else {
                    ((GridViewer) viewer).setMode(GridViewer.MULTI_TIER_MODE);
                }
            }

            updateComboBox(); // this results in a setTier() on the viewer
        }
    }

    private void fillComboBoxMulti() {
        tierComboBox.removeAllItems();
        tierTable.clear();
        tierComboBox.addItem(EMPTY_ITEM);

        try {
            Vector tiers = viewerManager.getTranscription().getTiers();
            int tiers_size = tiers.size();

            for (int i = 0; i < tiers_size; i++) {
                TierImpl tier = (TierImpl) tiers.elementAt(i);
                String parentName;
                LinguisticType lt = tier.getLinguisticType();
                Constraint c = lt.getConstraints();

                if (c != null) {
                    if (c.getStereoType() == Constraint.SYMBOLIC_ASSOCIATION) {
                        Tier tierParent = tier.getParentTier();
                        parentName = tierParent.getName();
                        addToCombo(parentName, tierParent);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void addToCombo(String parentName, Tier tierParent) {
        //only add if parent not yet in combobox
        String str = null;
        int combo_size = tierComboBox.getItemCount();

        for (int i = 0; i < combo_size; i++) {
            str = (String) tierComboBox.getItemAt(i);

            if (str != null) {
                if (parentName.equals(str)) {
                    return;
                }
            }
        }

        tierComboBox.addItem(parentName);
        tierTable.put(parentName, tierParent);
    }

    /**
     * Selects the tier with the specified name in the combobox and  updates
     * the viewer. Multimode is only relevant for the GridViewer.
     *
     * @param tierName the name of the tier
     *
     * @since oct 04 HS: for restoration of preferences
     */
    public void selectTier(String tierName) {
        if (tierName != null) {
            tierComboBox.setSelectedItem(tierName);
        }
    }

    /**
     * Returns the name of the selected tier.
     *
     * @return the name of the selected tier
     */
    public String getSelectedTierName() {
        String name = null;

        if (currentTier != null) {
            name = currentTier.getName();
        }

        return name;
    }

    /**
     * Returns whether or not the GridViewer is in multi tier mode, if the
     * viewer  is an instance of GridViewer.
     *
     * @return true when the viewer is a GridViewer and in multitier mode,
     *         false otherwise
     */
    public boolean isMultiTierMode() {
        if (viewer instanceof GridViewer && multiCheckBox.isSelected()) {
            return true;
        }

        return false;
    }

    /**
     * Changes the multi tier mode for the GridViewer. Ignored by all other
     * SingleTierViewers.
     *
     * @param multiMode when true the gridviewer switches to multi tier mode
     */
    public void setMultiTierMode(boolean multiMode) {
        if (viewer instanceof GridViewer) {
            multiCheckBox.setSelected(multiMode);
        }
    }

    /**
     * Update label(s).
     */
    public void updateLocale() {
        multiCheckBox.setToolTipText(ElanLocale.getString(
                "SingleTierViewerPanel.Label.MultiTier"));
    }
}
