/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.cdm;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.DebugStream;
import org.greenstone.gatherer.Dictionary;
import org.greenstone.gatherer.Gatherer;
import org.greenstone.gatherer.gui.DesignPaneHeader;
import org.greenstone.gatherer.gui.GLIButton;
import org.greenstone.gatherer.util.StaticStrings;
import org.greenstone.gatherer.util.Utility;

/** This class provides a graphical interface to allow a user to quickly and conviently (ie all in one place) translate the text fragments associated with general metadata and indexes into each of the assigned languages in the collection. It should provide clear controls for the editing of these text fragments, plus clear indicate what languages still need further translation, which it will do through a combination of coloring and other visual indicators.
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class TranslationView {

    private Control controls;

    static final private Dimension COMPONENT_SIZE = new Dimension(225,25);
    static final private int LANGUAGE_WIDTH = 75;

    public TranslationView() {
	DebugStream.println("TranslationView: Initialized.");
    }

    public void destroy() {
	if(controls != null) {
	    controls.destroy();
	    controls = null;
	}
    }

    public Control getControls() {
	if(controls == null) {
	    controls = new TranslationControl();
	}
	return controls;
    }

    /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden
     * @param mode the new mode as an int
     */
    public void modeChanged(int mode) {

    }

    private CollectionMeta newCollOrSearchMeta(String name, String type, String language) {
	return newCollOrSearchMeta(name, type, language, false);
    }

    private CollectionMeta newCollOrSearchMeta(String name, String type, String language, boolean dummy) {
	CollectionMeta meta;
	if (type != null) {
	    meta = new SearchMeta(name, type, language, dummy);
	} else {
	    meta = new CollectionMeta(name, language, dummy);
	}
	return meta;


    }
    private TreeSet getLanguages() {
	TreeSet lang_set = CollectionDesignManager.collectionmeta_manager.getLanguages();
	lang_set.addAll(CollectionDesignManager.searchmeta_manager.getLanguages());
	return lang_set;
    }
    private Object[] getFeaturesList() {
	ArrayList features_model = new ArrayList();

	// add general metadata
	ArrayList general_metadata = CollectionDesignManager.collectionmeta_manager.getMetadata();
	for(int i = 0; i < general_metadata.size(); i++) {
	    CollectionMeta metadata = (CollectionMeta) general_metadata.get(i);
	    if(!metadata.isSpecial()) {
		Object bob = new BobTheMagicalComparableWrapper(metadata.getName(), null);
		if(!features_model.contains(bob)) {
		    features_model.add(bob);
		}
	    }
	}

	// Add the indexes
	ArrayList indexes = CollectionDesignManager.index_manager.getIndexes();
	int indexes_size = indexes.size();
	for(int j = 0; j < indexes_size; j++) {
	    Object bob = new BobTheMagicalComparableWrapper(indexes.get(j), SearchMeta.TYPE_INDEX);
	    if(!features_model.contains(bob)) {
		features_model.add(bob);
	    }
	}
	// Add the levels, if any
	ArrayList levels = CollectionDesignManager.index_manager.getLevels();
	if (levels !=null) {
	  int levels_size = levels.size();
	  for (int j=0; j<levels.size(); j++) {
	      Object bob = new BobTheMagicalComparableWrapper(levels.get(j), SearchMeta.TYPE_LEVEL);
	    if(!features_model.contains(bob)) {
	      features_model.add(bob);
	    }
	  }
	}
	// Add the sortfields, if any
	ArrayList sortfields = CollectionDesignManager.index_manager.getSortFields();
	if (sortfields !=null) {
	  int sort_size = sortfields.size();
	  for (int j=0; j<sort_size; j++) {
	      Object bob = new BobTheMagicalComparableWrapper(sortfields.get(j), SearchMeta.TYPE_SORT);
	    if(!features_model.contains(bob)) {
	      features_model.add(bob);
	    }
	  }
	}
	// Add the facets, if any
	ArrayList facets = CollectionDesignManager.index_manager.getFacets();
	if (facets !=null) {
	  int facet_size = facets.size();
	  for (int j=0; j<facet_size; j++) {
	      Object bob = new BobTheMagicalComparableWrapper(facets.get(j), SearchMeta.TYPE_FACET);
	    if(!features_model.contains(bob)) {
	      features_model.add(bob);
	    }
	  }
	}
	// We also add subindexes.
	ArrayList subindexes = CollectionDesignManager.subcollectionindex_manager.getSubcollectionIndexes();
	int subindexes_size = subindexes.size();
	for(int j = 0; j < subindexes_size; j++) {
	    Object bob = new BobTheMagicalComparableWrapper(subindexes.get(j), SearchMeta.TYPE_PARTITION);
	    if(!features_model.contains(bob)) {
		features_model.add(bob);
	    }
	}

	ArrayList languages = CollectionDesignManager.language_manager.getLanguages();
	int languages_size = languages.size();
	for(int j = 0; j < languages_size; j++) {
	    Object bob = new BobTheMagicalComparableWrapper(languages.get(j), SearchMeta.TYPE_LANGUAGE);
	    if(!features_model.contains(bob)) {
		features_model.add(bob);
	    }
	}
	
	//Collections.sort(features_model); we want them in this order I reckon

	return features_model.toArray();
    }

    private class BobTheMagicalComparableWrapper
	implements Comparable {
	private Object content;
	private String text;
	private String type;
	
	BobTheMagicalComparableWrapper(Object content, String type) {
	    this.content = content;
	    if (type != null) {
		if (Gatherer.GS3) {
		    this.type = type;
		} else {
		    this.type = SearchMeta.TYPE_SEARCH;
		}
	    }
	}
	public int compareTo(Object object) {
	    if(object == null) {
		object = "";
	    }
	    if(text == null) {
		toString();
	    }
	    return text.compareTo(object.toString());
	}

	/** Equals is used by contains and since we want to prevent double up of metadata we compare differently than in compareTo. */
	public boolean equals(Object object) {
	    BobTheMagicalComparableWrapper bob = (BobTheMagicalComparableWrapper)object;
	    return (object != null && content.toString().equals(bob.getContent().toString()) && ((type == null && bob.getType()==null) || type.equals(bob.getType())));
	}

	public Object getContent() {
	    return content;
	}

	    public String getName() {
		if (content instanceof IndexOption) {
		   return ((IndexOption)content).getName();
		}
		if (content instanceof Index) {
		    return ((Index)content).getID();
		}
		if (content instanceof SubcollectionIndex) {
		    return ((SubcollectionIndex)content).getID();
		}
		return content.toString();
	    }

	    public String getType() {
		return type;
	    }

	public String toString() {

	    if(text == null) {
		String temp = content.toString();
		if (type != null) {
		    text = Dictionary.get("CDM.SearchMetadataManager.Type_"+type)+": "+temp;
		} else {
		    text = Dictionary.get("CDM.TranslationManager.Type_general")+": "+temp;
		}
	    }
	    return text;
	}
    }

    private class TranslationControl
	extends JPanel 
	implements Control {

	private boolean ignore_event = false;
	private FragmentTableModel fragment_table_model;
	private JButton add_button;
	private JButton remove_button;
	private JButton replace_button;
	private JComboBox language_combobox;
	private JList features_list;
	private JTable fragment_table;
	private JTextArea default_area;
	private JTextArea translation_area;

	TranslationControl() {
            this.setComponentOrientation(Dictionary.getOrientation());
	    fragment_table_model = new FragmentTableModel("", "", new TreeSet(), new ArrayList());
	    // Creation
	    JPanel header_panel = new DesignPaneHeader("CDM.GUI.Translation", "translatetext");

	    JPanel center_panel = new JPanel();
            center_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel selection_panel = new JPanel();
            selection_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel component_selection_panel = new JPanel();
            component_selection_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel component_label = new JLabel(Dictionary.get("CDM.TranslationManager.Affected_Features"));
            component_label.setComponentOrientation(Dictionary.getOrientation());
            
	    Object[] features = getFeaturesList();
	    features_list = new JList(features);
	    features_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
            if (features != null && features.length >0){
		features_list.setSelectedIndex(0);
	    }

	    JPanel fragment_selection_panel = new JPanel();
	    fragment_selection_panel.setComponentOrientation(Dictionary.getOrientation());
            JLabel fragment_label = new JLabel(Dictionary.get("CDM.TranslationManager.Assigned_Fragments"));
            fragment_label.setComponentOrientation(Dictionary.getOrientation());
	    
	    fragment_table = new JTable(fragment_table_model);
            fragment_table.setComponentOrientation(Dictionary.getOrientation());
	    fragment_table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
	    fragment_table.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	    fragment_table.setColumnSelectionAllowed(false);
	    fragment_table.setDefaultRenderer(Object.class, new FragmentTableRenderer());
	    fragment_table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

	    JScrollPane table_scroll = new JScrollPane(fragment_table);
	    table_scroll.getViewport().setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	    table_scroll.setOpaque(true);

	    JPanel south_panel = new JPanel();
            south_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel text_panel = new JPanel();
            text_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel language_panel = new JPanel();
            language_panel.setComponentOrientation(Dictionary.getOrientation());
            
	    JLabel language_label = new JLabel(Dictionary.get("CDM.TranslationManager.Language"));
	    language_label.setComponentOrientation(Dictionary.getOrientation());
            
	    language_combobox = new JComboBox(CollectionDesignManager.language_manager.getLanguageCodes().toArray());
	    language_combobox.setOpaque(!Utility.isMac());
	    language_combobox.setPreferredSize(COMPONENT_SIZE);
	    language_combobox.setRenderer(new LanguageListCellRenderer());
	    language_combobox.setToolTipText(Dictionary.get("CDM.TranslationManager.Language_Tooltip"));
	    language_combobox.setComponentOrientation(Dictionary.getOrientation());
            
	    JPanel default_text_panel = new JPanel();
            default_text_panel.setComponentOrientation(Dictionary.getOrientation());
	    JLabel default_label = new JLabel(Dictionary.get("CDM.TranslationManager.Default_Text"));
	    default_label.setComponentOrientation(Dictionary.getOrientation());
	    default_area = new JTextArea();
            default_area.setComponentOrientation(Dictionary.getOrientation());
	    default_area.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
	    default_area.setEditable(false);
	    default_area.setLineWrap(true);
	    default_area.setWrapStyleWord(true);

	    JPanel translated_text_panel = new JPanel();
            translated_text_panel.setComponentOrientation(Dictionary.getOrientation());
	    JLabel translation_label = new JLabel(Dictionary.get("CDM.TranslationManager.Translation"));
            translation_label.setComponentOrientation(Dictionary.getOrientation());
	    translation_area = new JTextArea();
            translation_area.setComponentOrientation(Dictionary.getOrientation());
	    translation_area.setBackground(Configuration.getColor("coloring.disabled", false));
	    translation_area.setEnabled(false);
	    translation_area.setLineWrap(true);
	    translation_area.setWrapStyleWord(true);
	    translation_area.setToolTipText(Dictionary.get("CDM.TranslationManager.Translation_Tooltip"));

	    JPanel button_pane = new JPanel();
            button_pane.setComponentOrientation(Dictionary.getOrientation());
	    add_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Add"), Dictionary.get("CDM.TranslationManager.Add_Tooltip"));
	    add_button.setEnabled(false);

	    replace_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Replace"), Dictionary.get("CDM.TranslationManager.Replace_Tooltip"));
	    replace_button.setEnabled(false);

	    remove_button = new GLIButton(Dictionary.get("CDM.TranslationManager.Remove"), Dictionary.get("CDM.TranslationManager.Remove_Tooltip"));
	    remove_button.setEnabled(false);


	    // Connection
	    add_button.addActionListener(new AddListener());
	    remove_button.addActionListener(new RemoveListener());
	    replace_button.addActionListener(new ReplaceListener());
	    language_combobox.addActionListener(new LanguageActionListener());
	    translation_area.getDocument().addDocumentListener(new TranslationDocumentListener());
	    features_list.addListSelectionListener(new FeaturesListSelectionListener());
	    fragment_table.getSelectionModel().addListSelectionListener(new FragmentListSelectionListener());

	    // Layout
	    component_selection_panel.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
	    component_selection_panel.setLayout(new BorderLayout());
	    component_selection_panel.add(component_label, BorderLayout.NORTH);
	    component_selection_panel.add(new JScrollPane(features_list), BorderLayout.CENTER);

	    fragment_selection_panel.setLayout(new BorderLayout());
	    fragment_selection_panel.add(fragment_label, BorderLayout.NORTH);
	    fragment_selection_panel.add(table_scroll, BorderLayout.CENTER);

	    selection_panel.setLayout(new GridLayout(2,1,0,5));
	    selection_panel.add(component_selection_panel);
	    selection_panel.add(fragment_selection_panel);

	    default_text_panel.setLayout(new BorderLayout());
	    default_text_panel.add(default_label, BorderLayout.NORTH);
	    default_text_panel.add(new JScrollPane(default_area), BorderLayout.CENTER);

	    translated_text_panel.setLayout(new BorderLayout());
	    translated_text_panel.add(translation_label, BorderLayout.NORTH);
	    translated_text_panel.add(new JScrollPane(translation_area), BorderLayout.CENTER);

	    text_panel.setLayout(new GridLayout(1,2,5,0));
	    text_panel.add(default_text_panel);
	    text_panel.add(translated_text_panel);

	    language_panel.setLayout(new BorderLayout(5,0));
	    language_panel.add(language_label, BorderLayout.LINE_START);
	    language_panel.add(language_combobox, BorderLayout.CENTER);

	    south_panel.setLayout(new BorderLayout());
	    south_panel.add(language_panel, BorderLayout.NORTH);
	    south_panel.add(text_panel, BorderLayout.CENTER);

	    center_panel.setLayout(new GridLayout(2,1,0,5));
	    center_panel.add(selection_panel);
	    center_panel.add(south_panel);

	    button_pane.setBorder(BorderFactory.createEmptyBorder(2,0,0,0));
	    button_pane.setLayout(new GridLayout(1,3));
	    button_pane.add(add_button);
	    button_pane.add(replace_button);
	    button_pane.add(remove_button);

	    setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
	    setLayout(new BorderLayout());
	    add(header_panel, BorderLayout.NORTH);
	    add(center_panel, BorderLayout.CENTER);
	    add(button_pane, BorderLayout.SOUTH);
	}

	public void destroy() {
	}

	public void gainFocus() {
	    // Rebuild the features model, just incase features have been added or removed.
	    if(features_list != null) {
		Object selected_feature = features_list.getSelectedValue();
		features_list.setListData(getFeaturesList());
		if(selected_feature != null) {
		    features_list.setSelectedValue(selected_feature, true);
		}
	    }
	}

	public void loseFocus() {
	}


	private class AddListener
	    implements ActionListener {
	    
	    public void actionPerformed(ActionEvent event) {
		ignore_event = true;
		String translation_text = translation_area.getText();
		if(translation_text.length() > 0) {
		    // If there is no current fragment table selection, but translation text is not an empty string, then this is a new fragment. Create new collection metadata, refresh the fragment table, then ensure that the new metadata is selected. Remember that it is the selected metadata to which changes will be applied.
		    BobTheMagicalComparableWrapper selected_feature = (BobTheMagicalComparableWrapper)features_list.getSelectedValue();
		    String language = (String) language_combobox.getSelectedItem();
		    CollectionMeta metadatum = newCollOrSearchMeta(selected_feature.getName(), selected_feature.getType(), language); //new CollectionMeta(selected_feature.getName(), language);
		    metadatum.setValue(translation_text);
		    fragment_table.clearSelection();
		    ArrayList metadata;
		    if (metadatum instanceof SearchMeta) {
			System.err.println("adding meta to search" + metadatum);
			CollectionDesignManager.searchmeta_manager.addMetadatum(metadatum);
		    
			metadata = CollectionDesignManager.searchmeta_manager.getMetadata(selected_feature.getName(), selected_feature.getType());
                        System.err.println("metadata = "+metadata); 
		    } else {
			// just a general metadata
			System.err.println("adding meta to general");
			CollectionDesignManager.collectionmeta_manager.addMetadatum(metadatum);
		    
			metadata = CollectionDesignManager.collectionmeta_manager.getMetadata(selected_feature.getName());
		    }
                    System.err.println("new langues = "+getLanguages());
		    fragment_table_model.setData(selected_feature.getName(), selected_feature.getType(), getLanguages(), metadata);
		    int index = fragment_table_model.getMetadataIndexByLanguage(language);
		   System.err.println("I want to select the row " + index + " out of " + metadata.size());
		    fragment_table.setRowSelectionInterval(index, index);
		    metadatum = null;
		    language = null;
		    selected_feature = null;
		    translation_text = null;
		    remove_button.setEnabled(true);
		}
		add_button.setEnabled(false);
		replace_button.setEnabled(false);
		ignore_event = false;
	    }
	}

	private class RemoveListener
	    implements ActionListener {
	    
	    public void actionPerformed(ActionEvent event) {
		ignore_event = true;
		int index = -1;
		if((index = fragment_table.getSelectedRow()) != -1) {
		    CollectionMeta metadata = fragment_table_model.getMetadata(index);
		    if (metadata instanceof SearchMeta) {
		        CollectionDesignManager.searchmeta_manager.removeMetadata(metadata);	 
		    } else {
		        CollectionDesignManager.collectionmeta_manager.removeMetadata(metadata);
		    }
                    fragment_table_model.remove(metadata);
		    // If a remove occured then enable add
		    add_button.setEnabled(true);
		}
		// Either way disable replace and remove
		replace_button.setEnabled(false);
		remove_button.setEnabled(false);
		ignore_event = false;
	    }
	}

	private class ReplaceListener 
	    implements ActionListener {

	    public void actionPerformed(ActionEvent event) {
		ignore_event = true;
		int index = -1;
		if((index = fragment_table.getSelectedRow()) != -1) {
		    String translation_text = translation_area.getText();
		    // Get the selected collection metadata
		    CollectionMeta metadata = fragment_table_model.getMetadata(index);
		    // Update the fragment metadata and ask the table to repaint the appropriate row
		    metadata.setValue(translation_text);
		    fragment_table_model.fireTableRowsUpdated(index, index);
		    metadata = null;
		    remove_button.setEnabled(true);
		}
		// Either way disable replace
		add_button.setEnabled(false);
		replace_button.setEnabled(false);
		ignore_event = false;
	    }
	}

	private class FeaturesListSelectionListener
	    implements ListSelectionListener {
	    public void valueChanged(ListSelectionEvent event) {
		if(!event.getValueIsAdjusting() && !ignore_event) {
		    ///ystem.err.println("FeaturesListSelectionListener");
		    if(!features_list.isSelectionEmpty()) {
			// Determine the features name. Remember to remove our helpful source prefix (delimited by ':').
			BobTheMagicalComparableWrapper selected_feature = (BobTheMagicalComparableWrapper)features_list.getSelectedValue();
			// Retrieve the default language translation, or otherwise the first match, to be default text.
			CollectionMeta default_metadata;
			// Update the text fragment table.
			fragment_table.clearSelection();
			if (selected_feature.getType() != null) {
			    default_metadata = CollectionDesignManager.searchmeta_manager.getMetadatum(selected_feature.getName(), selected_feature.getType());
			    fragment_table_model.setData(selected_feature.getName(), selected_feature.getType(), getLanguages(), CollectionDesignManager.searchmeta_manager.getMetadata(selected_feature.getName()));
			} else {
			    default_metadata = CollectionDesignManager.collectionmeta_manager.getMetadatum(selected_feature.getName());
			    fragment_table_model.setData(selected_feature.getName(), selected_feature.getType(), getLanguages(), CollectionDesignManager.collectionmeta_manager.getMetadata(selected_feature.getName()));
			}
			if(default_metadata != null) {
			    default_area.setText(default_metadata.getValue(CollectionMeta.TEXT));
			}
			
			selected_feature = null;
			// Now we check whatever value is currently selected in the combobox to see if it is valid to add.
			String language_name = (String) language_combobox.getSelectedItem();
			int index = fragment_table_model.getMetadataIndexByLanguage(language_name);
			if(index != -1) {
			    CollectionMeta metadata = fragment_table_model.getMetadata(index);
			    fragment_table.setRowSelectionInterval(index, index);
			    if(metadata != null) {
				translation_area.setText(metadata.getValue(CollectionMeta.TEXT));
			    }
			    else {
				translation_area.setText("");
			    }
			    metadata = null;
			}
			else {
			    translation_area.setText("");
			}
			// Update and enable the text area
			translation_area.setEnabled(true);
			translation_area.setBackground(Configuration.getColor("coloring.editable_background", false));
		    }
		    else {
			default_area.setText("");
			fragment_table.clearSelection();
			fragment_table_model.setData("", "", new TreeSet(), new ArrayList());
			translation_area.setText("");
			// Update and disable the text area
			translation_area.setText("");
			translation_area.setEnabled(false);
			translation_area.setBackground(Configuration.getColor("coloring.disabled", false));
		    }
		}
	    }
	}

	private class FragmentListSelectionListener
	    implements ListSelectionListener {
	    public void valueChanged(ListSelectionEvent event) {
		if(!event.getValueIsAdjusting() && !ignore_event) {
		    ignore_event = true;
		    ///ystem.err.println("FragmentListSelectionListener");
		    int index = -1;
		    if((index = fragment_table.getSelectedRow()) != -1) {
			// A row has been selected. Retrieve the collection metadata.
			CollectionMeta metadatum = fragment_table_model.getMetadata(index);
			if(metadatum != null) {
			    // Update the combobox to show the current language.
			    String language = metadatum.getLanguage();
			    ///ystem.err.println("Searching for the language: " + language_name);
			    for(int i = 0; i < language_combobox.getItemCount(); i++) {
				if(language_combobox.getItemAt(i).toString().equals(language)) {
				    language_combobox.setSelectedIndex(i);
				}
			    }
			    translation_area.setText(metadatum.getValue(CollectionMeta.TEXT));
			    remove_button.setEnabled(!metadatum.isDummy());
			}
			else {
			    remove_button.setEnabled(false);
			}
		    }
		    else {
			translation_area.setText("");
			remove_button.setEnabled(false);
		    }
		    ignore_event = false;
		}
	    }
	}

	private class FragmentTableModel
	    extends AbstractTableModel {

	    private ArrayList metadata;
	    private String feature_name;
	    private String feature_type;
	    private TreeSet languages;

	    FragmentTableModel(String feature_name, String feature_type, TreeSet languages, ArrayList metadata) {
		this.feature_name = feature_name;
		this.feature_type = feature_type;
		this.languages = languages;
		this.metadata = metadata;
	    }
	    public int getRowCount() {
	       // The row count is equal to the maximum number of languages currently assigned in the collection.
		return languages.size();
	    }
	    public int getColumnCount() {
		return 2;
	    }

	    public String getColumnName(int column) {
		if (column == 0) {
		    return Dictionary.get("CDM.TranslationManager.Language_Column");
		}
		if (column == 1) {
		    return Dictionary.get("CDM.TranslationManager.Fragment_Column");
		}
		return "";
	    }

	    /** Attempt to retrieve the metadata associated with a certain row - however not all rows actually have metadata with them (say the French row where no metadata has been set).
	     * @param row the row number as an int
	     * @return the CollectionMeta requested, which may be a dummy metadata pair
	     */
	    public CollectionMeta getMetadata(int row) {
		// Determine what language we are talking about
		String language = null;
		Iterator language_iterator = languages.iterator();
		int current_row = 0;
		while(current_row != row && language_iterator.hasNext()) {
		    language_iterator.next();
		    current_row++;
		}
		if(current_row == row) {
		    language = (String) language_iterator.next();
		    return getMetadata(language);
		}
		language_iterator = null;
		return null;
	    }

	    public CollectionMeta getMetadata(String language) {
		// Attempt to retrieve metadata with that language
		for(int i = 0; i < metadata.size(); i++) {
		    CollectionMeta metadatum = (CollectionMeta) metadata.get(i);
		    if(metadatum.getLanguage().equals(language)) {
			return metadatum;
		    }
		}
		//return new CollectionMeta(feature_name, language, true);
		return newCollOrSearchMeta(feature_name, feature_type, language, true);
	    }

	    public int getMetadataIndexByLanguage(String language) {
		///ystem.err.println("Find the index for the language " + language + " (out of " + languages.size() + ")");
		// First we have to iterate to the correct place
		Iterator language_iterator = languages.iterator();
		int current_row = 0;
		while(language_iterator.hasNext()) {
		    String target = (String)language_iterator.next();
		    if(language.equals(target)) {
			///ystem.err.println("Matches " + target);
			return current_row;
		    }
		    else {
			///ystem.err.println("Doesn't match " + target);
			current_row++;
		    }
		}
		///ystem.err.println("No such language in model: -1");
		return -1;
	    }

	    public Object getValueAt(int row, int column) {
		if(0 <= row && row < languages.size()) {
		    // First we have to iterate to the correct place
		    Iterator language_iterator = languages.iterator();
		    int current_row = 0;
		    while(current_row != row && language_iterator.hasNext()) {
			language_iterator.next();
			current_row++;
		    }
		    if(current_row == row) {
			String language = (String)language_iterator.next();
			if(column == 0) {
			    return CollectionDesignManager.language_manager.getLanguageName(language);
			}
			else {
			    CollectionMeta metadatum = getMetadata(language);
			    return metadatum.getValue(CollectionMeta.TEXT);
			}
		    }
		    language_iterator = null;
		}
		return "#Error";
	    }

          // we can't remove by index, as the table index doesn't relate to the index in the metadata list
          public void remove(CollectionMeta meta) {
            metadata.remove(meta);
          }
          

	    public void setData(String feature_name, String feature_type, TreeSet languages, ArrayList metadata) {
		this.feature_name = feature_name;
		this.feature_type = feature_type;
		this.languages = languages;
		this.metadata = metadata;
		fireTableDataChanged();
	    }

	    public int size() {
		return metadata.size();
	    }
	}

	private class FragmentTableRenderer
	    extends DefaultTableCellRenderer {
	    /** Retrieve the component used to rubberstamp the table based on the given parameters.
	     * @param table The <strong>JTable</strong> to rendered.
	     * @param value The <strong>Object</strong> whose representation will be shown in the table row.
	     * @param isSelected <i>true</i> iff the column/row is selected.
	     * @param hasFocus <i>true</i> iff the column/row is in focus.
	     * @param row An <i>int</i> specifying the target row.
	     * @param column An <i>int</i> specifying the target column.
	     * @see org.greenstone.gatherer.cdm.Language
	     */
	    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
		JComponent component = (JComponent) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
		if(isSelected) {
		    component.setBackground(Configuration.getColor("coloring.workspace_heading_background", false));
		}
		else {
		    component.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
		}
		return component;
	    }
	}

	private class LanguageActionListener
	    implements ActionListener {
	    public void actionPerformed(ActionEvent event) {
		if(!ignore_event && features_list.getSelectedIndex()>-1) {
		    ignore_event = true;
		    ///ystem.err.println("LanguageActionListener");
		    // If the newly selected language value already exists in the fragment table, and that row isn't the one selected, then select that row the text area.
		    String language_name = language_combobox.getSelectedItem().toString();
		    int index = fragment_table_model.getMetadataIndexByLanguage(language_name);
		    if(index != -1) {
			CollectionMeta metadata = fragment_table_model.getMetadata(index);
			fragment_table.setRowSelectionInterval(index, index);
			translation_area.setText(metadata.getValue(CollectionMeta.TEXT));
		    }
		    else {
			fragment_table.clearSelection();
			translation_area.setText("");
		    }
		    // Ready the text area
		    translation_area.setEnabled(true);
		    translation_area.setBackground(Configuration.getColor("coloring.editable_background", false));
		    ignore_event = false;
		}
	    }
	}

	private class TranslationDocumentListener
	    implements DocumentListener {
	    /** Gives notification that an attribute or set of attributes changed. */
	    public void changedUpdate(DocumentEvent e) {
		update();
	    }
	    /** Gives notification that there was an insert into the document. */
	    public void insertUpdate(DocumentEvent e) {
		update();
	    }
	    /** Gives notification that a portion of the document has been removed. */
	    public void removeUpdate(DocumentEvent e) {
		update();
	    }
	    /** The text area has changed in some way. Given that this can only happed when we are editing or adding a text fragment we better respond appropriately. */
	    private void update() {
		String translation_text = translation_area.getText();
		// Determine if add should be enabled. You can only add if the current text fragment doesn't already exist in the fragment table for the given language
		String language = (String) language_combobox.getSelectedItem();
		CollectionMeta metadatum = fragment_table_model.getMetadata(language);
		add_button.setEnabled(translation_text.length() > 0 && (metadatum.isDummy() || fragment_table_model.getMetadataIndexByLanguage(language) == -1));
		language = null;
		// Determine if replace should be enabled. Replace is only enabled it the text fragment is different from the one in the current fragment table selection.
		if(fragment_table.getSelectedRowCount() > 0) {
		    replace_button.setEnabled(!metadatum.isDummy() && !translation_text.equals(fragment_table.getValueAt(fragment_table.getSelectedRow(), 1)));
		}
		// Nothing selected, nothing to replace
		else {
		    replace_button.setEnabled(false);
		}
		translation_text = null;
	    }
	}
    }
}
