/*
 *	Opens an object in accordance with the MIME Types defined by
 *	Endeavour's MIME Types list.
 */

#include <errno.h>
#include <sys/stat.h>
#include <glib.h>
#include <endeavour2.h>

#include "../config.h"


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 <path(s)...> [options]\n",
	    prog_name
	);
	g_print("\n\
    The <path(s)...> specifies the paths to the object(s) that are\n\
    to be opened.\n\
\n\
    The [options] can be any of the following:\n\
\n\
	--edit                  Open using the edit command (if\n\
				defined on the MIME Type)\n\
	-e                      Same as --edit.\n\
	--command-name <name>   Open using the <name> command (if\n\
				defined on the MIME Type)\n\
	-c                      Same as --command-name.\n\
\n\
	--list-commands         List the commands available for the\n\
				specified object(s).\n\
	-l                      Same as --list-commands.\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 or no such object.\n\
	3       Systems error or memory allocation error.\n\
	4       User aborted.\n\
	7       No associated MIME Type was found.\n\
\n"
	);
}


static void ListCommands(
	edv_context_struct *ctx,
	GList *paths_list
)
{
	struct stat stat_buf;
	const gchar *path;
	GList *glist2;
	edv_mime_type_struct *m;
	edv_mime_type_command_struct *cmd;
	GList *glist = paths_list;
	for(;glist != NULL; glist = g_list_next(glist))
	{
	    path = (const gchar *)glist->data;
	    if(STRISEMPTY(path))
		continue;

	    if(stat(path, &stat_buf))
	    {
		const gint error_code = (gint)errno;
		g_printerr(
"%s: %s.\n",
		    path, g_strerror(error_code)
		);
		continue;
	    }

	    m = EDVMimeTypeMatch(ctx, path, &stat_buf);
	    if(m == NULL)
	    {
		g_printerr(
"%s: No associated MIME Type.\n",
		    path
		);
		continue;
	    }

	    g_print(
"%s: %s:\n",
		path, m->type
	    );
	    for(glist2 = m->commands_list;
		glist2 != NULL;
		glist2 = g_list_next(glist2)
	    )
	    {
		cmd = EDV_MIME_TYPE_COMMAND(glist2->data);
		if(cmd == NULL)
		    continue;

		g_print(
"\t%s\n",
		    cmd->name
		);
	    }
	}

}

int main(int argc, char *argv[])
{
	const gchar *command_name = NULL;
	gboolean list_commands = FALSE;
	gint i, status;
	const gchar *arg;
	GList *paths_list = NULL;
	edv_context_struct *ctx = EDVContextNew();

	EDVContextLoadConfigurationFile(ctx, NULL);

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

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

	/* 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 Open Version " PROG_VERSION "\n"
PROG_COPYRIGHT
		);
		DO_FREE_LOCALS
		return(0);
	    }
	    /* Edit */
	    else if(!g_strcasecmp(arg, "--edit") ||
		    !g_strcasecmp(arg, "-edit") ||
		    !g_strcasecmp(arg, "--e") ||
		    !g_strcasecmp(arg, "-e")
	    )
	    {
		command_name = "edit";
	    }
	    /* Command Name */
	    else if(!g_strcasecmp(arg, "--command-name") ||
		    !g_strcasecmp(arg, "-command-name") ||
		    !g_strcasecmp(arg, "--c") ||
		    !g_strcasecmp(arg, "-c")
	    )
	    {
		i++;
		arg = (i < argc) ? argv[i] : NULL;
		if(arg != NULL)
		{
		    command_name = arg;
		}
		else
		{
		    g_printerr(
"%s: Requires argument.\n",
			argv[i - 1]
		    );
		    DO_FREE_LOCALS
		    return(2);
		}
	    }
	    /* List Commands */
	    else if(!g_strcasecmp(arg, "--list-commands") ||
		    !g_strcasecmp(arg, "-list-commands") ||
		    !g_strcasecmp(arg, "--l") ||
		    !g_strcasecmp(arg, "-l")
	    )
	    {
		list_commands = TRUE;
	    }
	    /* Path? */
	    else if((*arg != '-') && (*arg != '+'))
	    {
		paths_list = g_list_append(paths_list, STRDUP(arg));
	    }
	    else
	    {
		g_printerr(
"%s: Unsupported argument.\n",
		    arg
		);
		DO_FREE_LOCALS
		return(2);
	    }
	}    

	/* No paths specified? */
	if(paths_list == NULL)
	{
	    print_help(argv[0]);
	    DO_FREE_LOCALS
	    return(2);
	}

	/* List the commands for the object(s)? */
	if(list_commands)
	{
	    ListCommands(ctx, paths_list);
	    status = 0;
	}
	else
	{
	    /* Open the object(s) */
	    status = EDVOpen(ctx, paths_list, command_name);
	    if(status != 0)
	    {
		switch(status)
	        {
		  case -1:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: An error occured while opening the object.\n",
				path
			    );
		    }
		    break;
		  case -2:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: Invalid value or no such object.\n",
				path
			    );
		    }
		    break;
		  case -3:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: Memory allocation error.\n",
				path
			    );
		    }
		    break;
		  case -4:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: User aborted.\n",
				path
			    );
		    }
		    break;
		  case -7:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: No associated MIME Type was found.\n",
				path
			    );
		    }
		    break;
		  default:
		    if(paths_list != NULL)
		    {
			const gchar *path = (const gchar *)paths_list->data;
			if(path != NULL)
			    g_printerr(
"%s: An unknown error occured while opening the object.\n",
				path
			    );
		    }
		    break;
		}
	    }
	    status = -status;
	}

	EDVContextSync(ctx);

	DO_FREE_LOCALS

	return(status);
#undef DO_FREE_LOCALS
}
