/* PureAdmin
 * Copyright (C) 2003 Isak Savo
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/*
 * Misc helperfunctions related to the GUI
 *
 * Copyright (C) 2003 Isak Savo
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <time.h>
#include <string.h>
#include "eggstatusicon.h"

#include "helper.h"
#include "gui_helper.h"
#include "srv_comm.h"
#include "usr_manager.h"
#include "cfg.h"
#include "binreloc.h"
#include "system_accounts.h"

EggStatusIcon *status_icon = NULL;

static gchar *sec_to_time (gulong sec)
{
	gchar *retval;
	gint hr, min;

	hr = sec / 3600;
	sec -= hr * 3600;
	min = sec / 60;
	sec -= min * 60;
     
	retval = g_strdup_printf ("%02d:%02d:%02d", hr, min, (gint) sec);
	return retval;
}

static gchar *get_size_string (guint kbytes)
{
	if (kbytes > 1024 * 1024)
		return g_strdup_printf ("%.2lf %s", (gdouble) kbytes / (1024.0 * 1024.0), _("GiB"));
	if (kbytes > 1024)
		return g_strdup_printf ("%.2lf %s", (gdouble) kbytes / 1024.0, _("MiB"));

	return g_strdup_printf ("%d %s", kbytes, _("KiB"));
}

static gchar *gen_speed_string (gulong speed)
{
	if (speed > 1024 * 1024)
		return g_strdup_printf ("%.1lf %s", (gdouble) speed / (1024.0 * 1024.0), _("GiB/s"));
	if (speed > 1024)
		return g_strdup_printf ("%.1lf %s", (gdouble) speed / 1024.0, _("MiB/s"));
	if (speed > 0)
		return g_strdup_printf ("%lu %s", speed, _("KiB/s"));

	/* Zero bytes per second in transfer speed */
	return g_strdup (_("0 b/s (stalled)"));
}

void gui_terminate (void)
{
	GtkWidget *pane, *main_window;
	/* Save window size and position */
	pane = MW("hpaned1");
	main_window = MW("main_window");
	
	gtk_window_get_position (GTK_WINDOW (main_window), &(cfg.win_pos[0]), &(cfg.win_pos[1]));
	gtk_window_get_size (GTK_WINDOW (main_window),  &(cfg.win_size[0]), &(cfg.win_size[1]));
	cfg.div_pos = gtk_paned_get_position (GTK_PANED (pane));

}

extern gboolean
trayicon_activate (EggStatusIcon *icon, gpointer user_data);

void show_status_icon (const gchar *tooltip)
{
	gboolean is_active;
	
	if (!cfg.use_tray_icon)
		return;

	g_object_get (MW("main_window"),
		      "is-active", &is_active,
		      NULL);
	
	if (is_active) {
		pur_log_dbg ("Won't show notification icon: main window already focused");
		return;
	}
	
	if (!status_icon) {
		gchar *trayicon_fname = g_build_filename (pixmapdir, "pureadmin-16x16.png", NULL);

		if (!g_file_test (trayicon_fname, G_FILE_TEST_EXISTS)) {
			pur_log_err ("Couldn't find trayicon icon: %s", trayicon_fname);
			g_free (trayicon_fname);
			return;
		}
		status_icon = egg_status_icon_new_from_file (trayicon_fname);
		g_signal_connect (G_OBJECT (status_icon), "activate",
				  G_CALLBACK (trayicon_activate), NULL);
		g_free (trayicon_fname);
	}

	egg_status_icon_set_tooltip (EGG_STATUS_ICON (status_icon), tooltip, NULL);
}

void hide_status_icon (void)
{
	if (!cfg.use_tray_icon || !status_icon)
		return;
	g_object_unref (G_OBJECT(status_icon));
	status_icon = NULL;
}

void send_notification (const gchar *summary, const gchar *body)
{
	GError *err = NULL;
	gchar *icon = g_build_filename (pixmapdir, "pureadmin-32x32.png", NULL);
	gchar *cmd = g_strdup_printf ("notify-send \"--icon=%s\" --urgency=low \"%s\" \"%s\"", icon, summary, body);
	if (!g_spawn_command_line_async(cmd, &err))
	{
		pur_log_wrn ("Couldn't send notification: %s", err->message);
		g_error_free (err);
		err = NULL;
	}
	g_free (icon);
	g_free (cmd);
}

/* Statusbar functions, FIXME? Should contex-id be taken into account?? */
void statusbar_push (const gchar *msg)
{
	static GtkWidget *statusbar = NULL;
	gchar *utf8_msg;
  
	if (!statusbar)
		statusbar = MW("main_statusbar");

	utf8_msg = string_to_utf8 (msg);
	gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, utf8_msg);
	g_free (utf8_msg);
		
}

void statusbar_pop (void)
{
	static GtkWidget *statusbar = NULL;
   
	if (!statusbar)
		statusbar = MW("main_statusbar");
   
	gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0);
}

void gui_select_treeview_row (GtkTreeIter *iter, GtkTreeView *tree)
{
	GtkTreePath *path;
	GtkTreeModel *model;
	GtkTreeSelection *sel;

	if (!iter || !tree)
		return;

	model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree));
	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree));
	path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
	
	gtk_tree_selection_select_iter (GTK_TREE_SELECTION (sel), iter);
	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (tree), path, NULL,
				      TRUE,
				      0.4, 0);
	gtk_tree_path_free (path);
	
	return;
}

void gui_update_server_status (void)
{
	GtkWidget *mnu_label, *mnu_item;
	gchar *msg = NULL, *lbl = NULL;

	switch (ftp_runmode)
	{
		case RUNMODE_STANDALONE:
			msg = g_strdup (_("PureFTPd running (standalone)"));
			lbl = g_strdup (_("_Stop Server"));
			break;

		case RUNMODE_INETD:
			msg = g_strdup (_("PureFTPd running (inetd)"));
			lbl = g_strdup (_("_Stop Server"));
			break;
			
		case RUNMODE_STOPPED:
			msg = g_strdup (_("PureFTPd not running"));
			lbl = g_strdup (_("_Start Server"));
			break;
	}
      
	statusbar_pop ();
	statusbar_push (msg);
	g_free (msg);

	mnu_item = MW("mi_server_startstop");
	mnu_label = gtk_bin_get_child (GTK_BIN (mnu_item));
	gtk_label_set_text_with_mnemonic (GTK_LABEL (mnu_label), lbl);
	g_free (lbl);
	
	if (!cfg.cmd_startstop || *cfg.cmd_startstop == '\0' || ftp_runmode == RUNMODE_INETD)
		gtk_widget_set_sensitive (mnu_item, FALSE);
	else
		gtk_widget_set_sensitive (mnu_item, TRUE);
}

/* Creates and displays the popup-menu for the activities */
void gui_display_activity_popup (popup_src_t source)
{
	static GtkWidget *mnu = NULL;
	GtkWidget *item;

	if (!mnu)
		mnu = DW("menu_act_popup");

	/* Not all menu-items should be clickable (depending on where the user popped-up the menu) */
	item = DW("mnu_close_tx");
	if (source == POPUP_FROM_USRTREE)
		gtk_widget_set_sensitive (item, FALSE);
	else if (source == POPUP_FROM_ACTTREE)
		gtk_widget_set_sensitive (item, TRUE);

	gtk_menu_popup (GTK_MENU(mnu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time ());
}

/* Appends a row to the activities list... */
void gui_add_to_activities (PGuiActivityRow line)
{
	GtkTreeIter iter;
	static GtkWidget *tree_activities = NULL;
	static GtkTreeModel *model = NULL;
   
	if (!tree_activities)
	{
		tree_activities = MW("tree_activity");
		model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_activities));
	}
	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
			    COL_ACT_ICON, line.icon,
			    COL_ACT_TEXT, line.text,
			    COL_ACT_ID, line.id,
			    -1);
}

void gui_add_to_online_users (PGuiUserRow line)
{
	GtkTreeIter iter;
	static GtkWidget *tree_users = NULL;
	static GtkTreeModel *model = NULL;
   
	if (!tree_users)
	{
		tree_users = MW("tree_users");
		model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_users));
	}
	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
	gtk_list_store_set (GTK_LIST_STORE (model), &iter,
			    COL_USR_ICON, line.icon,
			    COL_USR_USER, line.user,
			    COL_USR_HOST, line.host,
			    COL_USR_NUM_CONNECTIONS, line.num_connections,
			    -1);
   
}

void gui_clear_activity_list (void)
{
	static GtkWidget *tree_activities = NULL;
	static GtkTreeModel *model = NULL;
   
	if (!tree_activities)
	{
		tree_activities = MW("tree_activity");
		model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_activities));
	}
	gtk_list_store_clear (GTK_LIST_STORE (model));
}

void gui_clear_users_list (void)
{
	static GtkWidget *tree_users = NULL;
	static GtkTreeModel *model = NULL;
   
	if (!tree_users)
	{
		tree_users = MW("tree_users");
		model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_users));
	}
	gtk_list_store_clear (GTK_LIST_STORE (model));
}

void gui_update_info_frame (PActivity *a)
{
	static GtkWidget *w_name = NULL;
	static GtkWidget *w_host = NULL;
	static GtkWidget *w_action = NULL;
	static GtkWidget *w_progress = NULL;
	static GtkWidget *w_elapsed = NULL;
	static GtkWidget *w_eta = NULL;
	static GtkWidget *p_progress = NULL;
	static GtkWidget *hbox_progress = NULL;
	gchar *tmp, *num_cur_str, *num_tot_str, *speed_str, *percent;
#define DATE_STR_LEN 50
	
	tmp = num_cur_str = num_tot_str = speed_str = percent = NULL;

	if (G_UNLIKELY (!w_name))
	{
		w_name = MW("lbl_username");
		w_host = MW("lbl_host");
		w_action = MW("lbl_action");
		w_progress = MW("lbl_progress");
		w_elapsed = MW("lbl_elapsed_time");
		w_eta = MW("lbl_remaining_time");
		p_progress = MW("prog_progress");
		hbox_progress = MW("hbox_advinfo_progress");
	}
	if (!a)
	{
		/* Nothing sent, clear labels and hide progress bar */
		gtk_label_set_text (GTK_LABEL (w_name), "");
		gtk_label_set_text (GTK_LABEL (w_host), "");
		gtk_label_set_text (GTK_LABEL (w_action), "");
		gtk_label_set_text (GTK_LABEL (w_progress), "");
		gtk_label_set_text (GTK_LABEL (w_elapsed), "");
		gtk_label_set_text (GTK_LABEL (w_eta), "");
		gtk_widget_hide (p_progress);
		return;
	}

	gtk_label_set_text (GTK_LABEL (w_name), a->username);
	gtk_label_set_text (GTK_LABEL (w_host), a->remote_addr);

	tmp = g_strdup_printf ("%s '%s'", a->state == FTPWHO_STATE_DOWNLOAD ? _("Downloading") : _("Uploading"), a->filename);
	gtk_label_set_text (GTK_LABEL (w_action), tmp);
	g_free (tmp);
   
   
	num_cur_str = get_size_string (a->download_current_size); // number_separate (a->download_current_size);
	speed_str   = gen_speed_string (a->speed);
	/* If it's an upload, then we don't know the total size. */
	if (a->state == FTPWHO_STATE_DOWNLOAD)
	{
		gdouble fraction = (gdouble) a->download_current_size / (gdouble) a->download_total_size;
		percent = g_strdup_printf ("%.2lf", 100.0 * fraction);
		num_tot_str = get_size_string (a->download_total_size);
		/* Translators: This will be come 'xx MiB of yy MiB (pp%) nn KiB/s
		 * where 'xx' is current size, yy is total size, pp is percentage and nn is the current speed */
		tmp = g_strdup_printf (_("%s of %s (%s%%) %s"), num_cur_str, num_tot_str, percent, speed_str);
		gtk_progress_bar_set_text (GTK_PROGRESS_BAR(p_progress), tmp);
		gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(p_progress), fraction);
		/* Show the progress bar instead of label */
		gtk_widget_hide (w_progress);
		gtk_widget_show (p_progress);
	}
	else
	{

		/* Translators: This will become 'xx MiB at yy KiB/s' where xx is current
		 * uploaded size and yy is the upload speed */
		tmp = g_strdup_printf (_("%s at %s"), num_cur_str, speed_str);
		gtk_label_set_text (GTK_LABEL (w_progress), tmp);
		gtk_widget_hide (p_progress);
		gtk_widget_show (w_progress);
	}
   
	g_free (percent);
	g_free (num_cur_str);
	g_free (num_tot_str);
	g_free (speed_str);
	g_free (tmp);

	tmp = sec_to_time (a->online_since);
	gtk_label_set_text (GTK_LABEL (w_elapsed), tmp);
	if (a->speed == 0)
		tmp = g_strdup ("\342\210\236"); /* Infinity */
	else if (a->download_total_size == 0)
		/* Translators: This refers to the time remaining of an up/download */
		tmp = g_strdup_printf ("<i>%s</i>", _("Unknown"));
	else
		tmp = sec_to_time ((a->download_total_size - a->download_current_size) / a->speed);
	gtk_label_set_markup (GTK_LABEL (w_eta), tmp);
   
	g_free (tmp);
}
GtkWidget *gui_create_system_account_menu (GList *l)
{
	GList *node;
	GtkWidget *menu, *menu_item;
   
	menu = gtk_menu_new ();
	node = g_list_first (l);
	while (node)
	{
		SystemAccount *a = (SystemAccount *) node->data;
		gchar *label = g_strdup_printf ("%s (%d)", a->name, a->id);
		menu_item = gtk_menu_item_new_with_label (label);
		g_free (label);
		g_object_set_data (G_OBJECT(menu_item), "account_id", GINT_TO_POINTER(a->id));
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
		node = g_list_next (node);
	}
	gtk_widget_show_all (menu);
	return menu;
}

GtkWidget *gui_create_menu_from_glist (GList *l)
{
	GList *node;
	GtkWidget *menu, *menu_item;
   
	menu = gtk_menu_new ();
	node = g_list_first (l);
	while (node)
	{
//		SystemAccount *a = (SystemAccount *) node->data;
//		gchar *label = g_strdup_printf ("%s (%d)", a->name, a->id);
		menu_item = gtk_menu_item_new_with_label ((gchar *) node->data);
//		g_free (label);
//		g_object_set_data (G_OBJECT(menu_item), "account_id", GINT_TO_POINTER(a->id));
		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
		node = g_list_next (node);
	}
	gtk_widget_show_all (menu);
	return menu;
}

struct _alert_dlg_info {
	const gchar *primary, *secondary;
	const gchar *icon_type;
	GtkWidget *parent;
};

static GtkWidget *create_msgdialog (const struct _alert_dlg_info *info, gboolean show_chkbox)
{
	GtkWidget *dlg_alert;
	GtkWidget *icon;
	GtkWidget *lbl;
	gchar *text;

	dlg_alert = DW("dlg_msg");
	if (info->parent)
		gtk_window_set_transient_for (GTK_WINDOW (dlg_alert), GTK_WINDOW (info->parent));
	icon = DW("dlg_msg_icon");
	gtk_image_set_from_stock (GTK_IMAGE(icon), info->icon_type, GTK_ICON_SIZE_DIALOG);
	
	
	text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", info->primary, info->secondary);
	lbl = DW("dlg_msg_label");
	gtk_label_set_markup (GTK_LABEL (lbl), text);
	g_free (text);
	
	gtk_widget_show_all (dlg_alert);
	
	if (!show_chkbox)
		gtk_widget_hide (DW("dlg_msg_chk"));

	return dlg_alert;
}

void gui_display_msgdialog (const gchar *primary, const gchar *secondary, const gchar *icon_type, GtkWidget *parent)
{
	GtkWidget *dlg;
	struct _alert_dlg_info dlginfo;

	dlginfo.primary = primary;
	dlginfo.secondary = secondary;
	dlginfo.icon_type = icon_type;
	dlginfo.parent = parent;

	dlg = create_msgdialog (&dlginfo, FALSE);
	
	gtk_dialog_run (GTK_DIALOG (dlg));
	gtk_widget_hide (dlg);
}


void gui_display_msgdialog_checkbox (const gchar *primary, const gchar *secondary,
				     const gchar *icon_type, GtkWidget *parent,
				     gboolean *show_again)
{
	GtkWidget *dlg;
	struct _alert_dlg_info dlginfo;

	g_return_if_fail (show_again != NULL);
	
	dlginfo.primary = primary;
	dlginfo.secondary = secondary;
	dlginfo.icon_type = icon_type;
	dlginfo.parent = parent;

	dlg = create_msgdialog (&dlginfo, TRUE);
	
	gtk_dialog_run (GTK_DIALOG (dlg));
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (DW("dlg_msg_chk"))))
		*show_again = FALSE;
	else
		*show_again = TRUE;
	
	gtk_widget_hide (dlg);
}

static GtkWidget *create_imgbutton (const gchar *text, const gchar *icon_type)
{
	GtkWidget *btn, *img, *lbl;
	GtkWidget *hbox, *alignment;
	
	btn = gtk_button_new ();

	alignment = gtk_alignment_new (0.5, 0.5, 0, 0);
	gtk_container_add (GTK_CONTAINER (btn), alignment);

	hbox = gtk_hbox_new (FALSE, 2);
	gtk_container_add (GTK_CONTAINER (alignment), hbox);
     
	img = gtk_image_new_from_stock (icon_type, GTK_ICON_SIZE_BUTTON);
	gtk_box_pack_start (GTK_BOX (hbox), img, FALSE, FALSE, 0);

	lbl = gtk_label_new_with_mnemonic (text);
	gtk_box_pack_start (GTK_BOX (hbox), lbl, FALSE, FALSE, 0);
	gtk_label_set_justify (GTK_LABEL (lbl), GTK_JUSTIFY_LEFT);

	gtk_widget_show_all (btn);
	
	return btn;
}

/* Creates and runs a HIGified confirmation dialog asking the user to confirm something.
 * Args: 
 *	primary: The 'header' text, keep short and descriptive
 *	secondary: The longer description of the action to confirm
 *	txt_btn_yes: Text to use instead of 'Yes' (if NULL, then stockbutton YES is used)
 *	txt_btn_no: Text to use instead of 'No' (if NULL, then stockbutton NO is used)
 *	parent: The parent window, can be NULL to ignore parent.
 *
 * Returns:
 *	the GTK_RESPONSE_ID as returned by gtk_dialog_run():
 *		GTK_RESPONSE_YES : User pressed 'yes'
 *		GTK_RESPONSE_NO  : User pressed 'no'
 */
gint gui_display_confirmdialog (const gchar *primary, const gchar *secondary,
				const gchar *txt_btn_yes, const gchar *txt_btn_no,
				GtkWidget *parent)
{
	GtkWidget *dlg_alert;
	GtkWidget *lbl;
	GtkWidget *btn_no, *btn_yes;
	gchar *text;
	gint ret;
	
	dlg_alert = DW("dlg_confirm");
	if (parent)
		gtk_window_set_transient_for (GTK_WINDOW (dlg_alert), GTK_WINDOW (parent));
	
	text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s", primary, secondary);
	lbl = DW("dlg_confirm_label");
	gtk_label_set_markup (GTK_LABEL (lbl), text);
	g_free (text);
	
	
	if (txt_btn_no == NULL)
		btn_no = gtk_button_new_from_stock (GTK_STOCK_NO);
	else
		btn_no = create_imgbutton (txt_btn_no, GTK_STOCK_NO);
		
     	gtk_dialog_add_action_widget (GTK_DIALOG (dlg_alert), btn_no, GTK_RESPONSE_NO);
	GTK_WIDGET_SET_FLAGS (btn_no, GTK_CAN_DEFAULT);

	if (txt_btn_yes == NULL)
		btn_yes = gtk_button_new_from_stock (GTK_STOCK_YES);
	else
		btn_yes = create_imgbutton (txt_btn_yes, GTK_STOCK_YES);

	gtk_dialog_add_action_widget (GTK_DIALOG (dlg_alert), btn_yes, GTK_RESPONSE_YES);
	GTK_WIDGET_SET_FLAGS (btn_yes, GTK_CAN_DEFAULT);
		
	gtk_widget_show_all (dlg_alert);

	ret = gtk_dialog_run (GTK_DIALOG (dlg_alert));
	gtk_widget_hide (dlg_alert);
	gtk_widget_destroy (btn_no);
	gtk_widget_destroy (btn_yes);
	return ret;
}

static gchar pixmap_directory[FILEPATH_MAX];

/* Use this function to set the directory containing installed pixmaps. */
void
set_pixmap_directory                   (const gchar *directory)
{
	strncpy (pixmap_directory, directory, FILEPATH_MAX);
}

/* This is an internally used function to create pixmaps. */
GdkPixbuf*
create_pixbuf (const gchar *filename)
{
	gchar *pathname = NULL;
	GdkPixbuf *pixbuf;
	GError *error = NULL;

	if (!filename || !filename[0])
		return NULL;
	
	pathname = g_strdup_printf ("%s%s%s", pixmap_directory,
					   G_DIR_SEPARATOR_S, filename);
	if (!g_file_test (pathname, G_FILE_TEST_EXISTS))
	{
		g_warning ("Couldn't find pixmap file: %s", filename);
		g_free (pathname);
		return NULL;
	}
	
	pixbuf = gdk_pixbuf_new_from_file (pathname, &error);
	if (!pixbuf)
	{
		g_warning ("Failed to load pixbuf file: %s: %s\n",
			   pathname, error->message);
		g_error_free (error);
	}
	g_free (pathname);
	return pixbuf;
}

void show_not_yet_implemented (GtkWindow *parent)
{
	GtkWidget *dlg = gtk_message_dialog_new (parent,
					      0,
					      GTK_MESSAGE_ERROR,
					      GTK_BUTTONS_OK,
					      "The requested feature is not yet implemented");
	gtk_dialog_run (GTK_DIALOG(dlg));
	gtk_widget_destroy (dlg);
}


syntax highlighted by Code2HTML, v. 0.9.1