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

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

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

#include "fsckmanager.h"
#include "fsckcb.h"
#include "config.h"


#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 FSCKManagerSignalCB(int s)
{
	switch(s)
	{
	  case SIGINT:
	  case SIGTERM:
	  case SIGSEGV:
	    exit(1);
	    break;
	}
}

static gint FSCKManagerMainTOCB(gpointer data)
{
	fsck_manager_struct *fm = (fsck_manager_struct *)data;
	if((fm != NULL) ? !fm->map_state : TRUE)
	{
	    gtk_main_quit();
	    return(FALSE);
	}
	else
	{
	    return(TRUE);
	}
}

int main(int argc, char *argv[])
{
	gboolean initialized_gtk = FALSE;
	gint i;
	fsck_manager_struct *fm;
	gboolean auto_start = FALSE;
	const gchar *arg, *startup_device = NULL;
	gchar *devices_ini_file;
	edv_device_struct **device = NULL;
	gint total_devices = 0;
	edv_context_struct *ctx;


	/* Set up time zone */
	tzset();

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


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

	    /* Help */
	    if(!strcasecmp(arg, "--help") ||
	       !strcasecmp(arg, "-help") ||
	       !strcasecmp(arg, "--h") ||
	       !strcasecmp(arg, "-h") ||
	       !strcasecmp(arg, "-?")
	    )
	    {
		printf("%s", PROG_HELP_MESG);
		return(0);
	    }
	    /* Version */
	    else if(!strcasecmp(arg, "--version") ||
		    !strcasecmp(arg, "-version") ||
		    !strcasecmp(arg, "--v") ||
		    !strcasecmp(arg, "-v")
	    )
	    {
		printf("%s %s\n%s", PROG_NAME, PROG_VERSION, PROG_COPYRIGHT);
		return(0);
	    }
	    /* Auto start */
	    else if(!strcasecmp(arg, "--start") ||
		    !strcasecmp(arg, "-start") ||
		    !strcasecmp(arg, "--s") ||
		    !strcasecmp(arg, "-s")
	    )
	    {
		auto_start = TRUE;
	    }
	    /* All else assume startup device */
	    else
	    {
		startup_device = arg;
	    }
	}



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


	/* Initialize GDK RGB buffers system */
	gdk_rgb_init();


	/* Initialize dialogs */
	CDialogInit();

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

	/* Load devices */
	devices_ini_file = STRDUP(EDVGetS(ctx, EDV_CFG_PARM_FILE_DEVICES));
	device = EDVDevicesListLoadFromFile(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 file does not exist.",
		devices_ini_file
	    );
	    EDVPlaySoundWarning(ctx);
	    CDialogSetTransientFor(NULL);
	    CDialogGetResponse(
		"No Devices Found",
		buf,
"If you do have devices then you should run\n\
Endeavour Mark II to configure the device\n\
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 first and only fsck manager window */
	fm = FSCKManagerNew(ctx);
	if(fm != NULL)
	{
	    gtk_window_apply_args(GTK_WINDOW(fm->toplevel), argc, argv);
	    FSCKManagerMap(fm);

	    /* Transfer loaded devices to fsck manager */
	    FSCKManagerSetBusy(fm, TRUE);
	    FSCKManagerSetDevices(fm, device, total_devices);
	    device = NULL;
	    total_devices = 0;
	    FSCKManagerSetBusy(fm, FALSE);

	    /* Select startup device? */
	    if(startup_device != NULL)
	    {
		gboolean device_selected = FALSE;
		gint row, column;
		gchar *cell_text;
		guint8 spacing;
		GdkPixmap *pixmap;
		GdkBitmap *mask;
		GtkCList *clist = (GtkCList *)fm->devices_clist;
		if(clist != NULL)
		{
		    /* Search column 1, the device path column, for
		     * a cell who's value matches startup_device.
		     */
		    column = 1;
		    for(row = 0; row < clist->rows; row++)
		    {
			cell_text = NULL;
			switch((gint)gtk_clist_get_cell_type(clist, row, column))
			{
			  case GTK_CELL_TEXT:
			    gtk_clist_get_text(clist, row, column, &cell_text);
			    break;

			  case GTK_CELL_PIXTEXT:
			    gtk_clist_get_pixtext(
				clist, row, column, &cell_text,
				&spacing, &pixmap, &mask
			    );
			    break;
			}
			/* Got match? */
			if((cell_text != NULL) ?
			    !strcmp(startup_device, cell_text) : FALSE
			)
			{
			    gtk_clist_select_row(clist, row, column);
			    device_selected = TRUE;
			    break;
			}
		    }
		}

		/* Auto start and a device was selected? */
		if(auto_start && device_selected)
		{
		    FSCKManagerStartCB(fm->start_btn, fm);
		}
	    }


	    gtk_timeout_add(
		1000,		/* ms */
		(GtkFunction)FSCKManagerMainTOCB,
		fm
	    );
	    gtk_main();

	    FSCKManagerDelete(fm);
	}

	/* Deallocate list of devices (just in case a fsck manager
	 * was not created.
	 */
	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 */
	CDialogShutdown();

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

	return(0);
}
