/*
 * 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 org.gnu.glib.Type;
import org.gnu.glib.Value;

/**
 * The TreeStore is a Model used for storing data which will be displayed in any
 * number of {@link TreeView} widgets. For an overview of how the tree and list
 * objects fit together, see the {@link TreeView} description.
 *
 * <p>Data is stored within this object in {@link DataColumn}s. This
 * data is displayed in the widgets via {@link CellRenderer}'s; the mapping
 * between this data and the cell renderers of each treeview column is done in
 * the {@link TreeViewColumn} class. 
 *
 * @author Mark Howard &lt;mh@debian.org&gt;
 */
public class TreeStore extends TreeModel implements TreeSortable, TreeDragDest, TreeDragSource {
	/**
	 * Constructs a new TreeStore, defining the types for each datablock.
	 * Subsequent setting of data to any of the datablocks must follow the types
	 * you set out here. 
	 * @param dataColumns Types for the data to be stored in the TreeStore.
	 */
	public TreeStore(DataColumn[] dataColumns) {
		super(init(dataColumns));
	}
	
	private static int init(DataColumn[] dataColumns) {
		int[] intTypes = new int[dataColumns.length];
		for (int i = 0; i < intTypes.length; i++) {
			intTypes[i] = dataColumns[i].getType().getHandle();
			dataColumns[i].setColumn(i);
		}
		return gtk_tree_store_newv(dataColumns.length, intTypes);
	}

	public void setColumnTypes(DataColumn[] dataColumns) {
		int[] intTypes = new int[dataColumns.length];
		for (int i = 0; i < intTypes.length; i++) {
			intTypes[i] = dataColumns[i].getType().getHandle();
			dataColumns[i].setColumn(i);
		}
		gtk_tree_store_set_column_types(getHandle(), dataColumns.length, intTypes);
	}

	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set. 
	 */
	public void setValue(TreeIter iter, DataColumnString dataBlock, String value) {
		Value val = new Value(Type.STRING());
		val.setString(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}

	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set. 
	 */
	public void setValue(TreeIter iter, DataColumnStockItem dataBlock, GtkStockItem value) {
		Value val = new Value(Type.STRING());
		val.setString( value.getString() );
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}
	
	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set.
	 */
	public void setValue(TreeIter iter, DataColumnIconSize dataBlock, IconSize value) {
		Value val = new Value(Type.INT());
		val.setInteger(value.getValue());
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}
	
	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set.
	 */
	public void setValue(TreeIter iter, DataColumnInt dataBlock, int value) {
		Value val = new Value(Type.INT());
		val.setInteger(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}

	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set. 
	 */
	public void setValue(TreeIter iter, DataColumnBoolean dataBlock, boolean value) {
		Value val = new Value(Type.BOOLEAN());
		val.setBoolean(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}
	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set.
	 */
	public void setValue(TreeIter iter, DataColumnDouble dataBlock, double value) {
		Value val = new Value(Type.DOUBLE());
		val.setDouble(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}

	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data, starting at
	 * 0.
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set. This <em>must</em> match the type for
	 * that dataBlock, as set in the constructor.
	 */
	public void setValue(TreeIter iter, DataColumnObject dataBlock, Object value) {
		Value val = new Value(Type.JAVA_OBJECT());
		val.setJavaObject(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}

	/**
	 * Sets a value in the dataStore. The type of the value <em>must</em> match
	 * the type set for that dataBlock in the constructor.  <p>This does not
	 * make the data visible in any of the widgets which use the class - to do
	 * that, you have to construct a {@link TreeViewColumn} and add it to the
	 * {@link TreeView}; construct and add a {@link CellRenderer} to that; and
	 * finally associate the properties of the CellRenderer with the dataBlocks,
	 * using the {@link TreeViewColumn#addAttributeMapping(CellRenderer, CellRendererAttribute,
	 * int)} method.  @param dataBlock The datablock in which the data should be
	 * stored.
	 * @param dataBlock The data block in which to store the data, starting at
	 * 0.
	 * @param iter Valid iterator for the data row in which the value is to be
	 * set. These can be gotten using methods such as {@link #appendRow(TreeIter)}.
	 * @param value The value to be set. 
	 */
	public void setValue(TreeIter iter, DataColumnPixbuf dataBlock, org.gnu.gdk.Pixbuf value) {
		Value val = new Value(Type.PIXBUF());
		val.setPixbuf(value);
		gtk_tree_store_set_value(getHandle(), iter.getHandle(), dataBlock.getColumn(), val.getHandle());
	}

	/**
	 * Removes a row from the tree store. After being removed, iter is set to
	 * the next valid row at that level, or invalidated if it previously pointed
	 * to the last one.
	 * @paramTreeIter iterator representing the row.
	 */
	public void removeRow(TreeIter iter) {
		gtk_tree_store_remove(getHandle(), iter.getHandle());
	}

	/**
	 * Creates a new row at position. If parent is non-NULL, then the row will 
	 * be made a child of parent. Otherwise, the row will be created at the 
	 * toplevel. If position is larger than the number of rows at that level, 
	 * then the new row will be inserted to the end of the list. 
	 * @param parent A valid TreeIter, or NULL
	 * @param position Position to insert the new row
	 * @return Iterator for the new row
	 */
	public TreeIter insertRow(TreeIter parent, int position) {
		int hndl = (null== parent) ? 0 : parent.getHandle();
		return new TreeIter(gtk_tree_store_insert(getHandle(), hndl, position), (TreeModel) this );
	}

	/**
	 * Inserts a new row before sibling. If sibling is NULL, then the row will
	 * be appended to parent 's children. If parent and sibling are NULL, then
	 * the row will be appended to the toplevel. If both sibling and parent are
	 * set, then parent must be the parent of sibling. When sibling is set,
	 * parent is optional
	 * @param parent A valid TreeIter, or NULL
	 * @param sibling A valid TreeIter, or NULL
	 * @return Iterator for the new row.
	 */
	public TreeIter insertRowBefore(TreeIter sibling, TreeIter parent) {
		int s = 0;
		int p = 0;
		if (null != parent)
			p = parent.getHandle();
		if (null != sibling)
			s = sibling.getHandle();
		return new TreeIter(gtk_tree_store_insert_before(getHandle(), p, s), (TreeModel) this );
	}

	/**
	 * Inserts a new row after sibling. If sibling is NULL, then the row will be 
	 * prepended to the beginning of the parent 's children. If parent and sibling 
	 * are NULL, then the row will be prepended to the toplevel. If both sibling 
	 * and parent are set, then parent must be the parent of sibling. When
	 * sibling is set, parent is optional.
	 * @param parent A valid TreeIter, or NULL
	 * @param sibling A valid GtkTreeIter, or NULL
	 * @return Iterator for the new row.
	 */
	public TreeIter insertRowAfter(TreeIter sibling, TreeIter parent) {
		int s = 0;
		int p = 0;
		if (null != parent)
			p = parent.getHandle();
		if (null != sibling)
			s = sibling.getHandle();
		return new TreeIter(gtk_tree_store_insert_after(getHandle(), p, s), (TreeModel) this );
	}

	/**
	 * Prepends a new row to the store.  If parent is non-NULL, then it will
	 * prepend the new row before the first child of parent, otherwise it will
	 * prepend a row to the top level.
	 * @param parent A valid TreeIter, or NULL
	 * @return Iterator for the new row.
	 */
	public TreeIter prependRow(TreeIter parent) {
		int hndl = (null == parent) ? 0 : parent.getHandle();
		return new TreeIter(gtk_tree_store_prepend(getHandle(), hndl), (TreeModel) this );
	}

	/**
	 * Appends a new row to tree_store. If parent is non-NULL, then it will
	 * append the new row after the last child of parent, otherwise it will
	 * append a row to the top level.
	 * @param parent A valid TreeIter, or NULL
	 * @return Iterator for the new row.
	 */
	public TreeIter appendRow(TreeIter parent) {
		int hndl = (null == parent) ? 0 : parent.getHandle();
		return new TreeIter(gtk_tree_store_append(getHandle(), hndl), (TreeModel) this );
	}

	/**
	 * Returns TRUE if iter is an ancestor of descendant. That is, iter is the parent (or grandparent or great-grandparent) of descendant.
	 * @param iter Row to be tested.
	 * @param descendant Descendant row for testing
	 * @return True of iter is an ancestor of descendant
	 */
	public boolean isAncestor(TreeIter iter, TreeIter descendant) {
		return gtk_tree_store_is_ancestor(getHandle(), iter.getHandle(), descendant.getHandle());
	}

	/**
	 * Returns the depth of the iterator.  This will be 0 for anything on the root level, 1 for anything down a level, etc.
	 * @param iter The iterator to test the depth of
	 * @return Depth of the iterator.
	 */
	public int getIteratorDepth(TreeIter iter) {
		return gtk_tree_store_iter_depth(getHandle(), iter.getHandle());
	}

	/**
	 * Removes all items from the treestore.
	 */
	public void clear() {
		gtk_tree_store_clear(getHandle());
	}

	/**
	 * Retrieve the runtime type used by the GLib library.
	 */
	public static Type getType() {
		return new Type(gtk_tree_store_get_type());
	}


	public void setSortColumn( DataColumn column, SortType order ){
		setSortColumnId(getHandle(), column.getColumn());
		setOrder(getHandle(), order.getValue());
	}

	public void setSortMethod( TreeIterComparison method, DataColumn column ){	
		// TODO
		throw new RuntimeException("Not yet implemented");
	}

	public void setDragDestListener( TreeDragDestListener listener ){
		// TODO
		throw new RuntimeException("Not yet implemented");
	}

	public void setDragSourceListener( TreeDragSourceListener listener ){
		// TODO
		throw new RuntimeException("Not yet implemented");
	}

	/**
	 * Swaps a and b in the same level of tree_store. Note that this function
	 * only works with unsorted stores.
	 *
	 * @since 2.2
	 */
	public void swapRows( TreeIter a, TreeIter b ){
		gtk_tree_store_swap(getHandle(), a.getHandle(), b.getHandle());
	}

	/**
	 * Moves iter to the end of the model
	 *
	 * @since 2.2
	 */
	public void moveRowToEnd(TreeIter iter ){
		gtk_tree_store_move_before(getHandle(), iter.getHandle(), 0);
	}

	/**
	 * Moves iter in this store to the position after position. iter and
	 * position should be in the same level. Note that this function only works
	 * with unsorted stores.
	 *
	 * @since 2.2
	 */
	public void moveRowAfter(TreeIter iter, TreeIter position ){
		gtk_tree_store_move_after(getHandle(), iter.getHandle(), position.getHandle());
	}

	/**
	 * Moves iter in this store to the start of the store.
	 *
	 * @since 2.2
	 */
	public void moveRowToStart(TreeIter iter ){
		gtk_tree_store_move_after(getHandle(), iter.getHandle(), 0);
	}

	/**
	 * Moves iter in tree_store to the position before position. iter and
	 * position should be in the same level. Note that this function only works
	 * with unsorted stores.
	 *
	 * @since 2.2
	 */
	public void moveRowBefore(TreeIter iter, TreeIter position ){
		gtk_tree_store_move_before(getHandle(), iter.getHandle(), position.getHandle());
	}

	public boolean isIterValid(TreeIter iter) {
		return gtk_tree_store_iter_is_valid(getHandle(), iter.getHandle());
	}
	
	public void reorder(TreeIter iter, int[] newOrder) {
		gtk_tree_store_reorder(getHandle(), iter.getHandle(), newOrder);
	}

	
	/****************************************
	 * BEGINNING OF JNI CODE
	 ****************************************/
//	native static final protected int getRoot(int cptr);
//	native final protected void setRoot(int cptr, int root);
//	native static final protected int getLast(int cptr);
//	native final protected void setLast(int cptr, int last);
//	native static final protected int getNColumns(int cptr);
//	native final protected void setNColumns(int cptr, int n_columns);
	native static final protected int getSortColumnId(int cptr);
	native final protected void setSortColumnId(int cptr, int sort_column_id);
	native static final protected int getOrder(int cptr);
	native final protected void setOrder(int cptr, int order);
//	native static final protected int getColumnHeaders(int cptr);
//	native final protected void setColumnHeaders(int cptr, int column_headers);
//	native static final protected boolean getColumnsDirty(int cptr);
//	native final protected void setColumnsDirty(int cptr, boolean columns_dirty);

	native static final protected int gtk_tree_store_get_type();
	native static final protected int gtk_tree_store_newv(int numColumns, int[] types);
	native static final protected void gtk_tree_store_set_column_types(int treeStore, int numColumns, int[] types);
	native static final protected void gtk_tree_store_set_value(int treeStore, int iter, int columnt, int value);
	native static final protected void gtk_tree_store_remove(int treeStore, int iter);
	native static final protected int gtk_tree_store_insert(int treeStore, int parent, int position);
	native static final protected int gtk_tree_store_insert_before(int treeStore, int parent, int sibling);
	native static final protected int gtk_tree_store_insert_after(int treeStore, int parent, int sibling);
	native static final protected int gtk_tree_store_prepend(int treeStore, int parent);
	native static final protected int gtk_tree_store_append(int treeStore, int parent);
	native static final protected boolean gtk_tree_store_is_ancestor(int treeStore, int iter, int descendant);
	native static final protected int gtk_tree_store_iter_depth(int treeStore, int iter);
	native static final protected void gtk_tree_store_clear(int treeStore);
	native static final protected boolean gtk_tree_store_iter_is_valid(int treeStore, int iter);
	native static final protected void gtk_tree_store_reorder(int treeStore, int iter, int[] newOrder);
	native static final protected void gtk_tree_store_swap(int treeStore, int iterA, int iterB);
	native static final protected void gtk_tree_store_move_before(int treeStore, int iter, int position);
	native static final protected void gtk_tree_store_move_after(int treeStore, int iter, int position);
	/****************************************
	 * END OF JNI CODE
	 ****************************************/
}
