#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <gtk/gtk.h>

#include "../guiutils.h"
#include "../conmsg.h"
#include "../cdialog.h"
#include "../pdialog.h"

#include "../lib/endeavour2.h"
#include "../lib/edv_devices_list.h"

#include "ziptool.h"
#include "ziptoolcb.h"
#include "config.h"


static void ZipToolSignalCB(int s);
static gint ZipToolTOCB(gpointer data);


#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)


/*
 *	UNIX signal callback.
 */
static void ZipToolSignalCB(int s)
{
	switch(s)
	{
	  case SIGINT:
	  case SIGTERM:
	  case SIGSEGV:
	    exit(1);
	    break;
	}
}

/*
 *	Main timeout callback.
 */
static gint ZipToolTOCB(gpointer data)
{
	zip_tool_struct *zt = ZIP_TOOL(data);
	if(!ZipToolIsMapped(zt))
	{
	    gtk_main_quit();
	    return(FALSE);
	}

	return(TRUE);
}


int main(int argc, char *argv[])
{
	gboolean initialized_gtk = FALSE;
	gint i;
	zip_tool_struct *zt;
	const gchar *arg, *startup_device = "/dev/sda4";
	gchar *devices_ini_file;
	edv_device_struct **device = NULL;
	gint total_devices = 0;
	edv_context_struct *ctx;

#define DO_FREE_LOCALS	{		\
}

	/* Set up signal callbacks */
	signal(SIGINT, ZipToolSignalCB);
	signal(SIGTERM, ZipToolSignalCB);
	signal(SIGKILL, ZipToolSignalCB);
	signal(SIGSEGV, ZipToolSignalCB);
	signal(SIGSTOP, ZipToolSignalCB);
	signal(SIGCONT, ZipToolSignalCB);
	signal(SIGPIPE, ZipToolSignalCB);


	/* Parse 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") ||
	       !g_strcasecmp(arg, "-?")
	    )
	    {
		g_print("%s", PROG_HELP_MESG);
		DO_FREE_LOCALS
		return(0);
	    }
	    /* Version */
	    else if(!g_strcasecmp(arg, "--version") ||
		    !g_strcasecmp(arg, "-version")
	    )
	    {
		g_print("%s %s\n%s", PROG_NAME, PROG_VERSION, PROG_COPYRIGHT);
		DO_FREE_LOCALS
		return(0);
	    }
	    /* Skip these arguments so that gtk_window_apply_args()
	     * handles them
	     */
	    else if(gtk_is_window_arg(arg))
	    {
		i++;
	    }
	    /* Single character argument? */
	    else if((*arg == '-') ? (arg[1] != '-') : FALSE)
	    {
		const gchar *v = arg + 1;
/*		gchar c; */

		while(*v != '\0')
		{
#if 0
		    c = *v;
		    if(c == 's')
		    {
			auto_start = TRUE;
		    }
		    else
		    {
			g_printerr(
"-%c: Unsupported argument.\n",
			    c  
			);
			DO_FREE_LOCALS
			return(2);
		    }
#endif
		    v++;
		}
	    }
	    /* Non-option argument? */
	    else if((*arg != '-') && (*arg != '+'))
	    {
		/* Startup device */
		startup_device = arg;
	    }
	    else
	    {
		g_printerr(
"%s: Unsupported argument.\n",
		    arg
		);
		DO_FREE_LOCALS
		return(2);
	    }
	}


	/* Initialize GTK as needed */
	if(!initialized_gtk)
	{
	    if(!gtk_init_check(&argc, &argv))
	    {
		g_printerr("Unable to initialize GTK.\n");
		DO_FREE_LOCALS
		return(1);
	    }
	    initialized_gtk = TRUE;

	    /* Initialize the GDK RGB buffers */
	    gdk_rgb_init();
	}

	/* Initialize dialogs */
	ConMsgInit(PROG_NAME, 0, 0, TRUE, TRUE);
	CDialogInit();
	PDialogInit();

	/* Initialize Endeavour context */
	ctx = EDVContextNew();
	EDVContextLoadConfigurationFile(ctx, NULL);

	/* Load devices */
	devices_ini_file = STRDUP(EDVGetS(ctx, EDV_CFG_PARM_FILE_DEVICES));
	device = EDVDevicesListFileOpen(devices_ini_file, &total_devices);

	/* Update device mount states and stats */
	EDVDevicesListUpdateMountStates(device, total_devices);
	EDVDevicesListUpdateStats(device, total_devices);

	/* Check if no devices have been loaded, suggesting that
	 * either no device references exist in the devices.ini or
	 * that the file does not exist
	 */
	if(total_devices == 0)
	{
	    gchar *buf = g_strdup_printf(
"No devices were found in the file:\n\
\n\
    %s\n\
\n\
It is possible that no device references were defined\n\
or that the device references file does not exist.",
		devices_ini_file
	    );
	    EDVPlaySoundWarning(ctx);
	    CDialogSetTransientFor(NULL);
	    CDialogGetResponse(
		"No Devices Found",
		buf,
"You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
		CDIALOG_ICON_WARNING,
		CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
		CDIALOG_BTNFLAG_OK
	    );
	    CDialogSetTransientFor(NULL);
	    g_free(buf);
	}


	/* Create Zip Tool Window */
	zt = ZipToolNew(ctx);
	if(zt != NULL)
	{
	    gtk_window_apply_args(GTK_WINDOW(zt->toplevel), argc, argv);
	    ZipToolMap(zt);

	    ZipToolSetBusy(zt, TRUE);

	    /* Find startup device from devices list */
	    if(startup_device != NULL)
	    {
		edv_device_struct *dev_ptr;

		for(i = 0; i < total_devices; i++)
		{
		    dev_ptr = device[i];
		    if(dev_ptr == NULL)
			continue;

		    /* This device's path matches the startup device? */
		    if((dev_ptr->device_path != NULL) ?
		       !strcmp(dev_ptr->device_path, startup_device) : FALSE
		    )
		    {
			/* Transfer this device from the list to the
			 * zip tools window and mark it NULL on our list
			 * since it should not be referenced again
			 */
			ZipToolSetDevice(zt, dev_ptr);
			ZipToolRefreshDevice(zt, dev_ptr);
			ZipToolUpdate(zt);
			device[i] = dev_ptr = NULL;
			break;
		    }
		}
		/* No such device? */
		if((i >= total_devices) && (total_devices > 0))
		{
		    gchar *buf = g_strdup_printf(
"Could not find device:\n\
\n\
    %s\n\
\n\
It is possible that no device references were defined\n\
or that the device references file does not exist.",
			startup_device
		    );
		    EDVPlaySoundWarning(ctx);
		    CDialogSetTransientFor(zt->toplevel);
		    CDialogGetResponse(
			"Device Not Found",
			buf,
"You should run Endeavour Mark II and configure the\n\
device references by going to Device->Devices...\n\
and then exit Endeavour Mark II to ensure that\n\
the changes have been saved. Afterwards run this\n\
program again.",
			CDIALOG_ICON_WARNING,
			CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			CDIALOG_BTNFLAG_OK
		    );
		    CDialogSetTransientFor(NULL);
		    g_free(buf);

		}
	    }

	    ZipToolSetBusy(zt, FALSE);


	    gtk_timeout_add(
		1000,		/* ms */
		(GtkFunction)ZipToolTOCB,
		zt
	    );
	    gtk_main();

	    ZipToolDelete(zt);
	}

	/* Delete devices list */
	for(i = 0; i < total_devices; i++)
	    EDVDeviceDelete(device[i]);
	g_free(device);
	device = NULL;
	total_devices = 0;

	g_free(devices_ini_file);
	devices_ini_file = NULL;

	EDVContextSync(ctx);
	EDVContextDelete(ctx);
	ctx = NULL;

	/* Shutdown dialogs */
	PDialogShutdown();
	CDialogShutdown();
	ConMsgShutdown();

	/* Reset the DND Icon */
	GUIDNDSetDragIcon(NULL, NULL, 0, 0);

	DO_FREE_LOCALS

	return(0);
#undef DO_FREE_LOCALS
}
