/*
** 1998-12-19 -	Let's pretend we're one of those big, bad, desktop environments, and provide
**		users with an "Information" window in which we can show cool stuff... This
**		will basically repeat what stat() has told us, plus a few extras.
** 1999-03-06 -	Changed for the new selection/dirrow handling.
** 1999-03-28 -	Now uses the progress-indicating version of the fut_dir_size() routine.
** 1999-04-05 -	Added use of the new command configuration system, to allow user to have
**		some control over how this command operates. Nice.
*/

#include "gentoo.h"

#include <time.h>

#include "errors.h"
#include "dirpane.h"
#include "iconutil.h"
#include "sizeutil.h"
#include "strutil.h"
#include "fileutil.h"
#include "userinfo.h"
#include "progress.h"
#include "cmdseq_config.h"

#include "cmd_info.h"

#define	CMD_ID	"info"

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

typedef struct {
	gboolean modified;
	gboolean recurse_dirs;			/* Recurse directories? */
	gchar	 df_access[DP_DATEFMT_MAX];	/* Formatter for dates of last access, */
	gchar	 df_modify[DP_DATEFMT_MAX];	/*  modification, */
	gchar	 df_change[DP_DATEFMT_MAX];	/*  and change. */
} OptInfo;

static OptInfo	info_options;
static CmdCfg	*info_cmc = NULL;

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

static GtkWidget * jlabel_new(char *text, GtkJustification just)
{
	GtkWidget	*l;

	if((l = gtk_label_new(text)) != NULL)
		gtk_label_set_justify(GTK_LABEL(l), just);
	return l;
}

static void build_date(char *label, int y, time_t *date, char *fmt, GtkWidget *tab)
{
	gchar		buf[64];
	GtkWidget	*lab;
	struct tm	*tm;

	lab = jlabel_new(label, GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);

	if((tm = localtime(date)) != NULL)
	{
		if(strftime(buf, sizeof buf, fmt, tm) >= 0)
		{
			lab = jlabel_new(buf, GTK_JUSTIFY_RIGHT);
			gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
			gtk_widget_show(lab);
		}
	}
}

static GtkWidget * build_info(MainInfo *min, DirPane *dp, DirRow *row)
{
	gchar		buf[128], sbuf[32], *ptr;
	guint		y;
	const gchar	*iname;
	GtkWidget	*tab, *ico, *lab, *szlab, *sep;
	GdkPixmap	*ipix;
	GdkBitmap	*imsk;

	tab = gtk_table_new(15, 2, FALSE);

	y = 0;
	if(DP_ROW_TYPE(row)->style != NULL)
	{
		if((iname = stl_style_property_get_icon(DP_ROW_TYPE(row)->style, SPN_ICON_UNSEL)) != NULL)
		{
			ipix = ico_icon_get(min, GTK_WIDGET(dp->list)->window, iname, &imsk);
			ico = gtk_pixmap_new(ipix, imsk);
			gtk_table_attach(GTK_TABLE(tab), ico, 0, 1, y, y + 1, 0,0,5,5);
			gtk_widget_show(ico);
		}
	}
	lab = gtk_label_new(DP_ROW_NAME(row));
	gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, GTK_EXPAND,0,5,5);
	gtk_widget_show(lab);
	y++;

	/* For symbolic links, display name of the target, at least. */
	if(S_ISLNK(DP_ROW_LSTAT(row).st_mode))
	{
		GtkWidget	*scwin, *text;

		lab = jlabel_new("Link To", GTK_JUSTIFY_LEFT);
		gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
		gtk_widget_show(lab);		
		scwin = gtk_scrolled_window_new(NULL, NULL);
		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
		text = gtk_text_new(NULL, NULL);
		gtk_text_set_editable(GTK_TEXT(text), FALSE);
		gtk_text_insert(GTK_TEXT(text), NULL, NULL, NULL, DP_ROW_LINKNAME(row), strlen(DP_ROW_LINKNAME(row)));
		gtk_container_add(GTK_CONTAINER(scwin), text);
		gtk_widget_show(text);
		gtk_table_attach(GTK_TABLE(tab), scwin, 1, 2, y, y + 1,  GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,5,0);
		gtk_widget_show(scwin);
		y++;
	}

	sep = gtk_hseparator_new();
	gtk_table_attach(GTK_TABLE(tab), sep, 0, 2, y, y + 1, GTK_EXPAND | GTK_FILL,0,0,5);
	gtk_widget_show(sep);
	y++;

	lab = jlabel_new("Type", GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	lab = gtk_label_new(DP_ROW_TYPE(row)->name);
	gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	y++;

	lab = jlabel_new("Location", GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	lab = gtk_label_new(dp->dir.path);
	gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	y++;

	lab = jlabel_new("Size", GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	sze_put_size(DP_ROW_LSTAT(row).st_size, sbuf, sizeof sbuf, SZE_AUTO);
	if(DP_ROW_LSTAT(row).st_size < 1024)
		str_strncpy(buf, sbuf, sizeof buf);
	else
		g_snprintf(buf, sizeof buf, "%s (%u bytes)", sbuf, (gsize) DP_ROW_LSTAT(row).st_size);
	szlab = gtk_label_new(buf);
	gtk_table_attach(GTK_TABLE(tab), szlab, 1, 2, y, y + 1, 0,0,5,0);
	gtk_widget_show(szlab);
	y++;

	/* Building info for a directory, and recursing enabled? */
	if(S_ISDIR(DP_ROW_LSTAT(row).st_mode) && info_options.recurse_dirs)
	{
		gchar	buf[64], sbuf[64];
		guint64	bytes = 0;
		FUCount	fc;

		/* Collect information recursively. */
		if(fut_dir_size_progress(min, dp_full_name(dp, DP_ROW_INDEX(dp, row)), &bytes, &fc))
		{
			/* Replace contents of previously built size-label. */
			sze_put_size64(bytes, sbuf, sizeof buf, SZE_AUTO);
			g_snprintf(buf, sizeof buf, "%s (%Lu bytes)", sbuf, bytes);
			gtk_label_set_text(GTK_LABEL(szlab), buf);

			/* And tell a little something about the contents. */
			lab = gtk_label_new("Contains");
			gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
			gtk_widget_show(lab);
			g_snprintf(buf, sizeof buf, "%d dir%s, %d file%s, %d symlink%s",
					fc.num_dirs,  (fc.num_dirs == 1)  ? "" : "s",
					fc.num_files, (fc.num_files == 1) ? "" : "s",
					fc.num_links, (fc.num_links == 1) ? "" : "s");
			lab = gtk_label_new(buf);
			gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
			gtk_widget_show(lab);
		}
	}

	sep = gtk_hseparator_new();
	gtk_table_attach(GTK_TABLE(tab), sep, 0, 2, y, y + 1, GTK_EXPAND | GTK_FILL,0,0,5);
	gtk_widget_show(sep);
	y++;

	lab = jlabel_new("Owner", GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	if((ptr = (char *) usr_lookup_uname(DP_ROW_LSTAT(row).st_uid)) != NULL)
		g_snprintf(buf, sizeof buf, "%s (%d)", ptr, (int) DP_ROW_LSTAT(row).st_uid);
	else
		g_snprintf(buf, sizeof buf, "%d", (int) DP_ROW_LSTAT(row).st_uid);
	lab = jlabel_new(buf, GTK_JUSTIFY_RIGHT);
	gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	y++;

	lab = jlabel_new("Group", GTK_JUSTIFY_LEFT);
	gtk_table_attach(GTK_TABLE(tab), lab, 0, 1, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	if((ptr = (char *) usr_lookup_gname(DP_ROW_LSTAT(row).st_gid)) != NULL)
		g_snprintf(buf, sizeof buf, "%s (%d)", ptr, (int) DP_ROW_LSTAT(row).st_gid);
	else
		g_snprintf(buf, sizeof buf, "%d", (int) DP_ROW_LSTAT(row).st_gid);
	lab = jlabel_new(buf, GTK_JUSTIFY_RIGHT);
	gtk_table_attach(GTK_TABLE(tab), lab, 1, 2, y, y + 1, 0,0,5,0);
	gtk_widget_show(lab);
	y++;

	sep = gtk_hseparator_new();
	gtk_table_attach(GTK_TABLE(tab), sep, 0, 2, y, y + 1, GTK_EXPAND | GTK_FILL,0,0,5);
	gtk_widget_show(sep);
	y++;

	build_date("Accessed", y++, &DP_ROW_LSTAT(row).st_atime, info_options.df_access, tab);
	build_date("Modified", y++, &DP_ROW_LSTAT(row).st_ctime, info_options.df_modify, tab);
	build_date("Changed",  y++, &DP_ROW_LSTAT(row).st_mtime, info_options.df_change, tab);

	if(errno)
	{
		gtk_widget_destroy(tab);
		return NULL;
	}

	return tab;
}

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

/* 1998-12-19 -	As the user hits the "Close" button, we just destroy the GtkDialog widget. */
static gint evt_close_clicked(GtkWidget *wid, gpointer data)
{
	gtk_widget_destroy(GTK_WIDGET(data));

	return 1;
}

/* 1999-03-06 -	Create window showing all about <row>, which sits in <dp>. */
static gint info(MainInfo *min, DirPane *dp, DirRow *row)
{
	GtkWidget	*fr;

	if((fr = build_info(min, dp, row)) != NULL)
	{
		GtkWidget	*dlg, *btn;

		dlg = gtk_dialog_new();
		gtk_window_set_title(GTK_WINDOW(dlg), DP_ROW_NAME(row));
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->vbox), fr, TRUE, TRUE, 0);
		gtk_widget_show(fr);
		btn = gtk_button_new_with_label("Close");
		GTK_WIDGET_SET_FLAGS(btn, GTK_CAN_DEFAULT);
		gtk_signal_connect(GTK_OBJECT(btn), "clicked", GTK_SIGNAL_FUNC(evt_close_clicked), dlg);
		gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), btn, TRUE, TRUE, 0);
		gtk_widget_show(btn);
		gtk_widget_grab_default(btn);
		gtk_widget_show(dlg);
	}
	return (fr != NULL) ? TRUE : FALSE;
}

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

/* 1998-12-19 -	Show information about files in <src>. Knows about double-click, and handles
**		it like everybody else.
*/
int cmd_information(MainInfo *min, DirPane *src, DirPane *dst, CmdArg *ca)
{
	GSList	*slist, *iter;

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

	pgs_progress_begin(min, "Info", PFLG_BUSY_MODE);
	for(iter = slist; iter != NULL; iter = g_slist_next(iter))
	{
		if(!info(min, src, DP_SEL_ROW(iter)))
			break;
		dp_unselect(src, DP_SEL_INDEX(src, iter));
	}
	pgs_progress_end(min);

	if(errno)
		err_set(min, errno, CMD_ID, iter ? DP_SEL_NAME(iter) : NULL);
	else
		err_clear(min);
	err_show(min);

	dp_free_selection(slist);

	return 1;
}

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

/* 1999-04-05 -	Initialize configuration stuff. */
void cfg_information(MainInfo *min)
{
	if(info_cmc == NULL)
	{
		/* Initialize default option values. */
		info_options.modified	  = FALSE;
		info_options.recurse_dirs = TRUE;
		str_strncpy(info_options.df_access, "%Y-%m-%d %H:%M.%S", sizeof info_options.df_access);
		str_strncpy(info_options.df_modify, "%Y-%m-%d %H:%M.%S", sizeof info_options.df_modify);
		str_strncpy(info_options.df_change, "%Y-%m-%d %H:%M.%S", sizeof info_options.df_change);

		info_cmc = cmc_config_new("Information", &info_options);
		cmc_field_add_boolean(info_cmc, "modified", NULL, offsetof(OptInfo, modified));
		cmc_field_add_boolean(info_cmc, "recurse_dirs", "Recurse Directories?", offsetof(OptInfo, recurse_dirs));
		cmc_field_add_string(info_cmc, "df_access", "Access Date Format", offsetof(OptInfo, df_access), sizeof info_options.df_access);
		cmc_field_add_string(info_cmc, "df_modify", "Modify Date Format", offsetof(OptInfo, df_modify), sizeof info_options.df_modify);
		cmc_field_add_string(info_cmc, "df_change", "Change Date Format", offsetof(OptInfo, df_change), sizeof info_options.df_change);

		cmc_config_register(info_cmc);
	}
}
