#include <stdlib.h>
#include <string.h>

#include <Imlib.h>

#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gdk/gdkrgb.h>

#include "../../include/string.h"
#include "../../include/fio.h"
#include "../../include/disk.h"

#include "../guiutils.h"
#include "../guirgbimg.h"
#include "../tlist.h"

#include "../config.h"


gpointer imlib_handle;


static void tlist_destroy_cb(gpointer data);
static guint8 *load_image_rgba(
	const gchar *path, gint *width, gint *height, gint *bpl
);
static void tlist_get_dir(
	tlist_struct *tlist, const gchar *path
);

static gint delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data);
static gint button_event_cb(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);
static void select_cb(
	gpointer tl, GdkEventButton *button, gint thumb_num,
	gpointer data
);
static void unselect_cb(
	gpointer tl, GdkEventButton *button, gint thumb_num,
	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)


/*
 *	Thumbs List thumb destroy signal callback.
 */
static void tlist_destroy_cb(gpointer data)
{
g_print("Deleting 0x%.8x\n", (guint)data);
	g_free(data);
}


/*
 *	Loads image file and returns the RGBA data.
 */
static guint8 *load_image_rgba(
	const gchar *path, gint *width, gint *height, gint *bpl
)
{
	gint i, swidth, sheight, sbpp, sbpl, tbpp, tbpl;
	const gchar *ext_ptr;
	const guint8 *src_rgb, *src_alpha;
	guint8 *tar_rgba;
	gchar *dpath;
	const gchar *ext_list[] = IMLIB_IMAGE_FILE_EXTENSIONS;
	ImlibImage *imlib_image;


	if(path == NULL)
	    return(NULL);

	ext_ptr = strrchr(path, '.');
	if(ext_ptr == NULL)
	    return(NULL);

	/* Check extension, iterate through ext_list to find
	 * which extension matches the given path
	 */
	for(i = 0; ext_list[i] != NULL; i += 2)
	{
	    if(strstr(ext_list[i], ext_ptr) != NULL)
		break;
	}
	if(ext_list[i] == NULL)
	    return(NULL);

	/* Copy path and load Imlib image */
	dpath = g_strdup(path);
	imlib_image = Imlib_load_image(imlib_handle, dpath);
	g_free(dpath); dpath = NULL;

	if(imlib_image == NULL)
	    return(NULL);

	/* Need to realize changes */
	Imlib_changed_image(imlib_handle, imlib_image);

	src_rgb = (const guint8 *)imlib_image->rgb_data;
	src_alpha = (const guint8 *)imlib_image->alpha_data;

	swidth = imlib_image->rgb_width;
	sheight = imlib_image->rgb_height;
	sbpp = 3;
	sbpl = swidth * sbpp;

	tbpp = 4;
	tbpl = swidth * tbpp;

	if((src_rgb == NULL) || (swidth <= 0) || (sheight <= 0))
	{
	    Imlib_destroy_image(imlib_handle, imlib_image);
	    return(NULL);
	}

	/* Allocate return image data buffer */
	tar_rgba = (guint8 *)g_malloc(tbpl * sheight);
	if(tar_rgba != NULL)
	{
	    gint x, y;
	    guint8 *tar_ptr;
	    const guint8 *src_ptr;

	    /* Iterate through each line */
	    for(y = 0; y < sheight; y++)
	    {
		/* Iterate through current line */
		for(x = 0; x < swidth; x++)
		{
		    tar_ptr = &tar_rgba[
			(y * tbpl) + (x * tbpp)
		    ];
		    src_ptr = &src_rgb[
			(y * sbpl) + (x * sbpp)
		    ];

		    /* Copy rgba of this pixel (3 bytes plus one alpha value) */
		    *tar_ptr++ = *src_ptr++;
		    *tar_ptr++ = *src_ptr++;
		    *tar_ptr++ = *src_ptr;
		    *tar_ptr = 0xff;
		}
	    }
	}

	/* Destroy Imlib image, it is no longer needed */
	Imlib_destroy_image(imlib_handle, imlib_image);
	imlib_image = NULL;
	src_rgb = NULL;
	src_alpha = NULL;

	if(width != NULL)
	    *width = swidth;
	if(height != NULL)
	    *height = sheight;
	if(bpl != NULL)
	    *bpl = tbpl;

	return(tar_rgba);
}


/*
 *	Clears the thumbs list and gets all directory contents.
 */
static void tlist_get_dir(
	tlist_struct *tlist, const gchar *path
)
{
	gint strc;
	gchar **strv;
	gchar *lpath;

	if((tlist == NULL) || (path == NULL))
	    return;

	lpath = g_strdup(path);

	TListFreeze(tlist);

	TListClear(tlist);	/* This might delete path */

g_print("Loading images from: %s\n", lpath);

	strv = GetDirEntNames2(lpath, &strc);
	if(strv != NULL)
	{
	    gint i;
	    const gchar *name;

	    strv = StringQSort(strv, strc);

	    for(i = 0; i < strc; i++)
	    {
		name = strv[i];
		if(name == NULL)
		    continue;

		if(g_strcasecmp(name, "."))
		{
		    gint thumb_num;
		    const gchar *full_path = PrefixPaths(lpath, name);

		    thumb_num = TListAppend(tlist, name);
		    if(thumb_num > -1)
		    {
			gint width, height, bpl;
			guint8 *data_rgba;

			TListSetDataFull(
			    tlist, thumb_num,
			    g_strdup(full_path), tlist_destroy_cb
			);

			data_rgba = load_image_rgba(
			    full_path, &width, &height, &bpl
			);
			if(data_rgba != NULL)
			{
g_print("Loaded image: %s\n", full_path);
			    TListSetRGBA(
				tlist, thumb_num,
				width, height, bpl,
				GDK_RGB_DITHER_NORMAL,
				data_rgba,
				TRUE	/* Do not enlarge if small */
			    );
			}

			g_free(data_rgba);
		    }
		}

		g_free(strv[i]);
		strv[i] = NULL;
	    }

	    g_free(strv);
	}

	TListThaw(tlist);

	g_free(lpath);
}


/*
 *	GtkWindow "delete_event" signal callback.
 */
static gint delete_cb(GtkWidget *widget, GdkEvent *event, gpointer data)
{
	gtk_main_quit();
	return(TRUE);
}

/*
 *	Thumbs List "button_press_event" or "button_release_event"
 *	signal callback.
 */
static gint button_event_cb(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	tlist_struct *tlist = (tlist_struct *)data;
	if((button == NULL) || (tlist == NULL))
	    return(status);

	etype = button->type;

	switch(etype)
	{
	  case GDK_BUTTON_PRESS:
	    if(button->button == 1)
	    {
		gint thumb_num = -1;

		if(TListGetSelection(
		    tlist, button->x, button->y, &thumb_num, NULL, NULL
		))
		{
		    if(TListIsThumbVisible(tlist, thumb_num) !=
			GTK_VISIBILITY_FULL
		    )
			TListMoveTo(tlist, thumb_num, 0.5);
		}
	    }
	    else if(button->button == 3)
	    {
		gint thumb_num = -1;


		TListGetSelection(
		    tlist, button->x, button->y, &thumb_num, NULL, NULL
		);
		TListFreeze(tlist);
		TListRemove(tlist, thumb_num);
		TListThaw(tlist);
	    }
	    break;

	  case GDK_2BUTTON_PRESS:
	    if(button->button == 1)
	    {
		gint thumb_num = -1;
		const gchar *path;


		TListGetSelection(
		    tlist, button->x, button->y, &thumb_num, NULL, NULL
		);
		path = (const gchar *)TListGetThumbData(tlist, thumb_num);
		if(path != NULL)
		{
		    tlist_get_dir(tlist, path);
		}
	    }
	    break;


	}

	return(status);
}

/*
 *	Thumbs List select thumb signal callback.
 */
static void select_cb(
	gpointer tl, GdkEventButton *button, gint thumb_num,
	gpointer data
)
{
	tlist_struct *tlist = (tlist_struct *)tl;


	g_print(
	    "Selected thumb %i (vis: %i)\n",
	    thumb_num,
	    TListIsThumbVisible(tlist, thumb_num)
	);

}

/*
 *      Unselect thumb callback.
 */
static void unselect_cb(
	gpointer tl, GdkEventButton *button, gint thumb_num,
	gpointer data
)
{
/*	tlist_struct *tlist = TLIST(tl); */

	g_print("Unselected thumb %i\n", thumb_num);
}


int main(int argc, char *argv[])
{
	gboolean initialized_gtk = FALSE;
	gint i;
	const gchar *arg;
	GtkWidget *toplevel, *w, *parent;
	tlist_struct *tlist;


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

	    if(!g_strcasecmp(arg, "--help") ||
	       !g_strcasecmp(arg, "-help") ||
	       !g_strcasecmp(arg, "--h") ||
	       !g_strcasecmp(arg, "-h") ||
	       !g_strcasecmp(arg, "-?")
	    )
	    {
		g_print(
"Usage: %s [directory]\n",
		    argv[0]
		);
		return(0);
	    }
	}

	/* 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;

	    gdk_rgb_init();
	}

	imlib_handle = Imlib_init(GDK_DISPLAY());


	/* Begin creating thumbs list */

	/* Toplevel */
	toplevel = w = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_usize(
	    w, 320, 240
	);
	gtk_window_set_policy(
	    GTK_WINDOW(w),
	    TRUE, TRUE, TRUE
	);
	gtk_widget_realize(w);
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(delete_cb), NULL
	);
	parent = w;

	w = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(parent), w);
	gtk_widget_show(w);
	parent = w;

	/* Thumbs list */
	tlist = TListNew(
	    FALSE, 100, 90, 2,
	    NULL,	/* Client data */
	    select_cb, unselect_cb
	);
	tlist->selection_mode = GTK_SELECTION_EXTENDED;
	w = tlist->toplevel;
	gtk_box_pack_start(GTK_BOX(parent), w, TRUE, TRUE, 0);

	w = tlist->list_da;
	gtk_signal_connect_after(
	    GTK_OBJECT(w), "button_press_event",
	    GTK_SIGNAL_FUNC(button_event_cb), tlist
	);

	TListMap(tlist);



	/* Show toplevel window */
	gtk_widget_show(toplevel);


	/* Get directory listing */
	if(argc > 1)
	{
	    tlist_get_dir(tlist, argv[1]);
	}
	else
	{
	    gchar cwd[PATH_MAX];
	    tlist_get_dir(tlist, getcwd(cwd, sizeof(cwd)));
	}

	/* Enter GTK+ main loop */
	gtk_main();



	TListDelete(tlist);
	tlist = NULL;

	gtk_widget_destroy(toplevel);



	return(0);
}
