/**
 * vim: sw=4 ts=4:
 *
 * Gnome Apt gnome-vfs method
 *
 * 	(C) 2003 Filip Van Raemdonck <mechanix@debian.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 * 	$Id$
 *
 **/

/**
 * TODO: display hourglass emblems (HOW???), add fake directories for actions,
 *       add menu entry, complete run button, check cache for re-init
 **/

#define METHOD_NAME "software"

#include "pkgtree.h"

#include <string.h>
#include <libgnomevfs/gnome-vfs-method.h>
#include <libgnomevfs/gnome-vfs-utils.h>
#include <apt-pkg/init.h>
#include <apt-pkg/error.h>

#define LOG_ENTER g_message ("VFS call enter %s:%s()", METHOD_NAME, __FUNCTION__);
#define LOG_EXIT  g_message ("VFS call exit  %s:%s()", METHOD_NAME, __FUNCTION__);

static GAptCacheFile* cachefile;
static GAptPkgTree* pkgtree;

static gboolean pkgvfs_initdone = FALSE;

static GnomeVFSResult
pkgvfs_open (GnomeVFSMethod* meth, GnomeVFSMethodHandle** handle,
      GnomeVFSURI* uri, GnomeVFSOpenMode mode, GnomeVFSContext* ctx) {
	LOG_ENTER

	gchar *path, *puri;
	path = puri = gnome_vfs_unescape_string (gnome_vfs_uri_get_path (uri), NULL);
	g_message ("%s path %s", __FUNCTION__, path);

	g_free (puri);
	LOG_EXIT
	return GNOME_VFS_ERROR_NOT_FOUND;
}

static GnomeVFSResult
pkgvfs_open_dir (GnomeVFSMethod* meth, GnomeVFSMethodHandle** handle,
      GnomeVFSURI* uri, GnomeVFSFileInfoOptions opt, GnomeVFSContext* ctx) {
	LOG_ENTER

	gchar *path, *puri;
	path = puri = gnome_vfs_unescape_string (gnome_vfs_uri_get_path (uri), NULL);
	g_message ("%s path %s", __FUNCTION__, path);

	TreeNode *root = pkgtree->root(), *dir;

	if (!path) {
		/* Just in case... */
		g_free (puri);
		return GNOME_VFS_ERROR_NOT_FOUND;
	}

	if (!strcmp (path, G_DIR_SEPARATOR_S)) {
		/* optimize root node */
		dir = root;
		dir->cur = dir->begin();
		*handle = (GnomeVFSMethodHandle*) dir;

		g_free (puri);
		return GNOME_VFS_OK;
	}

	size_t dslen = strlen (G_DIR_SEPARATOR_S);
	if (strlen (g_strrstr (path, G_DIR_SEPARATOR_S)) == dslen) {
		g_strrstr (path, G_DIR_SEPARATOR_S)[0] = '\0';
	}

	if (!strncmp (path, G_DIR_SEPARATOR_S, dslen)) {
		path += dslen;
	}

	gboolean found = FALSE;

	dir = root;

	TreeNode::iterator it = dir->begin();
	while (it != dir->end() && !found) {
		const gchar* pcname = ((GAptPkgTree::Item*) (*it))->name();
		size_t pclen = strlen (pcname);

		if (!strncmp (path, pcname, pclen)) {
			if ((path + pclen)[0] == '\0') {
				found = true;
			} else if (!strncmp (path + pclen, G_DIR_SEPARATOR_S, dslen)) {
				path += (pclen + dslen);
				it = (*it)->begin();
				dir = *it;
			} else {
				/* Just a prefix of the path component matches */
				++it;
			}
		} else {
			++it;
		}
	}

	if (!found) {	/* includes it == dir->end() */
		g_warning ("Could not find %s!", path);
		g_free (puri);
		LOG_EXIT
		return GNOME_VFS_ERROR_NOT_FOUND;
	}

	dir = *it;
	dir->cur = dir->begin();

	*handle = (GnomeVFSMethodHandle*) dir;

	g_free (puri);
	LOG_EXIT
	return GNOME_VFS_OK;
}

static GnomeVFSResult
vfs_close_dir (GnomeVFSMethod* meth, GnomeVFSMethodHandle* handle,
      GnomeVFSContext* ctx) {
	LOG_ENTER
	/* Free ((sometype*) handle) */

	LOG_EXIT
	return GNOME_VFS_OK;
}

static GnomeVFSResult
vfs_read_dir (GnomeVFSMethod* meth, GnomeVFSMethodHandle* handle,
      GnomeVFSFileInfo* info, GnomeVFSContext* ctx) {
	LOG_ENTER
	/* FIXME: is this ever possible? */
	if (!pkgtree) {
		return GNOME_VFS_ERROR_EOF;
	}

	TreeNode* dir = (TreeNode*) handle;

	if (dir->cur == dir->end()) {
		return GNOME_VFS_ERROR_EOF;
	}

	TreeNode* n = *(dir->cur);
	/* FIXME: check n->hidden() */
	info->name = g_strdup (((GAptPkgTree::Item*) n)->name());
	g_message ("Reading %s", info->name);

	/*if (n->expandable()) {*/
	if (((GAptPkgTree::Item*) n)->relation() == GAptPkgTree::Item::CategoryItem) {
		info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
		info->mime_type = g_strdup ("x-directory/normal");
	} else {
		info->type = GNOME_VFS_FILE_TYPE_REGULAR;
		/* FIXME: need more generic package, and node needs to store type, too */
		info->mime_type = g_strdup ("application/x-deb");
		info->size = 0;
		info->valid_fields = (GnomeVFSFileInfoFields)
		      ((int) info->valid_fields | GNOME_VFS_FILE_INFO_FIELDS_SIZE);
	}
	info->valid_fields = (GnomeVFSFileInfoFields) ((int) info->valid_fields |
	      GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE);

	++(dir->cur);

	LOG_ENTER
	return GNOME_VFS_OK;
}

static GnomeVFSResult
vfs_file_info (GnomeVFSMethod* meth, GnomeVFSURI* uri,
      GnomeVFSFileInfo* info, GnomeVFSFileInfoOptions opt,
      GnomeVFSContext* ctx) {
	LOG_ENTER

	gchar *path, *puri;
	path = puri = gnome_vfs_unescape_string (gnome_vfs_uri_get_path (uri), NULL);
	g_message ("%s path %s", __FUNCTION__, path);

	TreeNode *root = pkgtree->root(), *dir;

	if (!path) {
		/* Just in case... */
		g_free (puri);
		return GNOME_VFS_ERROR_NOT_FOUND;
	}

	if (!strcmp (path, G_DIR_SEPARATOR_S)) {
		/* optimize root node */
		info->name = g_strdup (((GAptPkgTree::Item*) root)->name());
		info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
		info->mime_type = g_strdup ("x-directory/normal");
		info->valid_fields = (GnomeVFSFileInfoFields) ((int) info->valid_fields |
		      GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE);

		g_free (puri);
		return GNOME_VFS_OK;
	}

	size_t dslen = strlen (G_DIR_SEPARATOR_S);
	if (strlen (g_strrstr (path, G_DIR_SEPARATOR_S)) == dslen) {
		g_strrstr (path, G_DIR_SEPARATOR_S)[0] = '\0';
	}

	if (!strncmp (path, G_DIR_SEPARATOR_S, dslen)) {
		path += dslen;
	}

	gboolean found = FALSE;

	dir = root;

	TreeNode::iterator it = dir->begin();
	while (it != dir->end() && !found) {
		const gchar* pcname = ((GAptPkgTree::Item*) (*it))->name();
		size_t pclen = strlen (pcname);

		if (!strncmp (path, pcname, pclen)) {
			if ((path + pclen)[0] == '\0') {
				found = true;
			} else if (!strncmp (path + pclen, (gchar*) G_DIR_SEPARATOR_S, dslen)) {
				path += (pclen + dslen);
				it = (*it)->begin();
				dir = *it;
			} else {
				/* Just a prefix of the path component matches */
				++it;
			}
		} else {
			++it;
		}
	}

	if (!found) {	/* includes it == dir->end() */
		g_warning ("Could not find %s!", path);
		g_free (puri);
		LOG_EXIT
		return GNOME_VFS_ERROR_NOT_FOUND;
	}

	dir = *it;
	dir->cur = dir->begin();

	info->name = g_strdup (((GAptPkgTree::Item*) dir)->name());
	/*if (dir->expandable()) {*/
	if (((GAptPkgTree::Item*) dir)->relation() == GAptPkgTree::Item::CategoryItem) {
		info->type = GNOME_VFS_FILE_TYPE_DIRECTORY;
		info->mime_type = g_strdup ("x-directory/normal");
	} else {
		info->type = GNOME_VFS_FILE_TYPE_REGULAR;
		info->mime_type = g_strdup ("application/x-deb");
		info->size = 0;
		info->valid_fields = (GnomeVFSFileInfoFields)
		      ((int) info->valid_fields | GNOME_VFS_FILE_INFO_FIELDS_SIZE);
	}
	info->valid_fields = (GnomeVFSFileInfoFields) ((int) info->valid_fields |
	      GNOME_VFS_FILE_INFO_FIELDS_TYPE | GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE);

	g_free (puri);
	LOG_EXIT
	return GNOME_VFS_OK;
}

static gboolean
vfs_is_local (GnomeVFSMethod* meth, const GnomeVFSURI* uri) {
	g_message ("vfs_is_local %s", METHOD_NAME);
	return TRUE;
}

static GnomeVFSMethod method = {
	sizeof (GnomeVFSMethod),

	pkgvfs_open,
	NULL,	/* create */
	NULL,	/* close */
	NULL,	/* read */
	NULL,	/* write */
	NULL,	/* seek */
	NULL,	/* tell */
	NULL,	/* truncate_handle */
	pkgvfs_open_dir, vfs_close_dir, vfs_read_dir,
	vfs_file_info,
	NULL,	/* get_file_info_from_handle */
	vfs_is_local,
	NULL,	/* mkdir */
	NULL,	/* rmdir */
	NULL,	/* move */
	NULL,	/* rm */
	NULL,	/* check_same_fs */
	NULL,	/* set_file_info */
	NULL,	/* truncate */
	NULL,	/* find_directory */
	NULL,	/* create_symbolic_link */
	NULL,	/* monitor_add */
	NULL,	/* monitor_cancel */
	NULL	/* file_control */
};

static gboolean
vfs_tree_init (void) {
	LOG_ENTER
	if (pkgvfs_initdone) {
		g_warning ("Trying to reinit!");
		return TRUE;
	}

	if (!pkgInitConfig (*_config) || !pkgInitSystem (*_config, _system)) {
		_error->DumpErrors();
		return FALSE;
	}
	_config->Set ("Debug::NoLocking", true);

	OpTextProgress progress;
	cachefile = gnome_apt_cache_file_init (&progress);
	if (!cachefile) {
		_error->DumpErrors();
		return FALSE;
	}

	pkgtree = new GAptPkgTree;
	cachefile->add_view (pkgtree);

	pkgvfs_initdone = TRUE;

	LOG_EXIT
	return TRUE;
}

extern "C" GnomeVFSMethod*
vfs_module_init (const gchar* name, const gchar* args) {
	g_message ("VFS call %s:%s()", METHOD_NAME, __FUNCTION__);

	if (!g_ascii_strcasecmp (name, METHOD_NAME)) {
		if (vfs_tree_init()) {
			LOG_EXIT
			return &method;
		}
	}

	g_warning ("%s:/// vfs method init failed", METHOD_NAME);
	return NULL;
}

static void
vfs_tree_kill (void) {
	delete pkgtree;
}

extern "C" void
vfs_module_shutdown (GnomeVFSMethod* method) {
	g_message ("VFS call %s:%s()", METHOD_NAME, __FUNCTION__);

	vfs_tree_kill();
}
