/*
 * 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.gconf;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

import org.gnu.glib.GObject;
import org.gnu.glib.Handle;

/**
 * ConfClient provides a client-side cache for a specified list of directories
 * your're interested in.  You can preload entire directories into cache
 * to enhance performance.  It also provides events when a value changes.
 */
public class ConfClient extends GObject {
	
	private class NotifyData {
		public String namespace;
		public Vector listeners;
	}
	
	private class NotifyClient {
		public int id;
		public ConfClientListener lis;
	}
	
	/**
	 * Listeners for handling notify events
	 */
	private Hashtable listeners = null;
	
	private ConfClient(Handle handle) {
		super(handle);
	}
	
	/**
	 * Method to get the default ConfClient
	 * @return The default ConfClient.
	 */
	public static ConfClient getInstance() {
		return new ConfClient(gconf_client_get_default());
	}
	
	/**
	 * Add a directory to the list of directories the ConfClient will
	 * watch.  Any changes to keys below this directory will cause the
	 * "value_changed" event to be fired.  
	 * @param dir
	 * @param type
	 * @throws ConfException
	 */
	public void addDirectory(String dir, ConfClientPreloadType type) throws ConfException {
		int err = -1;
		gconf_client_add_dir(getHandle(), dir, type.getValue(), new int[] {err});
		checkError(err);
	}

	/**
	 * Remove a directory from the list created with addDirectory.
	 * @param dir
	 * @throws ConfException
	 */
	public void removeDirectory(String dir) throws ConfException {
		int err = -1;
		gconf_client_remove_dir(getHandle(), dir, new int[] {err});
		checkError(err);
	}

	

	/**
	 * Register an object to handle notify events.
	 * @see org.gnu.gconf.ConfClientListener
	 */
	public void addListener(ConfClientListener listener, String nameSpace)  throws ConfException {
		int err = -1;
		int id = gconf_client_notify_add(getHandle(), nameSpace, new int[] {err});
		checkError(err);
		
		if (null == listeners)
				listeners = new Hashtable();
		NotifyData nd = (NotifyData)listeners.get(nameSpace);

		if (null == nd) {
			nd = new NotifyData();
			nd.namespace = nameSpace;
			nd.listeners = new Vector();
			listeners.put(nameSpace, nd);
		}
		NotifyClient nc = new NotifyClient();
		nc.id = id;
		nc.lis = listener;
		nd.listeners.addElement(nc);
	}

	/**
	 * Removes a listener.
	 * @see #addListener(ConfClientListener, String)
	 */
	public void removeListener(ConfClientListener listener, String nameSpace) {
		int id = -1;
		
		// get the object from the hashtable.
		if (null == listeners)
			return;
		NotifyData nd = (NotifyData)listeners.get(nameSpace);
		if (null == nd)
			return;

		for (int i = 0; i < nd.listeners.size(); i++) {
			NotifyClient nc = (NotifyClient)nd.listeners.elementAt(i);
			if (nc.lis == listener) {
				id = nc.id;
				nd.listeners.remove(nc.lis);
				break;
			}
		}
		if (-1 == id)
			return;

		gconf_client_notify_remove(getHandle(), id);
	}

	/**
	 * If you know you're done reading values for a while you can blow
	 * away the cache.  Note that this nullifies the effect of any preloading
	 * you may have done.  However it frees some memory.
	 */
	public void clearCache() {
		gconf_client_clear_cache(getHandle());
	}

	/**
	 * Preload a directory.  The directory must have been added already.  This
	 * is only useful as an optimization if you clear the cache, then later want to
	 * do a lot a reads again.
	 * @param directory The directory to load
	 * @param type How to preload the directory.
	 * @throws ConfException
	 */
	public void preload(String directory, ConfClientPreloadType type) throws ConfException {
		int err = -1;
		gconf_client_preload(getHandle(), directory, type.getValue(), new int[] {err});
		checkError(err);
	}

	/**
	 * Set the value of a configuration key.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public void set(String key, ConfValue value) throws ConfException {
		int err = -1;
		gconf_client_set(getHandle(), key, value.getHandle(), new int[] {err});
		checkError(err);
	}
	
	/**
	 * Get the value of a configuration key.
	 * @param key
	 * @throws ConfException
	 */
	public ConfValue get(String key) throws ConfException {
		int err = -1;
		Handle hndl = gconf_client_get(getHandle(), key, new int[] {err});
		checkError(err);
		return new ConfValue(hndl);
	}
	
	/**
	 * Obtain the full ConfEntry for a value.
	 * @param key
	 * @param locale
	 * @param useSchemaDefault
	 * @throws ConfException
	 */
	public ConfEntry getEntry(String key, String locale, boolean useSchemaDefault) throws ConfException {
		int err = -1;
		Handle hndl = gconf_client_get_entry(getHandle(), key, locale, useSchemaDefault, new int[] {err});
		checkError(err);
		return new ConfEntry(hndl);
	}
	
	/**
	 * Return the default value stored in the key's schema, if the key has
	 * a schema associated and the schema exists and the schema contains
	 * a default value.
	 * @param key
	 * @throws ConfException
	 */
	public ConfValue getDefaultFromSchema(String key) throws ConfException {
		int err = -1;
		Handle hndl = gconf_client_get_default_from_schema(getHandle(), key, new int[] {err});
		checkError(err);
		return new ConfValue(hndl);
	}
	
	/**
	 * Unsets the value of the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public boolean unset(String key) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_unset(getHandle(), key, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Get a list of all ConfEntries in the provided direcotry.
	 * @param dir
	 * @throws ConfException
	 */
	public List getAllEntries(String dir) throws ConfException {
		int err = -1;
		Handle [] array = gconf_client_all_entries(getHandle(), dir, new int[] {err});
		checkError(err);
		if (array == null)
			return null;
		List ret = new ArrayList();
		for (int i = 0; i < array.length; i++) {
			ret.add(new ConfEntry(array[i]));
		}
		return ret;
	}
	
	/**
	 * Get a list of the subdirectories in the provided directory.
	 * @param dir
	 * @throws ConfException
	 */
	public List getAllDirs(String dir) throws ConfException {
		int err = -1;
		String [] array = gconf_client_all_dirs(getHandle(), dir, new int[] {err});
		checkError(err);
		if (array == null)
			return null;
		List ret = new ArrayList();
		for (int i = 0; i < array.length; i++) {
			ret.add(array[i]);
		}
		return ret;
	}
	
	/**
	 * Suggest to gconfd that you've just finished a block of changes and
	 * it would be an optimal time to sync to permanent storage.
	 * @throws ConfException
	 */
	public void suggestSync() throws ConfException {
		int err = -1;
		gconf_client_suggest_sync(getHandle(), new int[] {err});
		checkError(err);
	}
	
	/**
	 * Check to see if a directory exists in the GConf database.
	 * @param dir
	 * @throws ConfException
	 */
	public boolean dirExists(String dir) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_dir_exists(getHandle(), dir, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Request the double value stored at the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public double getDouble(String key) throws ConfException {
		int err = -1;
		double ret = gconf_client_get_float(getHandle(), key, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Request the int value stored at the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public int getInt(String key) throws ConfException {
		int err = -1;
		int ret = gconf_client_get_int(getHandle(), key, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Request the String value stored at the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public String getString(String key) throws ConfException {
		int err = -1;
		String ret = gconf_client_get_string(getHandle(), key, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Request the boolean value stored at the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public boolean getBoolean(String key) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_get_bool(getHandle(), key, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Request the ConfSchema value stored at the provided key.
	 * @param key
	 * @throws ConfException
	 */
	public ConfSchema getSchema(String key) throws ConfException {
		int err = -1;
		Handle ret = gconf_client_get_schema(getHandle(), key, new int[] {err});
		checkError(err);
		return new ConfSchema(ret);
	}
	
	/**
	 * Request the object array stored at the key.  The return object
	 * array is of the type provided.
	 * @param key
	 * @param listType
	 * @throws ConfException
	 */
	public Object[] getList(String key, ConfValueType listType) throws ConfException {
		int err = -1;
		Object [] list = gconf_client_get_list(getHandle(), key, listType.getValue(), new int[] {err});
		checkError(err);
		return list;
	}
	
	/**
	 * Change the value stored at the provided key to the provided double
	 * value.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public boolean setDouble(String key, double value) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_set_float(getHandle(), key, value, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Change the value stored at the provided key to the provided int
	 * value.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public boolean setInt(String key, int value) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_set_int(getHandle(), key, value, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Change the value stored at the provided key to the provided String
	 * value.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public boolean setString(String key, String value) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_set_string(getHandle(), key, value, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Change the value stored at the provided key to the provided boolean
	 * value.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public boolean setBoolean(String key, boolean value) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_set_bool(getHandle(), key, value, new int[] {err});
		checkError(err);
		return ret;
	}
	
	/**
	 * Change the value stored at the provided key to the provided ConfSchema
	 * value.
	 * @param key
	 * @param value
	 * @throws ConfException
	 */
	public boolean setSchema(String key, ConfSchema value) throws ConfException {
		int err = -1;
		boolean ret = gconf_client_set_schema(getHandle(), key, value.getHandle(), new int[] {err});
		checkError(err);
		return ret;
	}
	
	public boolean setList(String key, ConfValueType listType, List value) throws ConfException {
		// MUSTDO
		return false;
	}
	
	protected void notifyCallback(Handle e, String nameSpace) {
		NotifyData nd = (NotifyData)listeners.get(nameSpace);
		if (null == nd)
			return;
		ConfEntry entry = new ConfEntry(e);
		Vector notifyListeners = nd.listeners;
		if (null != notifyListeners) {
			for (int i = 0; i < notifyListeners.size(); i++) {
				NotifyClient nc = (NotifyClient)notifyListeners.elementAt(i);
				nc.lis.clientNotify(entry);
			}
		}
	}
	
	private void checkError(int error) throws ConfException {
		if (-1 < error)
			throw new ConfException(error);
	}


	native static final protected Handle gconf_client_get_default();
	native static final protected Handle gconf_client_get_for_engine(Handle engine);
	native static final protected void gconf_client_add_dir(Handle client, String dir, int preloadType, int[] error);
	native static final protected void gconf_client_remove_dir(Handle client, String dir, int[] error);
	native final protected int gconf_client_notify_add(Handle client, String namespace, int[] error);
	native static protected void gconf_client_notify_remove(Handle client, int cnxn);
	native static final protected void gconf_client_clear_cache(Handle client);
	native static final protected void gconf_client_preload(Handle client, String dirname, int type, int[] error);
	native static final protected void gconf_client_set(Handle client, String key, Handle val, int[] error);
	native static final protected Handle gconf_client_get(Handle client, String key, int[] error);
	native static final protected Handle gconf_client_get_without_default(Handle client, String key, int[] error);
	native static final protected Handle gconf_client_get_entry(Handle client, String key, String locale, boolean useSchemaDefault, int[] error);
	native static final protected Handle gconf_client_get_default_from_schema(Handle client, String key, int[] error);
	native static final protected boolean gconf_client_unset(Handle client, String key, int[] error);
	native static final protected Handle[] gconf_client_all_entries(Handle client, String dir, int[] error);
	native static final protected String[] gconf_client_all_dirs(Handle client, String dir, int[] error);
	native static final protected void gconf_client_suggest_sync(Handle client, int[] error);
	native static final protected boolean gconf_client_dir_exists(Handle client, String dir, int[] error);
	native static final protected double gconf_client_get_float(Handle client, String key, int[] error);
	native static final protected int gconf_client_get_int(Handle client, String key, int[] error);
	native static final protected String gconf_client_get_string(Handle client, String key, int[] error);
	native static final protected boolean gconf_client_get_bool(Handle client, String key, int[] error);
	native static final protected Handle gconf_client_get_schema(Handle client, String key, int[] error);
	native static final protected Object[] gconf_client_get_list(Handle client, String key, int listType, int[] error);
	native static final protected boolean gconf_client_get_pair(Handle client, String key, int carType, int cdrType, int[] car, int[] cdr, int[] error);
	native static final protected boolean gconf_client_set_float(Handle client, String key, double val, int[] error);
	native static final protected boolean gconf_client_set_int(Handle client, String key, int val, int[] error);
	native static final protected boolean gconf_client_set_string(Handle client, String key, String val, int[] error);
	native static final protected boolean gconf_client_set_bool(Handle client, String key, boolean val, int[] error);
	native static final protected boolean gconf_client_set_schema(Handle client, String key, Handle val, int[] error);
	native static final protected boolean gconf_client_set_list(Handle client, String key, int listType, Object[] list, int[] error);
	native static final protected boolean gconf_client_set_pair(Handle client, String key, int carType, int cdrType, int car, int cdr, int[] error);
	native static final protected void gconf_client_value_changed(Handle client, String key, Handle value);


	static {
		System.loadLibrary("gconfjni-@apiversion@");
	}
}
