/*  XMMS2 - X Music Multiplexer System
 *  Copyright (C) 2003-2007 XMMS2 Team
 *
 *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
 *
 *  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.
 */
#include "cmd_coll.h"
#include "common.h"



cmds coll_commands[] = {
	{ "save", "[collname] [pattern] - Save a pattern as a collection", cmd_coll_save },
	{ "rename", "[oldname] [newname] - Rename a collection", cmd_coll_rename },
	{ "list", "[namespace] - List all collections in a given namespace", cmd_coll_list },
	{ "query", "[collname] [order] - Display all the media in a collection", cmd_coll_query },
	{ "queryadd", "[collname] [order] - Add all media in a collection to active playlist", cmd_coll_queryadd },
	{ "find", "[mid] [namespace] - Find all collections that contain the given media", cmd_coll_find },
	{ "get", "[collname] - Display the structure of a collection", cmd_coll_get },
	{ "remove", "[collname] - Remove a saved collection", cmd_coll_remove },
	{ "attr", "[collname] [attr] [val] - Get/set an attribute for a saved collection", cmd_coll_attr },
	{ NULL, NULL, NULL },
};


static void
cmd_coll_help (void) {
	gint i;

	print_info ("Available collection commands:");
	for (i = 0; coll_commands[i].name; i++) {
		print_info ("  %s\t %s", coll_commands[i].name,
		            coll_commands[i].help);
	}
}

/* Dispatch collection subcommands */
void
cmd_coll (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gint i;
	if (argc < 3) {
		cmd_coll_help();
		return;
	}

	for (i = 0; coll_commands[i].name; i++) {
		if (g_strcasecmp (coll_commands[i].name, argv[2]) == 0) {
			coll_commands[i].func (conn, argc, argv);
			return;
		}
	}

	cmd_coll_help();
	print_error ("Unrecognised coll command: %s", argv[2]);
}


static void
coll_list (xmmsc_connection_t *conn, gchar *namespace)
{
	xmmsc_result_t *res;

	res = xmmsc_coll_list (conn, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("Couldn't list collections in namespace %s: %s",
		             namespace, xmmsc_result_get_error (res));
	} else {
		gchar *name;
		while (xmmsc_result_list_valid (res)) {
			xmmsc_result_get_string (res, &name);
			print_info ("%s", name);
			xmmsc_result_list_next (res);
		}
	}
	xmmsc_result_unref (res);
}

static void
coll_find (xmmsc_connection_t *conn, gchar *namespace, guint mid)
{
	xmmsc_result_t *res;

	res = xmmsc_coll_find (conn, mid, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("Couldn't find collections containing media %d in namespace %s: %s",
		             mid, namespace, xmmsc_result_get_error (res));
	} else {
		gchar *name;
		while (xmmsc_result_list_valid (res)) {
			xmmsc_result_get_string (res, &name);
			print_info ("%s", name);
			xmmsc_result_list_next (res);
		}
	}
	xmmsc_result_unref (res);
}

/* Produce a GString from the idlist of the collection (must be freed manually!) */
static GString *
coll_idlist_to_string (xmmsc_coll_t *coll)
{
	gint i;
	guint *idlist;
	GString *s;

	s = g_string_new ("(");

	idlist = xmmsc_coll_get_idlist (coll);
	for (i = 0; idlist[i] != 0; ++i) {
		if(i > 0) {
			g_string_append (s, ", ");
		}
		g_string_append_printf (s, "%d", idlist[i]);
	}
	g_string_append (s, ")");

	return s;
}

/* Dump the structure of the collection as a string */
static void
coll_dump (xmmsc_coll_t *coll, unsigned int level)
{
	gint i;
	gchar *indent;

	gchar *attr1;
	gchar *attr2;
	xmmsc_coll_t *operand;
	GString *idlist_str;

	indent = (gchar*) malloc ((level * 2) + 1);
	for (i = 0; i < level * 2; ++i) {
		indent[i] = ' ';
	}
	indent[i] = '\0';

	/* type */
	switch (xmmsc_coll_get_type (coll)) {
	case XMMS_COLLECTION_TYPE_REFERENCE:
		xmmsc_coll_attribute_get (coll, "reference", &attr1);
		print_info ("%sReference: '%s'", indent, attr1);
		break;

	case XMMS_COLLECTION_TYPE_UNION:
		print_info ("%sUnion:", indent);
		for(xmmsc_coll_operand_list_first (coll);
			xmmsc_coll_operand_list_entry (coll, &operand);
			xmmsc_coll_operand_list_next (coll)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_INTERSECTION:
		print_info ("%sIntersection:", indent);
		for (xmmsc_coll_operand_list_first (coll);
			 xmmsc_coll_operand_list_entry (coll, &operand);
			 xmmsc_coll_operand_list_next (coll)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_COMPLEMENT:
		print_info ("%sComplement:", indent);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_EQUALS:
		xmmsc_coll_attribute_get(coll, "field",  &attr1);
		xmmsc_coll_attribute_get(coll, "value", &attr2);
		print_info ("%sEquals ('%s', '%s') for:", indent, attr1, attr2);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_HAS:
		xmmsc_coll_attribute_get(coll, "field",  &attr1);
		print_info ("%sHas ('%s') for:", indent, attr1);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_MATCH:
		xmmsc_coll_attribute_get(coll, "field",  &attr1);
		xmmsc_coll_attribute_get(coll, "value", &attr2);
		print_info ("%sMatch ('%s', '%s') for:", indent, attr1, attr2);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_SMALLER:
		xmmsc_coll_attribute_get(coll, "field",  &attr1);
		xmmsc_coll_attribute_get(coll, "value", &attr2);
		print_info ("%sSmaller ('%s', '%s') for:", indent, attr1, attr2);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_GREATER:
		xmmsc_coll_attribute_get(coll, "field",  &attr1);
		xmmsc_coll_attribute_get(coll, "value", &attr2);
		print_info ("%sGreater ('%s', '%s') for:", indent, attr1, attr2);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	case XMMS_COLLECTION_TYPE_IDLIST:
		idlist_str = coll_idlist_to_string (coll);
		print_info ("%sIdlist: %s", indent, idlist_str->str);
		g_string_free (idlist_str, TRUE);
		break;

	case XMMS_COLLECTION_TYPE_QUEUE:
		idlist_str = coll_idlist_to_string (coll);
		print_info ("%sQueue: %s", indent, idlist_str->str);
		g_string_free (idlist_str, TRUE);
		break;

	case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
		idlist_str = coll_idlist_to_string (coll);
		print_info ("%sParty Shuffle: %s from :", indent, idlist_str->str);
		g_string_free (idlist_str, TRUE);
		xmmsc_coll_operand_list_first (coll);
		if (xmmsc_coll_operand_list_entry (coll, &operand)) {
			coll_dump (operand, level + 1);
		}
		break;

	default:
		print_info ("%sUnknown Operator!", indent);
		break;
	}
}

static void
coll_print (xmmsc_coll_t *coll)
{
	coll_dump (coll, 0);
}

static void
coll_attr_print (const gchar *key, const gchar *val, gpointer userdata)
{
	print_info ("[%s] %s", (gchar*)key, (gchar*)val);
}



void
cmd_coll_save (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	xmmsc_coll_t *coll = NULL;
	xmmsc_result_t *res;
	gchar *pattern;
	gchar **args;
	int i;

	if (argc < 5) {
		print_error ("usage: coll save [collname] [pattern]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	args = g_new0 (char*, argc - 3);
	for (i = 0; i < argc - 4; i++) {
		args[i] = string_escape (argv[i + 4]);
	}
	args[i] = NULL;

	pattern = g_strjoinv (" ", args);
	if (!xmmsc_coll_parse (pattern, &coll)) {
		print_error ("Unable to generate query");
	}

	for (i = 0; i < argc - 4; i++) {
		g_free (args[i]);
	}

	g_free (pattern);

	res = xmmsc_coll_save (conn, coll, name, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("Couldn't save %s in namespace %s: %s", name, namespace,
		             xmmsc_result_get_error (res));
	}
	xmmsc_result_unref (res);
	xmmsc_coll_unref (coll);
	g_free (name);
	g_free (namespace);
}


void
cmd_coll_rename (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *from_name, *from_namespace;
	gchar *to_name, *to_namespace;
	xmmsc_result_t *res;

	if (argc < 5) {
		print_error ("usage: coll rename [collname] [newname]");
	}

	if (!coll_read_collname (argv[3], &from_name, &from_namespace)) {
		print_error ("invalid old collection name");
	}

	if (!coll_read_collname (argv[4], &to_name, &to_namespace)) {
		print_error ("invalid new collection name");
	}

	if (strcmp (from_namespace, to_namespace) != 0) {
		print_error ("cannot rename collection to a different namespace");
	}

	res = xmmsc_coll_rename (conn, from_name, to_name, from_namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("Couldn't rename %s to %s in namespace %s: %s",
		             from_name, to_name, from_namespace,
		             xmmsc_result_get_error (res));
	}
	xmmsc_result_unref (res);
	g_free (from_name);
	g_free (from_namespace);
	g_free (to_name);
	g_free (to_namespace);
}


void
cmd_coll_list (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	/* No namespace, show all namespaces */
	if (argc < 4) {
		print_info ("* %s Namespace:", "Playlists");
		coll_list (conn, "Playlists");
		print_info ("* %s Namespace:", "Collections");
		coll_list (conn, "Collections");
	} else {
		coll_list (conn, argv[3]);
	}
}


void
cmd_coll_query (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	gchar **order = NULL;
	xmmsc_coll_t *collref;
	xmmsc_result_t *res;
	GList *n = NULL;

	if (argc < 4) {
		print_error ("usage: coll query [collname] [order]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	/* allow custom ordering, if specified */
	if (argc > 4) {
		order = g_strsplit (argv[4], ",", 0);
		g_assert (order);
	}

	/* Create a reference collection to the saved coll */
	collref = xmmsc_coll_new (XMMS_COLLECTION_TYPE_REFERENCE);
 	xmmsc_coll_attribute_set (collref, "reference", name);
 	xmmsc_coll_attribute_set (collref, "namespace", namespace);

	res = xmmsc_coll_query_ids (conn, collref, (const gchar**)order, 0, 0);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("%s", xmmsc_result_get_error (res));
	}

	while (xmmsc_result_list_valid (res)) {
		guint id;

		if (!xmmsc_result_get_uint (res, &id)) {
			print_error ("Broken resultset");
		}

		n = g_list_prepend (n, XINT_TO_POINTER (id));
		xmmsc_result_list_next (res);
	}
	n = g_list_reverse (n);
	format_pretty_list (conn, n);
	g_list_free (n);

	xmmsc_result_unref (res);

	if (order) {
		g_strfreev (order);
	}

	xmmsc_coll_unref (collref);
	g_free (name);
	g_free (namespace);
}

void
cmd_coll_queryadd (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	gchar **order = NULL;
	xmmsc_coll_t *collref;
	xmmsc_result_t *res;

	if (argc < 4) {
		print_error ("usage: coll queryadd [collname] [order]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	/* allow custom ordering, if specified */
	if (argc > 4) {
		order = g_strsplit (argv[4], ",", 0);
		g_assert (order);
	}

	/* Create a reference collection to the saved coll */
	collref = xmmsc_coll_new (XMMS_COLLECTION_TYPE_REFERENCE);
	xmmsc_coll_attribute_set (collref, "reference", name);
	xmmsc_coll_attribute_set (collref, "namespace", namespace);

	res = xmmsc_playlist_add_collection (conn, NULL, collref, (const gchar**)order);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("%s", xmmsc_result_get_error (res));
	}

	xmmsc_result_unref (res);

	if (order) {
		g_strfreev (order);
	}

	xmmsc_coll_unref (collref);
	g_free (name);
	g_free (namespace);
}

void
cmd_coll_find (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	guint mid;

	if (argc < 4) {
		print_error ("usage: coll find [mid] [namespace]");
	}

	mid = strtoul (argv[3], NULL, 10);
	if (!mid) {
		print_error ("invalid media id");
	}

	if (argc < 5) {
		print_info ("* %s Namespace:", "Playlists");
		coll_find (conn, "Playlists", mid);
		print_info ("* %s Namespace:", "Collections");
		coll_find (conn, "Collections", mid);
	} else {
		coll_find (conn, argv[4], mid);
	}
}


void
cmd_coll_get (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	xmmsc_result_t *res;

	if (argc < 4) {
		print_error ("usage: coll get [collname]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	res = xmmsc_coll_get (conn, name, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("%s", xmmsc_result_get_error (res));
	} else {
		xmmsc_coll_t *coll;
		xmmsc_result_get_collection (res, &coll);
		coll_print (coll);
	}
}


void
cmd_coll_remove (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	xmmsc_result_t *res;

	if (argc < 4) {
		print_error ("usage: coll remove [collname]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	res = xmmsc_coll_remove (conn, name, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("%s", xmmsc_result_get_error (res));
	}
}


void
cmd_coll_attr (xmmsc_connection_t *conn, gint argc, gchar **argv)
{
	gchar *name, *namespace;
	xmmsc_result_t *res;

	if (argc < 4) {
		print_error ("usage: coll attr [collname] [attr] [val]");
	}

	if (!coll_read_collname (argv[3], &name, &namespace)) {
		print_error ("invalid collection name");
	}

	res = xmmsc_coll_get (conn, name, namespace);
	xmmsc_result_wait (res);

	if (xmmsc_result_iserror (res)) {
		print_error ("%s", xmmsc_result_get_error (res));
	} else {
		xmmsc_coll_t *coll;
		xmmsc_result_get_collection (res, &coll);

		/* Print all attributes */
		if (argc == 4) {
			xmmsc_coll_attribute_foreach (coll, coll_attr_print, NULL);

		/* Print the given attribute */
		} else if (argc == 5) {
			gchar *val;
			if (xmmsc_coll_attribute_get (coll, argv[4], &val)) {
				coll_attr_print (argv[4], val, NULL);
			}

		/* Set the given attribute */
		} else {
			xmmsc_result_t *saveres;
			if (strlen (argv[5]) > 0) {
				xmmsc_coll_attribute_set (coll, argv[4], argv[5]);
			} else {
				/* Empty value = remove */
				xmmsc_coll_attribute_remove (coll, argv[4]);
			}

			saveres = xmmsc_coll_save (conn, coll, name, namespace);
			xmmsc_result_wait (saveres);

			if (xmmsc_result_iserror (saveres)) {
				print_error ("Couldn't save %s in namespace %s: %s",
				             name, namespace,
				             xmmsc_result_get_error (saveres));
			}

			xmmsc_result_unref (saveres);
		}

		xmmsc_coll_unref (coll);
	}

	xmmsc_result_unref (res);
}
