// gcc -g -Wall -o test test.c `pkg-config --libs --cflags gtk+-2.0` -larchive

#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <archive.h>
#include <archive_entry.h>

#define GNOMELOCALEDIR "/usr/share/locale"
#define GETTEXT_PACKAGE "empathy" //FIXME remove

#define CONTENTS_PREFIX ".AdiumMessageStyle/Contents/"
#define INCOMING_PREFIX CONTENTS_PREFIX "Resources/Incoming/"

static char **uris = NULL;
static int retval = 0;

static const GOptionEntry entries[] = {
	{G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &uris, "Theme to install, starting with adiumxtra://", NULL},
	{NULL}
};

static struct archive *
setup_archive (char *contents, gsize length)
{
	struct archive *zip;

	//FIXME error checking
	zip = archive_read_new ();
	archive_read_support_compression_all (zip);
	archive_read_support_format_all (zip);
	if (archive_read_open_memory (zip, (void *) contents, length) != 0) {
		g_warning ("Failed to read archive '%s'", uris[0]);
		return NULL;
	}

	return zip;
}

static int
copy_data (struct archive *ar, struct archive *aw)
{
	int r;
	const void *buff;
	size_t size;
	off_t offset;

	while (1) {
		r = archive_read_data_block (ar, &buff, &size, &offset);
		if (r == ARCHIVE_EOF)
			return ARCHIVE_OK;
		if (r != ARCHIVE_OK)
			return r;
		r = archive_write_data_block (aw, buff, size, offset);
		if (r != ARCHIVE_OK)
			return r;
	}
}

static gboolean
is_theme (const char *path,
	  GHashTable *themes)
{
	char **items;
	gboolean ret;

	items = g_strsplit (path, "/", -1);
	if (items[0] != NULL)
		ret = (g_hash_table_lookup (themes, items[0]) != NULL);
	else
		ret = FALSE;
	g_strfreev (items);

	return ret;
}

static gboolean
extract_themes (GHashTable *themes,
		struct archive *ar)
{
	struct archive *ext;
	struct archive_entry *entry;
	int flags;
	int r;
	gboolean retval;

	/* Attributes restore */
	flags = ARCHIVE_EXTRACT_TIME;
	flags |= ARCHIVE_EXTRACT_PERM;
	flags |= ARCHIVE_EXTRACT_ACL;
	flags |= ARCHIVE_EXTRACT_FFLAGS;

	retval = TRUE;

	//FIXME cwd() to the empathy themes directory
	ext = archive_write_disk_new ();
	archive_write_disk_set_options (ext, flags);
	archive_write_disk_set_standard_lookup (ext);
	while (1) {
		const char *path;

		r = archive_read_next_header(ar, &entry);
		if (r == ARCHIVE_EOF)
			break;
		if (r < ARCHIVE_WARN) {
			retval = FALSE;
			break;
		}
		path = archive_entry_pathname (entry);
		if (path == NULL) {
			archive_read_data_skip (ar);
			continue;
		}
		/* Skip leading slashes */
		if (path[0] == '/')
			path++;
		if (is_theme (path, themes) == FALSE) {
			archive_read_data_skip (ar);
			continue;
		}

		r = archive_write_header (ext, entry);
		if (r != ARCHIVE_OK)
			fprintf(stderr, "%s\n", archive_error_string(ext));
		else if (archive_entry_size (entry) > 0) {
			copy_data (ar, ext);
			if (r < ARCHIVE_WARN) {
				retval = FALSE;
				break;
			}
		}
		r = archive_write_finish_entry (ext);
		if (r < ARCHIVE_WARN) {
			retval = FALSE;
			break;
		}
	}

	archive_write_close(ext);
	archive_write_finish(ext);

	return retval;
}

static char *
get_theme_name (const char *path)
{
	char **items;
	char *ret;

	items = g_strsplit (path, "/", -1);
	if (items[0] == NULL)
		goto bail;
	if (g_str_has_suffix (items[0], ".AdiumMessageStyle") == FALSE)
		goto bail;

	ret = g_strdup (items[0]);
	g_strfreev (items);

	return ret;
bail:
	g_strfreev (items);
	return NULL;
}

static void
file_loaded (GObject *source_object,
	     GAsyncResult *res,
	     gpointer user_data)
{
	GError *error = NULL;
	char *contents;
	gsize length;

	struct archive *zip;
	struct archive_entry *entry;

	char *previous_theme;
	gboolean has_info, has_content;
	GHashTable *themes;

	if (g_file_load_contents_finish (G_FILE (source_object),
					 res,
					 &contents,
					 &length,
					 NULL,
					 &error) == FALSE) {
		g_warning ("Failed to load '%s': %s", uris[0], error->message);
		g_error_free (error);
		retval = 1;
		gtk_main_quit ();
		return;
	}

	previous_theme = NULL;
	has_info = has_content = FALSE;
	themes = g_hash_table_new_full (g_str_hash, g_str_equal,
					g_free, NULL);

	//FIXME error checking, somewhat
	zip = setup_archive (contents, length);
	while (archive_read_next_header (zip, &entry) == ARCHIVE_OK) {
		const char *path;
		char *name;

		path = archive_entry_pathname (entry);
		if (path == NULL) {
			archive_read_data_skip (zip);
			continue;
		}
		/* Skip leading slashes */
		if (path[0] == '/')
			path++;
		name = get_theme_name (path);
		if (name == NULL) {
			g_message ("no theme name: %s", path);
			archive_read_data_skip (zip);
			continue;
		}

		/* Check whether we're still in the same theme */
		if (previous_theme == NULL)
			previous_theme = g_strdup (name);

		if (g_strcmp0 (name, previous_theme) != 0) {
			g_free (previous_theme);
			previous_theme = g_strdup (name);
			has_info = has_content = FALSE;
		}

		if (g_str_has_suffix (path, CONTENTS_PREFIX "Info.plist")) {
			g_message ("has info");
			has_info = TRUE;
		} else if (g_str_has_suffix (path, INCOMING_PREFIX "Content.html")) {
			g_message ("has content");
			has_content = TRUE;
		}

		if (has_info && has_content) {
			g_message ("adding %s to the list", name);
			g_hash_table_insert (themes, g_strdup (name), GINT_TO_POINTER (TRUE));
			has_info = has_content = FALSE;
		}

		g_free (name);

		archive_read_data_skip (zip);
	}
	archive_read_finish (zip);

	if (g_hash_table_size (themes) == 0) {
		retval = 1;
		g_hash_table_destroy (themes);
		g_warning ("No Adium chat themes in '%s'", uris[0]);
		gtk_main_quit ();
		return;
	}

	zip = setup_archive (contents, length);
	if (extract_themes (themes, zip) == FALSE) {
		retval = 1;
		g_warning ("Failed to extract themes");
	}
	archive_read_finish (zip);

	g_hash_table_destroy (themes);

	g_free (contents);

	gtk_main_quit ();
}

int main (int argc, char **argv)
{
	GOptionContext *context;
	GFile *file;
	GError *error = NULL;
	char *str;

	bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
	textdomain (GETTEXT_PACKAGE);

	context = g_option_context_new ("Install Adium themes");
	g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
	g_type_init ();

	if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
		g_print ("couldn't parse command-line options: %s\n", error->message);
		g_error_free (error);
		return 1;
	}

	if (uris == NULL || g_strv_length (uris) != 1 || g_str_has_prefix (uris[0], "adiumxtra://") == FALSE) {
		char *help;

		g_strfreev (uris);
		help = g_option_context_get_help (context, FALSE, NULL);
		g_print ("%s", help);
		g_free (help);

		return 1;
	}

	str = g_strdup_printf ("http://%s", uris[0] + strlen ("adiumxtra://"));
	file = g_file_new_for_commandline_arg (str);
	g_free (str);

	g_file_load_contents_async (file, NULL, file_loaded, NULL);

	gtk_main ();

	return retval;
}
