/**
 *#########################################################################
 *
 * 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.
 *
 * <BR><BR>
 *
 * Author: John Thompson, Greenstone Digital Library, University of Waikato
 *
 * <BR><BR>
 *
 * Copyright (C) 1999 New Zealand Digital Library Project
 *
 * <BR><BR>
 *
 * 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.
 *
 * <BR><BR>
 *
 * 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.
 *
 * <BR><BR>
 *
 * 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.gui;

import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;
import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.util.Utility;

/**
 * @author John Thompson, Greenstone Digital Library, University of Waikato
 * @version 2.3
 */
public class GComboBox
    extends JComboBox {

    private Color background = null;
    private Color foreground = null;
    private Color editable_background = null;
    private Color editable_foreground = null;
    private Color selection_background = null;
    private Color selection_foreground = null;

    private boolean sort_objects = true;

    public GComboBox() {
	super();
	init();
	setOpaque(!Utility.isMac());
    }

    public GComboBox(boolean editable) {
	super();
	setOpaque(!Utility.isMac());
	setEditable(editable);
	init();

    }

    public GComboBox(ArrayList data) {
	super(data.toArray());
	setOpaque(!Utility.isMac());
	init();
    }

    public GComboBox(ArrayList data, boolean editable) {
	super(data.toArray());
	setOpaque(!Utility.isMac());
	setEditable(editable);
	init();
    }

    public GComboBox(ArrayList data, boolean editable, boolean sorted) {
	super(data.toArray());
	setOpaque(!Utility.isMac());
	setEditable(editable);
	setSorted(sorted);
	init();
    }

    public GComboBox(ComboBoxModel model) {
	super(model);
	setOpaque(!Utility.isMac());
	init();
    }

    public GComboBox(ComboBoxModel model, boolean editable) {
	super(model);
	setOpaque(!Utility.isMac());
	setEditable(editable);
	init();
    }

    public GComboBox(Object data[]) {
	super(data);
	setOpaque(!Utility.isMac());
	init();
    }

    public GComboBox(Object data[], boolean editable) {
	super(data);
	setOpaque(!Utility.isMac());
	setEditable(editable);
	init();
    }

    public GComboBox(Object data[], boolean editable, boolean sorted) {
	super(data);
	setOpaque(!Utility.isMac());
	setEditable(editable);
	setSorted(sorted);
	init();
    }

    public GComboBox(Vector data) {
	super(data);
	setOpaque(!Utility.isMac());
	init();
    }

    public GComboBox(Vector data, boolean editable) {
	super(data);
	setOpaque(!Utility.isMac());
	setEditable(editable);
	init();
    }

    public void setSorted(boolean sort) {
	sort_objects = sort;
    }
    public int add(Object object) {
	if (dataModel instanceof Model) {
	    return ((Model) dataModel).add(object);
	}
	else {
	    return -1;
	}
    }

    public Object get(int index) {
	return dataModel.getElementAt(index);
    }

    public void clear() {
	if (dataModel instanceof Model) {
	    ((Model) dataModel).clear();
	}
    }

    public void init() {
	Model model = new Model();
	ComboBoxModel old_model = (ComboBoxModel) getModel();
	setModel(model);
	setOpaque(true);

	// Restore any data given into our model
	for(int i = 0; i < old_model.getSize(); i++) {
	    model.add(old_model.getElementAt(i));
	}

	// Change component
	UI ui = new UI();
	setUI(ui);
	ui.setButtonBackground();

	// Initialization
	this.background = Configuration.getColor("coloring.collection_tree_background", false);
	this.foreground = Configuration.getColor("coloring.collection_tree_foreground", false);
	this.editable_background = Configuration.getColor("coloring.editable_background", false);
	this.editable_foreground = Configuration.getColor("coloring.editable_foreground", false);
	this.selection_background = Configuration.getColor("coloring.collection_selection_background", false);
	this.selection_foreground = Configuration.getColor("coloring.collection_selection_foreground", false);
	if (isEditable()) {
	    this.setBackground(editable_background);
	    this.setForeground(editable_foreground);
	}
	else {
	    this.setBackground(background);
	    this.setForeground(foreground);
	}
	setBorder(BorderFactory.createLoweredBevelBorder());
	setRenderer(new Renderer());
    }

    /** Overridden to do nothing. 
     * @param background
     */
    public void setBackground(Color background) {
    }

    public void setBackgroundEditableColor(Color editable_background) {
	this.editable_background = editable_background;
    }

    public void setBackgroundNonSelectionColor(Color background) {
	this.background = background;
    }

    public void setBackgroundSelectionColor(Color selection_background) {
	this.selection_background = selection_background;
    }

    public void setEditable(boolean editable) {
	setEditor(new Editor());
	super.setEditable(editable);
	if (isEditable()) {
	    this.setBackground(editable_background);
	    this.setForeground(editable_foreground);
	}
	else {
	    this.setBackground(background);
	    this.setForeground(foreground);
	}
    }

    public void setTextEditableColor(Color editable_foreground) {
	this.editable_foreground = editable_foreground;
    }

    public void setTextNonSelectionColor(Color foreground) {
	this.foreground = foreground;
    }

    public void setTextSelectionColor(Color selection_foreground) {
	this.selection_foreground = selection_foreground;
    }


    private class Editor
	extends JTextField
	implements ComboBoxEditor {
	public Editor() {
	    setOpaque(true);
	    if (isEditable()) {
		setBackground(editable_background);
		setForeground(editable_foreground);
	    }
	    else {
		setBackground(background);
		setForeground(foreground);
	    }
	    setSelectionColor(selection_background);
	    setSelectedTextColor(selection_foreground);
	}

	public Component getEditorComponent() {
	    return this;
	}

	public Object getItem() {
	    return getText();
	}

	public void setItem(Object item) {
	    if(item != null) {
		setText(item.toString());
	    }
	    else {
		setText("");
	    }
	}
    }

    private class Model
	extends DefaultComboBoxModel {
	
	
	public int add(Object extension) {
	    int position = 0;
	    String extension_str = extension.toString().toLowerCase();
	    while(extension != null && position < getSize()) {
		String sibling = getElementAt(position).toString().toLowerCase();
		int order = extension_str.compareTo(sibling);
		// If we are now less than the sibling, insert here.
		if(sort_objects && order < 0) {
		    insertElementAt(extension, position);
		    extension = null;
		}
		// We are equal to the sibling, thus we already exist in list. Done.
		else if(order == 0) {
		    extension = null;
		}
		// Continue searching.
		else {
		    position++;
		}
	    }
	    if(extension != null) {
		position = getSize();
		addElement(extension);
	    }
	    return position;
	}

	public void clear() {
	    removeAllElements();
	}
    }

    private class Renderer
	extends JLabel
	implements ListCellRenderer {
	public Renderer() {
	    super("");
	    this.setOpaque(true);
	}
	public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
	    if(value != null) {
		this.setText(value.toString());
	    }
	    else {
		this.setText("");
	    }
	    if(isSelected) {
		this.setBackground(selection_background);
		this.setForeground(selection_foreground);
	    }
	    else if (isEditable()) {
		this.setBackground(editable_background);
		this.setForeground(editable_foreground);
	    }
	    else {
		this.setBackground(background);
		this.setForeground(foreground);
	    }
	    return this;
	}
    }

    private class UI
	extends BasicComboBoxUI {
	public UI() {
	    super();
	}

	protected ComboPopup createPopup() {
	    BasicComboPopup popup = new BasicComboPopup(comboBox);
	    // ---- I don't know why this code is here... maybe it is needed for the Mac? ----
// 	    {
// 		    public void show() {
// 			if(comboBox.getMaximumRowCount() > 0) {
// 			    Dimension popupSize = new Dimension( comboBox.getWidth(), getPopupHeightForRowCount( comboBox.getMaximumRowCount()));
// 			    Rectangle popupBounds = new Rectangle( 0, 0, popupSize.width, popupSize.height);
// 			    scroller.setMaximumSize( popupBounds.getSize() );
// 			    scroller.setPreferredSize( popupBounds.getSize() );
// 			    scroller.setMinimumSize( popupBounds.getSize() );
// 			    list.invalidate();
// 			    int selectedIndex = comboBox.getSelectedIndex();
// 			    if ( selectedIndex == -1 ) {
// 				list.clearSelection();
// 			    }
// 			    else {
// 				list.setSelectedIndex( selectedIndex );
// 			    }
// 			    list.ensureIndexIsVisible( list.getSelectedIndex() );
// 			    setLightWeightPopupEnabled( comboBox.isLightWeightPopupEnabled() );
// 			    show(arrowButton, arrowButton.getSize().width - (popupSize.width + 4), arrowButton.getSize().height);
// 			}
// 		    }
// 		};
	    popup.getAccessibleContext().setAccessibleParent(comboBox);
	    return popup;
	}

	public void setButtonBackground() {
		arrowButton.setBackground(Configuration.getColor("coloring.button_background", false));
	}
    }
}
