/*
 *	Notifies Endeavour about objects being added, modified, or
 *	removed.
 */

#include <string.h>
#include <glib.h>
#include <endeavour2.h>
#include "../config.h"


typedef enum {
	NOTIFY_ADDED,
	NOTIFY_MODIFIED,
	NOTIFY_RENAMED,
	NOTIFY_REMOVED
} notify_action;


static void print_help(const gchar *prog_name);


#define ATOI(s)		(((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)		(((s) != NULL) ? atol(s) : 0)
#define ATOF(s)		(((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)	(((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)	(((a) > (b)) ? (a) : (b))
#define MIN(a,b)	(((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)	(MIN(MAX((a),(l)),(h)))
#define STRLEN(s)	(((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)	(((s) != NULL) ? (*(s) == '\0') : TRUE)


static void print_help(const gchar *prog_name)
{
	g_print(
"Usage: %s --added <new_object> [options]\n\
       %s --modified <object> [options]\n\
       %s --renamed <old_object> <new_object> [options]\n\
       %s --removed <object> [options]\n",
	    prog_name,
	    prog_name,
	    prog_name,
	    prog_name
	);
	g_print("\n\
    The [options] can be any of the following:\n\
\n\
	--added                 Send an object added event.\n\
	-a                      Same as --added.\n\
	--modified              Send an object modified event (for\n\
				renamed, see --renamed).\n\
	-m                      Same as --modified.\n\
	--renamed               Send an object modified event for\n\
				renamed objects (must have two object\n\
				paths secified, one with the old name\n\
				and one with the new name).\n\
	-n                      Same as --renamed.\n\
	--removed               Send an object removed event.\n\
	-r                      Same as --removed.\n\
\n\
	--help                  Prints (this) help screen and exits.\n\
	--version               Prints version information and exits.\n\
\n\
    Return values:\n\
\n\
	0       Success.\n\
	1       General error.\n\
	2       Invalid value.\n\
	3       Systems error or memory allocation error.\n\
	4       User aborted.\n\
\n"
	);
}

int main(int argc, char *argv[])
{
	notify_action action = NOTIFY_ADDED;
	gint i, status;
	const gchar *arg;
	GList *file_list = NULL;
	edv_context_struct *ctx = EDVContextNew();

#define DO_FREE_LOCALS	{		\
 g_list_foreach(			\
  file_list, (GFunc)g_free, NULL	\
 );					\
 g_list_free(file_list);		\
 file_list = NULL;			\
					\
 EDVContextDelete(ctx);			\
 ctx = NULL;				\
}

	EDVContextLoadConfigurationFile(ctx, NULL);

	/* Insufficient arguments? */
	if(argc <= 1)
	{
	    print_help(argv[0]);
	    DO_FREE_LOCALS
	    return(2);
	}

	/* Determine the action based on the program's name */
	arg = (argc > 0) ? argv[0] : NULL;
	if(arg != NULL)
	{
	    if(strstr(arg, "added") != NULL)
		action = NOTIFY_ADDED;
	    else if(strstr(arg, "modified") != NULL)
		action = NOTIFY_MODIFIED;
	    if(strstr(arg, "renamed"))
		action = NOTIFY_RENAMED;
	    if(strstr(arg, "removed"))
		action = NOTIFY_REMOVED;
	}

	/* Handle the arguments */
	for(i = 1; i < argc; i++)
	{
	    arg = argv[i];
	    if(arg == NULL)
		continue;

	    /* Help? */
	    if(!g_strcasecmp(arg, "--help") ||
	       !g_strcasecmp(arg, "-help") ||
	       !g_strcasecmp(arg, "--h") ||
	       !g_strcasecmp(arg, "-h")
	    )
	    {
		print_help(argv[0]);
		DO_FREE_LOCALS
		return(0);
	    }
	    /* Version? */
	    else if(!g_strcasecmp(arg, "--version") ||
		    !g_strcasecmp(arg, "-version")
	    )
	    {
		g_print(
"Endeavour Mark II Properties Version " PROG_VERSION "\n"
PROG_COPYRIGHT
		);
		DO_FREE_LOCALS
		return(0);
	    }
	    /* Added? */
	    else if(!g_strcasecmp(arg, "--added") ||
		    !g_strcasecmp(arg, "-added") ||
		    !g_strcasecmp(arg, "--a") ||
		    !g_strcasecmp(arg, "-a")
	    )
	    {
		action = NOTIFY_ADDED;
	    }
	    /* Modified? */
	    else if(!g_strcasecmp(arg, "--modified") ||
		    !g_strcasecmp(arg, "-modified") ||
		    !g_strcasecmp(arg, "--m") ||
		    !g_strcasecmp(arg, "-m")
	    )
	    {
		action = NOTIFY_MODIFIED;
	    }
	    /* Renamed? */
	    else if(!g_strcasecmp(arg, "--renamed") ||
		    !g_strcasecmp(arg, "-renamed") ||
		    !g_strcasecmp(arg, "--n") ||
		    !g_strcasecmp(arg, "-n")
	    )
	    {
		action = NOTIFY_RENAMED;
	    }
	    /* Removed? */
	    else if(!g_strcasecmp(arg, "--removed") ||
		    !g_strcasecmp(arg, "-removed") ||
		    !g_strcasecmp(arg, "--r") ||
		    !g_strcasecmp(arg, "-r")
	    )
	    {
		action = NOTIFY_REMOVED;
	    }
	    else if((*arg != '-') && (*arg != '+'))
	    {
		file_list = g_list_append(file_list, STRDUP(arg));
	    }
	}

	/* Handle by action */
	status = 0;
	switch(action)
	{
	  case NOTIFY_ADDED:
	    if(g_list_length(file_list) >= 1)
	    {
		/* Queue an object added event
		 *
		 * It will be sent when EDVContextSync() is called
		 *
		 * All specified paths must be absolute paths
		 */
		const gchar *path = (const gchar *)g_list_nth_data(
		    file_list, 0
		);
		if(path != NULL)
		{
		    gchar *new_path;

		    if(g_path_is_absolute(path))
			new_path = STRDUP(path);
		    else
			new_path = g_strdup_printf(
			    "%s%c%s",
			    g_get_current_dir(),
			    G_DIR_SEPARATOR,
			    path
			);
		    EDVNotifyQueueObjectAdded(ctx, new_path);
		    g_free(new_path);
		}
		else
		{
		    print_help(argv[0]);
		    status = 2;
		}
	    }
	    else
	    {
		print_help(argv[0]);
		status = 2;
	    }
	    break;

	  case NOTIFY_MODIFIED:
	    if(g_list_length(file_list) >= 1)
	    {
		/* Queue an object modified event
		 *
		 * Note that new_path as passed for both the old and
		 * the newp paths
		 *
		 * It will be sent when EDVContextSync() is called
		 */
		const gchar *path = (const gchar *)g_list_nth_data(
		    file_list, 0
		);
		if(path != NULL)
		{
		    gchar *new_path;

		    if(g_path_is_absolute(path))
			new_path = STRDUP(path);
		    else
			new_path = g_strdup_printf(
			    "%s%c%s",
			    g_get_current_dir(),
			    G_DIR_SEPARATOR,
			    path
			);
		    EDVNotifyQueueObjectModified(ctx, new_path, new_path);
		    g_free(new_path);
		}
		else
		{
		    print_help(argv[0]);
		    status = 2;
		}
	    }
	    else
	    {
		print_help(argv[0]);
		status = 2;
	    }
	    break;

	  case NOTIFY_RENAMED:
	    if(g_list_length(file_list) >= 2)
	    {
		/* Queue an object modified event for the renamed
		 * notify specifying the old and new paths
		 *
		 * It will be sent when EDVContextSync() is called
		 */
		const gchar	*path1 = (const gchar *)g_list_nth_data(
		    file_list, 0
		),
				*path2 = (const gchar *)g_list_nth_data(
		    file_list, 1
		);
		if((path1 != NULL) && (path2 != NULL))
		{
		    gchar *old_path, *new_path;

		    if(g_path_is_absolute(path1))
			old_path = STRDUP(path1);
		    else
			old_path = g_strdup_printf(
			    "%s%c%s",
			    g_get_current_dir(),
			    G_DIR_SEPARATOR,
			    path1
			);

		    if(g_path_is_absolute(path2))
			new_path = STRDUP(path2);
		    else
			new_path = g_strdup_printf(
			    "%s%c%s",
			    g_get_current_dir(),
			    G_DIR_SEPARATOR,
			    path2
			);

		    EDVNotifyQueueObjectModified(ctx, old_path, new_path);
		    g_free(old_path);
		    g_free(new_path);
		}
		else
		{
		    print_help(argv[0]);
		    status = 2;
		}
	    }
	    else
	    {
		print_help(argv[0]);
		status = 2;
	    }
	    break;

	  case NOTIFY_REMOVED:
	    if(g_list_length(file_list) >= 1)
	    {
		/* Queue an object removed event
		 *
		 * It will be sent when EDVContextSync() is called
		 */
		const gchar *path = (const gchar *)g_list_nth_data(
		    file_list, 0
		);
		if(path != NULL)
		{
		    gchar *new_path;

		    if(g_path_is_absolute(path))
			new_path = STRDUP(path);
		    else
			new_path = g_strdup_printf(
			    "%s%c%s",
			    g_get_current_dir(),
			    G_DIR_SEPARATOR,
			    path
			);
		    EDVNotifyQueueObjectRemoved(ctx, new_path);
		    g_free(new_path);
		}
		else
		{
		    print_help(argv[0]);
		    status = 2;
		}
	    }
	    else
	    {
		print_help(argv[0]);
		status = 2;
	    }
	    break;
	}

	EDVContextSync(ctx);

	DO_FREE_LOCALS

	return(status);
#undef DO_FREE_LOCALS
}
