/*
 * GQmpeg
 * (C)1998, 1999 John Ellis
 *
 * Author: John Ellis
 *
 * This software is released under the GNU General Public License.
 * Please read the included file COPYING for more information.
 * This software comes with no warranty of any kind, use at you own risk!
 */

#include "gqmpeg.h"
#include <gdk/gdkkeysyms.h> /* for key values */

#include "tabcomp.xpm"

/* ----------------------------------------------------------------
   Tab completion routines, can be connected to any gtkentry widget
   using the tab_completion_add_to_entry() function.
   Use remove_trailing_slash() to strip the trailing '/'.
   ----------------------------------------------------------------*/

typedef struct _TabCompData TabCompData;
struct _TabCompData
{
	GtkWidget *entry;
	gchar *dir_path;
	GList *file_list;
	void (*enter_func)(gchar *, gpointer);
	void (*tab_func)(gchar *, gpointer);
	gpointer enter_data;
	gpointer tab_data;
};

static void tab_completion_free_list(TabCompData *td);
static void tab_completion_read_dir(TabCompData *td, gchar *path);
static void tab_completion_destroy(GtkWidget *widget, gpointer data);
static void tab_completion_emit_enter_signal(TabCompData *td);
static void tab_completion_emit_tab_signal(TabCompData *td);
static gint tab_completion_do(TabCompData *td);
static gint tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data);

static void tab_completion_free_list(TabCompData *td)
{
	GList *list;

	g_free(td->dir_path);
	td->dir_path = NULL;

	list = td->file_list;

	while(list)
		{
		g_free(list->data);
		list = list->next;
		}

	g_list_free(td->file_list);
	td->file_list = NULL;
}

static void tab_completion_read_dir(TabCompData *td, gchar *path)
{
        DIR *dp;
        struct dirent *dir;
        GList *list = NULL;

	tab_completion_free_list(td);

        if((dp = opendir(path))==NULL)
                {
                /* dir not found */
                return;
                }
        while ((dir = readdir(dp)) != NULL)
                {
                /* skips removed files */
                if (dir->d_ino > 0)
                        {
			gchar *name = dir->d_name;
			if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
				{
				list = g_list_prepend(list, g_strdup(name));
				}
                        }
		}
        closedir(dp);

	td->dir_path = g_strdup(path);
	td->file_list = list;
}

static void tab_completion_destroy(GtkWidget *widget, gpointer data)
{
	TabCompData *td = data;
	tab_completion_free_list(td);
	g_free(td);
}

static void tab_completion_emit_enter_signal(TabCompData *td)
{
	gchar *text;
	if (!td->enter_func) return;

	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry)));

	if (text[0] == '~')
		{
		gchar *t = text;
		text = g_strconcat(homedir(), t + 1, NULL);
		g_free(t);
		}

	td->enter_func(text, td->enter_data);
	g_free(text);
}

static void tab_completion_emit_tab_signal(TabCompData *td)
{
	gchar *text;
	if (!td->tab_func) return;

	text = g_strdup(gtk_entry_get_text(GTK_ENTRY(td->entry)));

	if (text[0] == '~')
		{
		gchar *t = text;
		text = g_strconcat(homedir(), t + 1, NULL);
		g_free(t);
		}

	td->tab_func(text, td->tab_data);
	g_free(text);
}

static gint tab_completion_do(TabCompData *td)
{
	gchar *entry_text = gtk_entry_get_text(GTK_ENTRY(td->entry));
	gchar *entry_file;
	gchar *entry_dir;
	gchar *ptr;
	gint home_exp = FALSE;

	/* home dir expansion */
	if (entry_text[0] == '~')
		{
		entry_dir = g_strconcat(homedir(), entry_text + 1, NULL);
		home_exp = TRUE;
		}
	else
		{
		entry_dir = g_strdup(entry_text);
		}

	entry_file = filename_from_path(entry_text);

	if (isfile(entry_dir))
		{
		if (home_exp)
			{
			gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir);
			gtk_entry_set_position(GTK_ENTRY(td->entry), strlen(entry_dir));
			}
		g_free(entry_dir);
		return home_exp;
		}
	if (isdir(entry_dir) && strcmp(entry_file, ".") != 0 && strcmp(entry_file, "..") != 0)
		{
		ptr = entry_dir + strlen(entry_dir) - 1;
		if (ptr[0] == '/')
			{
			if (home_exp)
				{
				gtk_entry_set_text(GTK_ENTRY(td->entry), entry_dir);
				gtk_entry_set_position(GTK_ENTRY(td->entry), strlen(entry_dir));
				}
			g_free(entry_dir);
			return home_exp;
			}
		else
			{
			gchar *buf = g_strconcat(entry_dir, "/", NULL);
			gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
			gtk_entry_set_position(GTK_ENTRY(td->entry), strlen(buf));
			g_free(buf);
			g_free(entry_dir);
			return TRUE;
			}
		}

	ptr = filename_from_path(entry_dir);
	if (ptr > entry_dir) ptr--;
	ptr[0] = '\0';

	if (strlen(entry_dir) == 0)
		{
		g_free(entry_dir);
		entry_dir = g_strdup("/");
		}

	if (isdir(entry_dir))
		{
		GList *list;
		GList *poss = NULL;
		gint l = strlen(entry_file);

		if (!td->dir_path || !td->file_list || strcmp(td->dir_path, entry_dir) != 0)
			{
			tab_completion_read_dir(td, entry_dir);
			}

		if (strcmp(entry_dir, "/") == 0) entry_dir[0] = '\0';

		list = td->file_list;
		while(list)
			{
			gchar *file = list->data;
			if (strncmp(entry_file, file, l) == 0)
				{
				poss = g_list_prepend(poss, file);
				}
			list = list->next;
			}

		if (poss)
			{
			if (!poss->next)
				{
				gchar *file = poss->data;
				gchar *buf;

				buf = g_strconcat(entry_dir, "/", file, NULL);

				if (isdir(buf))
					{
					g_free(buf);
					buf = g_strconcat(entry_dir, "/", file, "/", NULL);
					}
				gtk_entry_set_text(GTK_ENTRY(td->entry), buf);
				gtk_entry_set_position(GTK_ENTRY(td->entry), strlen(buf));
				g_free(buf);
				g_list_free(poss);
				return TRUE;
				}
			else
				{
				gint c = strlen(entry_file);
				gint done = FALSE;
				gchar *test_file = poss->data;

				while (!done)
					{
					list = poss;
					if (!list) done = TRUE;
					while(list && !done)
						{
						gchar *file = list->data;
						if (strlen(file) < c || strncmp(test_file, file, c) != 0)
							{
							done = TRUE;
							}
						list = list->next;
						}
					c++;
					}
				c -= 2;
				if (c > 0)
					{
					gchar *file;
					gchar *buf;
					file = g_strdup(test_file);
					file[c] = '\0';
					buf = g_strconcat(entry_dir, "/", file, NULL);
					gtk_entry_set_text(GTK_ENTRY(td->entry),buf);
					gtk_entry_set_position(GTK_ENTRY(td->entry), strlen(buf));
					g_free(file);
					g_free(buf);
					g_list_free(poss);
					return TRUE;
					}
				}
			g_list_free(poss);
			}
		}

	g_free(entry_dir);

	return FALSE;
}

static gint tab_completion_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
{
	TabCompData *td = data;
	gint stop_signal = FALSE;

	switch (event->keyval)
		{
                case GDK_Tab:
			if (tab_completion_do(td))
				{
				tab_completion_emit_tab_signal(td);
				}
			stop_signal = TRUE;
			break;
		case GDK_Return:
			tab_completion_emit_enter_signal(td);
			stop_signal = TRUE;
			break;
		default:
			break;
		}

	if (stop_signal)
		{
		if (stop_signal) gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), "key_press_event");
		return TRUE;
		}

	return FALSE;
}

static void tab_completion_button_pressed(GtkWidget *widget, gpointer data)
{
	TabCompData *td;
	GtkWidget *entry = data;

	td = gtk_object_get_data(GTK_OBJECT(entry), "tab_completion_data");

	if (!td) return;

	if (!GTK_WIDGET_HAS_FOCUS(entry))
		{
		gtk_widget_grab_focus(entry);
		}

	if (tab_completion_do(td))
		{
		tab_completion_emit_tab_signal(td);
		}
}

/*
 *----------------------------------------------------------------------------
 * public interface
 *----------------------------------------------------------------------------
 */

GtkWidget *tab_completion_new(GtkWidget **entry, GtkWidget *window, gchar *text,
			      void (*enter_func)(gchar *, gpointer), gpointer data)
{
	GtkWidget *hbox;
	GtkWidget *button;
	GtkWidget *icon;
	GtkWidget *newentry;
	GdkPixmap *pixmap = NULL;
	GdkBitmap *mask = NULL;
	GtkStyle *style;

	hbox = gtk_hbox_new(FALSE, 0);

	newentry = gtk_entry_new();
	if (text) gtk_entry_set_text(GTK_ENTRY(newentry), text);
	gtk_box_pack_start(GTK_BOX(hbox), newentry, TRUE, TRUE, 0);
	gtk_widget_show(newentry);

	style = gtk_widget_get_style(window);
	pixmap = gdk_pixmap_create_from_xpm_d(window->window, &mask,
		&style->bg[GTK_STATE_NORMAL], (gchar **)tabcomp_xpm);

	button = gtk_button_new();
	GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
	gtk_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) tab_completion_button_pressed, newentry);
	gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
	gtk_widget_show(button);
	
	icon = gtk_pixmap_new(pixmap, mask);
	gtk_container_add(GTK_CONTAINER(button), icon);
	gtk_widget_show(icon);

	tab_completion_add_to_entry(newentry, enter_func, data);

	if (entry) *entry = newentry;
	return hbox;
}

void tab_completion_add_to_entry(GtkWidget *entry, void (*enter_func)(gchar *, gpointer), gpointer data)
{
	TabCompData *td;
	if (!entry)
		{
		printf("Tab completion error: entry != NULL\n");
		return;
		}

	td = g_new0(TabCompData, 1);
	td->entry = entry;
	td->dir_path = NULL;
	td->file_list = NULL;
	td->enter_func = enter_func;
	td->enter_data = data;
	td->tab_func = NULL;
	td->tab_data = NULL;

	gtk_object_set_data(GTK_OBJECT(td->entry), "tab_completion_data", td);

	gtk_signal_connect(GTK_OBJECT(entry), "key_press_event",
			(GtkSignalFunc) tab_completion_key_pressed, td);
	gtk_signal_connect(GTK_OBJECT(entry), "destroy",
			(GtkSignalFunc) tab_completion_destroy, td);
}

void tab_completion_add_tab_func(GtkWidget *entry, void (*tab_func)(gchar *, gpointer), gpointer data)
{
	TabCompData *td = gtk_object_get_data(GTK_OBJECT(entry), "tab_completion_data");

	if (!td) return;

	td->tab_func = tab_func;
	td->tab_data = data;
}

gchar *remove_trailing_slash(gchar *path)
{
	gchar *ret;
	gint l;
	if (!path) return NULL;

	ret = g_strdup(path);
	l = strlen(ret);
	if (l > 1 && ret[l - 1] == '/') ret[l - 1] = '\0';

	return ret;
}

