/*
** 1999-05-29 -	A command analogous to Copy, but not quite. Instead of copying the selected source files to
**		the destination directory, this command creates a link at the destination, pointing to the
**		source. Pretty cool. This module also implements the SymLinkEdit command, which can be used
**		to create (!) and edit symbolic links.
*/

#include "gentoo.h"

#include "cmdarg.h"
#include "dialog.h"
#include "dirpane.h"
#include "errors.h"
#include "fileutil.h"
#include "guiutil.h"
#include "overwrite.h"
#include "strutil.h"
#include "cmd_delete.h"
#include "cmd_generic.h"
#include "cmd_symlink.h"

/* ----------------------------------------------------------------------------------------- */

typedef struct {		/* Symbolic link editing info. */
	GtkWidget	*vbox;
	GtkWidget	*name;		/* Entry for the name of the link (not editable unless creating new link). */
	GtkWidget	*contents;	/* Entry for the contents of the link (with pick button for coolness). */
	GtkWidget	*fsel;		/* File selector optionally used to set contents. */

	MainInfo	*min;
	DirPane		*src;
} SleInfo;

/* ----------------------------------------------------------------------------------------- */

/* 1999-05-30 -	Create a link called <name>, pointing at <contents>. If anything already
**		exists under the name <name>, that anything will be silently deleted.
*/
static gboolean set_link(MainInfo *min, const gchar *name, const gchar *contents)
{
	struct stat	stbuf;

	if(lstat(name, &stbuf) == 0)
	{
		gboolean	ok;

		if(S_ISDIR(stbuf.st_mode))
			ok = del_delete_directory(min, name);
		else
			ok = del_delete_file(min, name);
		if(!ok)
		{
			err_show(min);
			return FALSE;
		}
	}
	return (symlink(contents, name) == 0) ? TRUE : FALSE;
}

/* 1999-05-29 -	Create (absolute) symbolic links for all selected entries in <src> at <dst>. */
int cmd_symlink(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	gchar	dest[PATH_MAX];
	GSList	*slist, *iter;
	OvwRes	res;
	guint	num = 0;

	err_clear(min);

	if((src == NULL) || (dst == NULL))
		return 1;

	if((slist = dp_get_selection(src)) == NULL)
		return 1;

	ovw_overwrite_begin(min, "\"%s\" exists - overwrite?", 0UL);
	for(iter = slist; iter != NULL; iter = g_slist_next(iter))
	{
		g_snprintf(dest, sizeof dest, "%s/%s", dst->dir.path, DP_SEL_NAME(iter));
		res = ovw_overwrite_file(min, dest, dp_full_name(src, DP_SEL_INDEX(src, iter)));
		if(res == OVW_SKIP)
			continue;
		else if(res == OVW_CANCEL)
			break;

		if(set_link(min, dest, dp_full_name(src, DP_SEL_INDEX(src, iter))))
		{
			dp_unselect(src, DP_SEL_INDEX(src, iter));
			num++;
		}
		else
		{
			err_set(min, errno, "SymLink", DP_SEL_NAME(iter));
			err_show(min);
			break;
		}
	}
	ovw_overwrite_end(min);
	dp_free_selection(slist);

	if(num)
		dp_rescan_dir(dst);

	return errno == 0;
}

/* ----------------------------------------------------------------------------------------- */

/* 1999-05-30 -	Update generic command dialog for a new link. */
static void link_body(MainInfo *min, DirPane *src, DirRow *row, GtkWindow *win, gpointer user)
{
	SleInfo	*sle = user;

	gtk_window_set_title(win, "Edit Symbolic Link");
	gtk_entry_set_text(GTK_ENTRY(sle->name), DP_ROW_NAME(row));
	gtk_entry_set_text(GTK_ENTRY(sle->contents), DP_ROW_LINKNAME(row));
	gtk_entry_select_region(GTK_ENTRY(sle->contents), 0, -1);
	gtk_widget_grab_focus(sle->contents);
}

/* 1999-05-30 -	Change a link. */
static int link_action(MainInfo *min, DirPane *src, DirPane *dst, DirRow *row, gpointer user)
{
	gchar		buf[PATH_MAX];
	SleInfo		*sle = user;
	const gchar	*name, *contents;

	name = gtk_entry_get_text(GTK_ENTRY(sle->name));
	g_snprintf(buf, sizeof buf, "%s/%s", src->dir.path, name);
	contents = gtk_entry_get_text(GTK_ENTRY(sle->contents));

	if(set_link(min, buf, contents))
	{
		dp_unselect(src, DP_ROW_INDEX(src, row));
		return TRUE;
	}
	return FALSE;
}

/* 1999-05-30 -	Free the link GUI when the generic module is done with it. */
static void link_free(gpointer user)
{
	SleInfo	*sle = user;

	gtk_widget_destroy(sle->vbox);
	sle->vbox = NULL;
}

/* 1999-05-30 -	User clicked "OK" in file selector. Grab selection, and close it. */
static void evt_fsel_ok_clicked(GtkWidget *btn, gpointer user)
{
	SleInfo	*sle = user;

	gtk_entry_set_text(GTK_ENTRY(sle->contents), gtk_file_selection_get_filename(GTK_FILE_SELECTION(sle->fsel)));
	gtk_grab_remove(sle->fsel);
	gtk_widget_destroy(sle->fsel);
}

static void evt_fsel_cancel_clicked(GtkWidget *wid, gpointer user)
{
	SleInfo	*sle = user;

	gtk_grab_remove(sle->fsel);
	gtk_widget_destroy(sle->fsel);
}

/* 1999-05-30 -	User clicked the "details" button for setting link target from file selector. Pop it up. */
static void evt_pick_clicked(GtkWidget *wid, gpointer user)
{
	gchar	buf[PATH_MAX], *text;
	SleInfo	*sle = user;

	sle->fsel = gtk_file_selection_new("Select Link Target");
	if(((text = gtk_entry_get_text(GTK_ENTRY(sle->contents))) != NULL) && (*text != '\0'))
		g_snprintf(buf, sizeof buf, "%s", text);
	else
		g_snprintf(buf, sizeof buf, "%s/", sle->src->dir.path);
	gtk_file_selection_set_filename(GTK_FILE_SELECTION(sle->fsel), buf);
	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(sle->fsel)->ok_button), "clicked", GTK_SIGNAL_FUNC(evt_fsel_ok_clicked), sle);
	gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(sle->fsel)->cancel_button), "clicked", GTK_SIGNAL_FUNC(evt_fsel_cancel_clicked), sle);
	gtk_widget_show(sle->fsel);
	gtk_grab_add(sle->fsel);
}

/* 1999-05-30 -	Edit all selected symbolic links one at a time, or, if there is no selection,
**		create a new link letting the user type both source and destination names.
*/
int cmd_symlinkedit(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	static SleInfo	sle;
	GtkWidget	*label, *table, *pick;

	sle.min = min;
	sle.src = src;

	sle.vbox = gtk_vbox_new(FALSE, 0);
	table = gtk_table_new(2, 3, FALSE);
	label = gtk_label_new("Name");
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1,  0,0,0,0);
	gtk_widget_show(label);
	sle.name = gtk_entry_new_with_max_length(FILENAME_MAX - 1);
	gtk_table_attach(GTK_TABLE(table), sle.name, 1, 3, 0, 1,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(sle.name);
	label = gtk_label_new("Contents");
	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,  0,0,0,0);
	gtk_widget_show(label);
	sle.contents = gtk_entry_new_with_max_length(PATH_MAX - 1);
	gtk_table_attach(GTK_TABLE(table), sle.contents, 1, 2, 1, 2,  GTK_EXPAND|GTK_FILL,0,0,0);
	gtk_widget_show(sle.contents);
	pick = gui_details_button_new(min->gui->window->window);
	gtk_signal_connect(GTK_OBJECT(pick), "clicked", GTK_SIGNAL_FUNC(evt_pick_clicked), &sle);
	gtk_table_attach(GTK_TABLE(table), pick, 2, 3, 1, 2,  0,0,0,0);
	gtk_widget_show(pick);
	gtk_box_pack_start(GTK_BOX(sle.vbox), table, TRUE, TRUE, 0);
	gtk_widget_show(table);
	gtk_widget_show(sle.vbox);

	if(dp_has_selection(src))
	{
		gtk_entry_set_editable(GTK_ENTRY(sle.name), FALSE);
		return cmd_generic(min, CGF_NOALL | CGF_SRC | CGF_LINKSONLY, link_body, link_action, link_free, &sle);
	}
	else
	{
		Dialog	*dlg;
		guint	res;

		dlg = dlg_dialog(sle.vbox, "Create Symbolic Link", "OK|Cancel", NULL, NULL);
		dlg_dialog_set_keep(dlg, TRUE);
		gtk_widget_grab_focus(sle.name);
		if((res = dlg_dialog_wait(dlg)) == 0)
		{
			gchar	buf[PATH_MAX], *name, *contents;
			OvwRes	ores;

			name = gtk_entry_get_text(GTK_ENTRY(sle.name));
			if(strchr(name, G_DIR_SEPARATOR) != NULL)
				str_strncpy(buf, name, sizeof buf);
			else
				g_snprintf(buf, sizeof buf, "%s/%s", src->dir.path, name);
			contents = gtk_entry_get_text(GTK_ENTRY(sle.contents));
			ovw_overwrite_begin(min, "\"%s\" exists - overwrite?", 0UL);
			ores = ovw_overwrite_file(min, buf, NULL);
			if(ores == OVW_PROCEED)
			{
				if(set_link(min, buf, contents))
					dp_rescan_dir(src);
			}
			ovw_overwrite_end(min);
		}
		dlg_dialog_destroy(dlg);
	}
	return 1;
}
