/*
 * Java-Gnome Bindings Library
 *
 * Copyright 1998-2004 the Java-Gnome Team, all rights reserved.
 *
 * The Java-Gnome bindings library is free software distributed under
 * the terms of the GNU Library General Public License version 2.
 */

package org.gnu.gtk;

import java.util.Vector;

import org.gnu.glib.EventMap;
import org.gnu.glib.GObject;
import org.gnu.glib.Type;
import org.gnu.glib.Value;
import org.gnu.gtk.event.GtkEventType;
import org.gnu.gtk.event.TreeModelEvent;
import org.gnu.gtk.event.TreeModelListener;

/**
 * A generic tree interface for use with {@link TreeView} widgets. Internally,
 * the {@link TreeStore} and {@link ListStore} objects are constructed on top of
 * TreeModels. If you were using the C version of gtk, you would be able to
 * construct other objects like those (although I find no reason for doing so).
 * This object provides a number of useful methods which can be used with either
 * the TreeStore or ListStore. 
 * <p>For a full overview of the tree, list and table widgets, please see the
 * {@link TreeView} description.
 */
public class TreeModel extends GObject {

	/**
	 */
	protected TreeModel(int handle) {
		super(handle);
		initializeEventHandlers();
	}

	/*
	 * TODO: Foreach function.
	 */

	/**
	 * Returns the number of data blocks supported by the model
	 */
	public int getDataBlockCount() {
		return gtk_tree_model_get_n_columns(getHandle());
	}

	/**
	 * Returns the type of the data block.
	 * @param index The index of the data block
	 */
	public Type getType(int index) {
		return new org.gnu.glib.Type(gtk_tree_model_get_column_type(getHandle(), index));
	}

	/**
	 * Returns a valid iterator pointing to <code>path</code>, or
	 * <code>null</code>/
	 */
	public TreeIter getIter(TreePath path) {
		int hndl = gtk_tree_model_get_iter(getHandle(), path.getHandle());
		return iterFromHandle(hndl);
	}

	/**
	 * Returns a valid iterator pointing to the path represented by
	 * <code>pathString</code>, or returns <code>null</code> if the path is
	 * invalid.
	 */
	public TreeIter getIter(String pathString) {
		int hndl = gtk_tree_model_get_iter_from_string(getHandle(), pathString);
		return iterFromHandle(hndl);
	}

	/**
	 * Returns the iterator at the start of the model (the one at the path "0"),
	 * or returns <code>null</code> if the tree is empty.
	 */
	public TreeIter getFirstIter() {
		int hndl = gtk_tree_model_get_iter_first(getHandle());
		return iterFromHandle(hndl);
	}

	/**
	 * Returns the Value at the given iter in the specified data block. 
	 */
	public String getValue(TreeIter iter, DataColumnString dataBlock) {
		int value = gtk_tree_model_get_value(getHandle(), iter.getHandle(), dataBlock.getColumn());
		Value val = new Value(value);
		return val.getString();
	}
	/**
	 * Returns the Value at the given iter in the specified data block.
	 */
	public int getValue(TreeIter iter, DataColumnInt dataBlock) {
		int value = gtk_tree_model_get_value(getHandle(), iter.getHandle(), dataBlock.getColumn());
		Value val = new Value(value);
		return val.getInt();
	}
	/**
	 * Returns the Value at the given iter in the specified data block. 
	 */
	public boolean getValue(TreeIter iter, DataColumnBoolean dataBlock) {
		int value = gtk_tree_model_get_value(getHandle(), iter.getHandle(), dataBlock.getColumn());
		Value val = new Value(value);
		return val.getBoolean();
	}
	/**
	 * Returns the Value at the given iter in the specified data block. 
	 */
	public double getValue(TreeIter iter, DataColumnDouble dataBlock) {
		int value = gtk_tree_model_get_value(getHandle(), iter.getHandle(), dataBlock.getColumn());
		Value val = new Value(value);
		return val.getDouble();
	}
	/**
	 * Returns the Value at the given iter in the specified data block. 
	 */
	public Object getValue(TreeIter iter, DataColumnObject dataBlock) {
		int value = gtk_tree_model_get_value(getHandle(), iter.getHandle(), dataBlock.getColumn());
		Value val = new Value(value);
		return val.getJavaObject();
	}

	private TreeIter iterFromHandle(int hndl) {
		if (-1 == hndl)
			return null;
		return new TreeIter(hndl, this);
	}


	
	
	/***************************************
	 * Event Handler Related code
	 ****************************************/

	/**
	 * Listeners for handling toggle events
	 */
	private Vector listeners = null;
	/**
	 * Register an object to handle button events.
	 * @see org.gnu.gtk.event.TreeModelListener
	 */
	public void addListener(TreeModelListener listener) {
		// Don't add the listener a second time if it is in the Vector.
		int i = findListener(listeners, listener);
		if (i == -1) {
			if (null == listeners) {
				listeners = new Vector();
			}
			listeners.addElement(listener);
		}
	}
	/**
	 * Removes a listener
	 * @see #addListener(TreeModelListener)
	 */
	public void removeListener(TreeModelListener listener) {
		int i = findListener(listeners, listener);
		if (i > -1) {
			listeners.remove(i);
		}
		if (0 == listeners.size()) {
			listeners = null;
		}
	}

	/**
	 * Give us a way to locate a specific listener in a Vector.
	 * @param list The Vector of listeners to search.
	 * @param listener The object that is to be located in the Vector.
	 * @return Returns the index of the listener in the Vector, or -1 if
	 *                 the listener is not contained in the Vector.
	 */
	protected static int findListener(Vector list, Object listener) {
		if (null == list || null == listener)
			return -1;
		return list.indexOf(listener);
	}

	protected void fireTreeModelEvent(TreeModelEvent event) {
		if (null == listeners) {
			return;
		}
		int size = listeners.size();
		int i = 0;
		while (i < size) {
			TreeModelListener tl = (TreeModelListener)listeners.elementAt(i);
			tl.treeModelEvent(event);
			i++;
		}
	}

	private void handleRowChanged(int path, int iter) {
		TreeModelEvent event = new TreeModelEvent(this, TreeModelEvent.Type.ROW_CHANGED);
		event.setTreePath(new TreePath(path));
		event.setTreeIter(new TreeIter(iter, this));
		fireTreeModelEvent(event);
	}

	private void handleRowInserted(int path, int iter) {
		TreeModelEvent event = new TreeModelEvent(this, TreeModelEvent.Type.ROW_INSERTED);
		event.setTreePath(new TreePath(path));
		event.setTreeIter(new TreeIter(iter, this));
		fireTreeModelEvent(event);
	}

	private void handleRowHasChildToggled(int path, int iter) {
		TreeModelEvent event = new TreeModelEvent(this, TreeModelEvent.Type.ROW_HAS_CHILD_TOGGLED);
		event.setTreePath(new TreePath(path));
		event.setTreeIter(new TreeIter(iter, this));
		fireTreeModelEvent(event);
	}

	private void handleRowDeleted(int path) {
		TreeModelEvent event = new TreeModelEvent(this, TreeModelEvent.Type.ROW_DELETED);
		event.setTreePath(new TreePath(path));
		fireTreeModelEvent(event);
	}

	private void handleRowsReordered(int path, int iter, int newOrder) {
		System.out.println("Handle rows reordered");
		System.err.println("Handle rows reordered");
		TreeModelEvent event = new TreeModelEvent(this, TreeModelEvent.Type.ROWS_REORDERED);
		event.setTreePath(new TreePath(path));
		event.setTreeIter(new TreeIter(iter, this));
		event.setNewOrder(new int[0]);
		fireTreeModelEvent(event);
	}

	protected void initializeEventHandlers() {
		if (!eventsInitialized) {
			evtMap.initialize(this);
			eventsInitialized = true;
		}
			
	}

	public Class getEventListenerClass(String signal) {
		Class cls = evtMap.getEventListenerClass(signal);
		if (cls == null) cls = super.getEventListenerClass(signal);
		return cls;
	}

	public GtkEventType getEventType(String signal) {
		GtkEventType et = evtMap.getEventType(signal);
		if (et == null) et = super.getEventType(signal);
		return et;
	}

	private static EventMap evtMap = new EventMap();
	static {
		addEvents(evtMap);
	}

	/**
	* Implementation method to build an EventMap for this widget class.
	* Not useful (or supported) for application use.
	*/
	private static void addEvents(EventMap anEvtMap) {
		anEvtMap.addEvent("rows_reordered", "handleRowsReordered", TreeModelEvent.Type.ROWS_REORDERED, TreeModelListener.class);
		anEvtMap.addEvent("row_changed", "handleRowChanged", TreeModelEvent.Type.ROW_CHANGED, TreeModelListener.class);
		anEvtMap.addEvent("row_inserted", "handleRowInserted", TreeModelEvent.Type.ROW_INSERTED, TreeModelListener.class);
		anEvtMap.addEvent("row_has_child_toggled", "handleRowHasChildToggled", TreeModelEvent.Type.ROW_HAS_CHILD_TOGGLED, TreeModelListener.class);
		anEvtMap.addEvent("row_deleted", "handleRowDeleted", TreeModelEvent.Type.ROW_DELETED, TreeModelListener.class);
	}

	
	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
    native static final protected int gtk_tree_model_get_n_columns(int treeModel);
    native static final protected int gtk_tree_model_get_column_type(int treeModel, int index);
    native static final protected int gtk_tree_model_get_iter(int treeModel, int path);
    native static final protected int gtk_tree_model_get_iter_from_string(int treeModel, String pathString);
    native static final protected String gtk_tree_model_get_string_from_iter(int treeModel, int iter);
    native static final protected int gtk_tree_model_get_iter_first(int treeModel);
    native static final protected int gtk_tree_model_get_path(int treeModel, int iter);
    native static final protected int gtk_tree_model_get_value(int treeModel, int iter, int column);
    native static final protected int gtk_tree_model_iter_next(int treeModel, int iter);
    native static final protected int gtk_tree_model_iter_children(int treeModel, int parent);
    native static final protected boolean gtk_tree_model_iter_has_child(int treeModel, int iter);
    native static final protected int gtk_tree_model_iter_n_children(int treeModel, int iter);
    native static final protected int gtk_tree_model_iter_nth_child(int treeModel, int parent, int n);
    native static final protected int gtk_tree_model_iter_parent(int treeModel, int child);
//    native static final protected void gtk_tree_model_row_changed(int treeModel, int path, int iter);
//    native static final protected void gtk_tree_model_row_inserted(int treeModel, int path, int iter);
//    native static final protected void gtk_tree_model_row_has_child_toggled(int treeModel, int path, int iter);
//    native static final protected void gtk_tree_model_row_deleted(int treeModel, int path);
//    native static final protected void gtk_tree_model_rows_reordered(int treeModel, int path, int iter, int[] newOrder); 
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
