/*
 * Copyright (C) 2003, 2013 by the gtk2-perl team
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * $Id$
 */

#include "vfs2perl.h"

/* ------------------------------------------------------------------------- */

GType
vfs2perl_gnome_vfs_uri_get_type (void)
{
	static GType t = 0;
	if (!t)
		t = g_boxed_type_register_static ("GnomeVFSURI",
		      (GBoxedCopyFunc) gnome_vfs_uri_ref,
		      (GBoxedFreeFunc) gnome_vfs_uri_unref);
	return t;
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSFileSize (GnomeVFSFileSize size)
{
	return newSVuv (size);
}

GnomeVFSFileSize
SvGnomeVFSFileSize (SV *size)
{
	return SvUV (size);
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSFileOffset (GnomeVFSFileOffset offset)
{
	return newSViv (offset);
}

GnomeVFSFileOffset
SvGnomeVFSFileOffset (SV *offset)
{
	return SvIV (offset);
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSHandle (GnomeVFSHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::Handle", handle);
}

GnomeVFSHandle *
SvGnomeVFSHandle (SV *handle)
{
	return INT2PTR (GnomeVFSHandle *, SvIV (SvRV (handle)));

}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSMonitorHandle (GnomeVFSMonitorHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::Monitor::Handle", handle);
}

GnomeVFSMonitorHandle *
SvGnomeVFSMonitorHandle (SV *handle)
{
	return INT2PTR (GnomeVFSMonitorHandle *, SvIV (SvRV (handle)));

}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSDirectoryHandle (GnomeVFSDirectoryHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::Directory::Handle", handle);
}

GnomeVFSDirectoryHandle *
SvGnomeVFSDirectoryHandle (SV *handle)
{
	return INT2PTR (GnomeVFSDirectoryHandle *, SvIV (SvRV (handle)));

}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSAsyncHandle (GnomeVFSAsyncHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::Async::Handle", handle);
}

GnomeVFSAsyncHandle *
SvGnomeVFSAsyncHandle (SV *handle)
{
	return INT2PTR (GnomeVFSAsyncHandle *, SvIV (SvRV (handle)));

}

/* ------------------------------------------------------------------------- */

#if VFS_CHECK_VERSION (2, 8, 0)

SV *
newSVGnomeVFSDNSSDBrowseHandle (GnomeVFSDNSSDBrowseHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::DNSSD::Browse::Handle", handle);
}

GnomeVFSDNSSDBrowseHandle *
SvGnomeVFSDNSSDBrowseHandle (SV *handle)
{
	return INT2PTR (GnomeVFSDNSSDBrowseHandle *, SvIV (SvRV (handle)));

}

#endif /* 2.8 */

/* ------------------------------------------------------------------------- */

#if VFS_CHECK_VERSION (2, 8, 0)

SV *
newSVGnomeVFSDNSSDResolveHandle (GnomeVFSDNSSDResolveHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::DNSSD::Resolve::Handle", handle);
}

GnomeVFSDNSSDResolveHandle *
SvGnomeVFSDNSSDResolveHandle (SV *handle)
{
	return INT2PTR (GnomeVFSDNSSDResolveHandle *, SvIV (SvRV (handle)));

}

#endif /* 2.8 */

/* ------------------------------------------------------------------------- */

#if VFS_CHECK_VERSION (2, 8, 0)

SV *
newSVGnomeVFSResolveHandle (GnomeVFSResolveHandle *handle)
{
	SV *sv = newSV (0);

	return sv_setref_pv (sv, "Gnome2::VFS::Resolve::Handle", handle);
}

GnomeVFSResolveHandle *
SvGnomeVFSResolveHandle (SV *handle)
{
	return INT2PTR (GnomeVFSResolveHandle *, SvIV (SvRV (handle)));

}

#endif /* 2.8 */

/* ------------------------------------------------------------------------- */

GnomeVFSApplication *
SvGnomeVFSApplication (SV *object)
{
	MAGIC *mg;

	if (!object || !SvOK (object) || !SvROK (object) || !(mg = mg_find (SvRV (object), PERL_MAGIC_ext)))
		return NULL;

	return (GnomeVFSApplication *) mg->mg_ptr;
}

SV *
newSVGnomeVFSApplication (GnomeVFSApplication *app_id)
{
	SV *rv;
	HV *stash;
	SV *object = (SV *) newHV ();

	sv_magic (object, 0, PERL_MAGIC_ext, app_id, 0);

	rv = newRV_noinc (object);
	stash = gv_stashpv ("Gnome2::VFS::Application", 1);

	return sv_bless (rv, stash);
}

/* ------------------------------------------------------------------------- */

#define VFS2PERL_CHECK_AND_STORE(_type, _key, _sv) \
		if (info->valid_fields & _type) \
			hv_store (object, _key, strlen (_key), _sv, 0);

SV *
newSVGnomeVFSFileInfo (GnomeVFSFileInfo *info)
{
	HV *object = newHV ();

	if (info && info->name && info->valid_fields) {
		hv_store (object, "name", 4, newSVpv (info->name, 0), 0);
		hv_store (object, "valid_fields", 12, newSVGnomeVFSFileInfoFields (info->valid_fields), 0);

		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_TYPE, "type", newSVGnomeVFSFileType (info->type));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS, "permissions", newSVGnomeVFSFilePermissions (info->permissions));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_FLAGS, "flags", newSVGnomeVFSFileFlags (info->flags));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_DEVICE, "device", newSViv (info->device));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_INODE, "inode", newSVuv (info->inode));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT, "link_count", newSVuv (info->link_count));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_SIZE, "size", newSVGnomeVFSFileSize (info->size));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT, "block_count", newSVGnomeVFSFileSize (info->block_count));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE, "io_block_size", newSVuv (info->io_block_size));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_ATIME, "atime", newSViv (info->atime));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_MTIME, "mtime", newSViv (info->mtime));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_CTIME, "ctime", newSViv (info->ctime));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME, "symlink_name", newSVpv (info->symlink_name, 0));
		VFS2PERL_CHECK_AND_STORE (GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE, "mime_type", newSVpv (info->mime_type, 0));

		/* FIXME: what about GNOME_VFS_FILE_INFO_FIELDS_ACCESS? */
	}

	return sv_bless (newRV_noinc ((SV *) object),
	                 gv_stashpv ("Gnome2::VFS::FileInfo", 1));
}

#define VFS2PERL_FETCH_AND_CHECK(_type, _key, _member, _sv) \
		if (hv_exists (hv, _key, strlen (_key))) { \
			value = hv_fetch (hv, _key, strlen (_key), FALSE); \
			if (value) _member = _sv; \
			info->valid_fields |= _type; \
		}

GnomeVFSFileInfo *
SvGnomeVFSFileInfo (SV *object)
{
	HV *hv = (HV *) SvRV (object);
	SV **value;

	GnomeVFSFileInfo *info = gperl_alloc_temp (sizeof (GnomeVFSFileInfo));

	if (object && SvOK (object) && SvROK (object) && SvTYPE (SvRV (object)) == SVt_PVHV) {
		value = hv_fetch (hv, "name", 4, FALSE);
		if (value) info->name = SvPV_nolen (*value);

		info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_NONE;

		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_TYPE, "type", info->type, SvGnomeVFSFileType (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS, "permissions", info->permissions, SvGnomeVFSFilePermissions (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_FLAGS, "flags", info->flags, SvGnomeVFSFileFlags (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_DEVICE, "device", info->device, SvIV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_INODE, "inode", info->inode, SvUV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_LINK_COUNT, "link_count", info->link_count, SvUV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_SIZE, "size", info->size, SvGnomeVFSFileSize (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_BLOCK_COUNT, "block_count", info->block_count, SvGnomeVFSFileSize (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_IO_BLOCK_SIZE, "io_block_size", info->io_block_size, SvUV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_ATIME, "atime", info->atime, SvIV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_MTIME, "mtime", info->mtime, SvIV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_CTIME, "ctime", info->ctime, SvIV (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_SYMLINK_NAME, "symlink_name", info->symlink_name, SvPV_nolen (*value));
		VFS2PERL_FETCH_AND_CHECK (GNOME_VFS_FILE_INFO_FIELDS_MIME_TYPE, "mime_type", info->mime_type, SvPV_nolen (*value));

		/* FIXME: what about GNOME_VFS_FILE_INFO_FIELDS_ACCESS? */
	}

	return info;
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSXferProgressInfo (GnomeVFSXferProgressInfo *info)
{
	HV * hv = newHV ();

	if (info) {
		hv_store (hv, "status", 6, newSVGnomeVFSXferProgressStatus (info->status), 0);
		hv_store (hv, "vfs_status", 10, newSVGnomeVFSResult (info->vfs_status), 0);
		hv_store (hv, "phase", 5, newSVGnomeVFSXferPhase (info->phase), 0);
		hv_store (hv, "file_index", 10, newSVuv (info->file_index), 0);
		hv_store (hv, "files_total", 11, newSVuv (info->files_total), 0);
		hv_store (hv, "bytes_total", 11, newSVuv (info->bytes_total), 0);
		hv_store (hv, "file_size", 9, newSVuv (info->file_size), 0);
		hv_store (hv, "bytes_copied", 12, newSVuv (info->bytes_copied), 0);
		hv_store (hv, "total_bytes_copied", 18, newSVuv (info->total_bytes_copied), 0);
		hv_store (hv, "top_level_item", 14, newSVuv (info->top_level_item), 0);

		if (info->source_name)
			hv_store (hv, "source_name", 11, newSVGChar (info->source_name), 0);

		if (info->target_name)
			hv_store (hv, "target_name", 11, newSVGChar (info->target_name), 0);

		if (info->duplicate_count)
			hv_store (hv, "duplicate_count", 15, newSViv (info->duplicate_count), 0);

		/* FIXME: add a version check once the fix from teuf makes it
		          into an official release. */
		if (info->duplicate_name && info->phase != GNOME_VFS_XFER_PHASE_COMPLETED)
			hv_store (hv, "duplicate_name", 14, newSVGChar (info->duplicate_name), 0);
	}

	return newRV_noinc ((SV*) hv);
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSMimeApplication (GnomeVFSMimeApplication *application)
{
	HV *hash = newHV ();

	if (application == NULL)
		return &PL_sv_undef;

#if VFS_CHECK_VERSION (2, 10, 0)
	sv_magic ((SV *) hash, 0, PERL_MAGIC_ext, (const char *) application, 0);
#endif

	hv_store (hash, "id", 2, newSVpv (application->id, 0), 0);
	hv_store (hash, "name", 4, newSVpv (application->name, 0), 0);
	hv_store (hash, "command", 7, newSVpv (application->command, 0), 0);
	hv_store (hash, "can_open_multiple_files", 23, newSVuv (application->can_open_multiple_files), 0);
	hv_store (hash, "expects_uris", 12, newSVGnomeVFSMimeApplicationArgumentType (application->expects_uris), 0);
	hv_store (hash, "requires_terminal", 17, newSVuv (application->requires_terminal), 0);

	if (application->supported_uri_schemes != NULL) {
		AV *array = newAV ();
		GList *i;

		for (i = application->supported_uri_schemes; i != NULL; i = i->next)
			av_push (array, newSVpv (i->data, 0));

		hv_store (hash, "supported_uri_schemes", 21, newRV_noinc ((SV *) array), 0);
	}

	return sv_bless (newRV_noinc ((SV *) hash),
	                 gv_stashpv ("Gnome2::VFS::Mime::Application", 1));
}

GnomeVFSMimeApplication *
SvGnomeVFSMimeApplication (SV *object)
{
#if VFS_CHECK_VERSION (2, 10, 0)
	GnomeVFSMimeApplication *application;
	MAGIC *mg;

	if (!object || !SvOK (object) || !SvROK (object) || !(mg = mg_find (SvRV (object), PERL_MAGIC_ext)))
		return NULL;

	application = (GnomeVFSMimeApplication *) mg->mg_ptr;
#else
	GnomeVFSMimeApplication *application = gperl_alloc_temp (sizeof (GnomeVFSMimeApplication));

	if (object && SvOK (object) && SvROK (object) && SvTYPE (SvRV (object)) == SVt_PVHV) {
		HV *hv = (HV *) SvRV (object);
		SV **value;

		value = hv_fetch (hv, "id", 2, FALSE);
		if (value) application->id = SvPV_nolen (*value);

		value = hv_fetch (hv, "name", 4, FALSE);
		if (value) application->name = SvPV_nolen (*value);

		value = hv_fetch (hv, "command", 7, FALSE);
		if (value) application->command = SvPV_nolen (*value);

		value = hv_fetch (hv, "can_open_multiple_files", 23, FALSE);
		if (value) application->can_open_multiple_files = SvUV (*value);

		value = hv_fetch (hv, "expects_uris", 12, FALSE);
		if (value) application->expects_uris = SvGnomeVFSMimeApplicationArgumentType (*value);

		value = hv_fetch (hv, "requires_terminal", 17, FALSE);
		if (value) application->requires_terminal = SvUV (*value);

		value = hv_fetch (hv, "supported_uri_schemes", 21, FALSE);
		if (value && *value && SvOK (*value) && SvROK (*value) && SvTYPE (SvRV (*value)) == SVt_PVAV) {
			AV *array = (AV *) SvRV (*value);
			int i;

			application->supported_uri_schemes = NULL;

			for (i = 0; i <= av_len (array); i++) {
				value = av_fetch (array, i, 0);

				if (value)
					application->supported_uri_schemes = g_list_append (application->supported_uri_schemes, SvPV_nolen (*value));
			}
		}
	}
#endif

	return application;
}

/* ------------------------------------------------------------------------- */

GnomeVFSMimeType *
SvGnomeVFSMimeType (SV *object)
{
	MAGIC *mg;

	if (!object || !SvOK (object) || !SvROK (object) || !(mg = mg_find (SvRV (object), PERL_MAGIC_ext)))
		return NULL;

	return (GnomeVFSMimeType *) mg->mg_ptr;
}

SV *
newSVGnomeVFSMimeType (GnomeVFSMimeType *mime_type)
{
	SV *rv;
	HV *stash;
	SV *object = (SV *) newHV ();

	sv_magic (object, 0, PERL_MAGIC_ext, mime_type, 0);

	rv = newRV_noinc (object);
	stash = gv_stashpv ("Gnome2::VFS::Mime::Type", 1);

	return sv_bless (rv, stash);
}

/* -------------------------------------------------------------------------  */

GList *
SvPVGList (SV *ref)
{
	int i;

	AV *array;
	SV **value;

	GList *list = NULL;

	if (! (SvRV (ref) && SvTYPE (SvRV (ref)) == SVt_PVAV))
		croak ("URI list has to be a reference to an array");

	array = (AV *) SvRV (ref);

	for (i = 0; i <= av_len (array); i++)
		if ((value = av_fetch (array, i, 0)) && SvOK (*value))
			list = g_list_append(list, SvPV_nolen (*value));

	return list;
}

/* ------------------------------------------------------------------------- */

GList *
SvGnomeVFSURIGList (SV *ref)
{
	int i;

	AV *array;
	SV **value;

	GList *list = NULL;

	if (! (SvRV (ref) && SvTYPE (SvRV (ref)) == SVt_PVAV))
		croak ("URI list has to be a reference to an array");

	array = (AV *) SvRV (ref);

	for (i = 0; i <= av_len (array); i++)
		if ((value = av_fetch (array, i, 0)) && SvOK (*value))
			list = g_list_append(list, SvGnomeVFSURI (*value));

	return list;
}

/* ------------------------------------------------------------------------- */

char **
SvEnvArray (SV *ref)
{
	char **result = NULL;

	if (SvOK (ref)) {
		if (SvRV (ref) && SvTYPE (SvRV (ref)) == SVt_PVAV) {
			AV *array = (AV *) SvRV (ref);
			SV **string;

			int i, length = av_len (array);
			result = g_new0 (char *, length + 2);

			for (i = 0; i <= length; i++)
				if ((string = av_fetch (array, i, 0)) && SvOK (*string))
					result[i] = SvPV_nolen (*string);

			result[length + 1] = NULL;
		}
		else
			croak ("the environment parameter must be an array reference");
	}

	return result;
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSFileInfoGList (GList *list)
{
	AV *array = newAV ();

	for (; list != NULL; list = list->next)
		av_push (array, newSVGnomeVFSFileInfo (list->data));

	return newRV_noinc ((SV *) array);
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSGetFileInfoResultGList (GList *list)
{
	AV *array = newAV ();

	for (; list != NULL; list = list->next) {
		HV *hash = newHV ();
		GnomeVFSGetFileInfoResult* result = list->data;

		gnome_vfs_uri_ref (result->uri);

		hv_store (hash, "uri", 3, newSVGnomeVFSURI (result->uri), 0);
		hv_store (hash, "result", 6, newSVGnomeVFSResult (result->result), 0);
		hv_store (hash, "file_info", 9, newSVGnomeVFSFileInfo (result->file_info), 0);

		av_push (array, newRV_noinc ((SV *) hash));
	}

	return newRV_noinc ((SV *) array);
}

/* ------------------------------------------------------------------------- */

SV *
newSVGnomeVFSFindDirectoryResultGList (GList *list)
{
	AV *array = newAV ();

	for (; list != NULL; list = list->next) {
		HV *hash = newHV ();
		GnomeVFSFindDirectoryResult* result = list->data;

		hv_store (hash, "result", 6, newSVGnomeVFSResult (result->result), 0);

		if (result->uri) {
			gnome_vfs_uri_ref (result->uri);
			hv_store (hash, "uri", 3, newSVGnomeVFSURI (result->uri), 0);
		}

		av_push (array, newRV_noinc ((SV *) hash));
	}

	return newRV_noinc ((SV *) array);
}
