/* $Id: e2_password_dialog.c 706 2007-11-06 09:13:06Z tpgww $

Copyright (C) 2007 tooar <tooar@gmx.net>

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

/**
@file src/dialogs/e2_password_dialog.c
@brief functions to setup and cleanup password-related widgets for a dialog
*/

#include "e2_password_dialog.h"
//#include <pthread.h>
#define E2_HINT_MSEC 600

//static assuming last-closed window sets size for next one in this session only
//static gint window_width = -1;
#ifdef USE_GTK2_10
static guint pwrefcount = 0;
static guint hinttime;
static gboolean hinted = FALSE;		//TRUE when the last char in either used entry is displayed as plaintext
#endif
static gboolean plaintext = FALSE;	//TRUE when all entered chars are echoed as is
static gboolean hidden = FALSE;		//TRUE when no entered char is to be echoed

/**
@brief toggle temporary display of last-entered character in the password
This is a key-press callback for both password-entry entry widgets
Toggles parameters in response to <Ctrl>p, <Ctrl>h, and for gtk >= 2.10, <Ctrl>t
@param entry pointer to the widget that received the keypress
@param event event data struct
@param rt pointer to data struct for the dialog

@return TRUE if the key was one of the recognised ones
*/
static gboolean _e2_pwdlg_key_press_cb (GtkWidget *entry, GdkEventKey *event,
	E2_PWDataRuntime *rt)
{
	if ((event->state & GDK_CONTROL_MASK) != 0)
	{
		if (event->keyval == GDK_p || event->keyval == GDK_P)
		{
			rt->plain = !rt->plain;
			gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry1), rt->plain);
			gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry2), rt->plain);
			return TRUE;
		}
		else if (event->keyval == GDK_h || event->keyval == GDK_H)
		{
			if (rt->plain)
			{
				rt->plain = FALSE;
				gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry1), FALSE);
				gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry2), FALSE);
			}
			rt->hide = !rt->hide;
			gunichar ch = (rt->hide) ? (gunichar) 0 : (gunichar) '*';
			gtk_entry_set_invisible_char (GTK_ENTRY (rt->pwentry1), ch);
			gtk_entry_set_invisible_char (GTK_ENTRY (rt->pwentry2), ch);
			return TRUE;
		}
#ifdef USE_GTK2_10
		else if (event->keyval == GDK_t || event->keyval == GDK_T)
		{
			if (rt->plain)
			{
				rt->plain = FALSE;
				gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry1), FALSE);
				gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry2), FALSE);
			}
			rt->hint = !rt->hint;
			guint msec = (rt->hint) ? E2_HINT_MSEC : 0;
			GtkSettings *s = gtk_settings_get_default ();
			g_object_set (G_OBJECT (s), "gtk-entry-password-hint-timeout", msec, NULL);
			return TRUE;
		}
#endif
	}
	return FALSE;
}
/**
@brief setup password-related widgets to get user input with obfuscated text display
One or two sets of centred labels and entries consecutively added to the end of @a box
or placed side-by-side in a table added to @a box
@a pw may hold a pointer to a suggested value.
@param box the widget into which things will be packed
@param confirm TRUE to get 2 matched copies of entered password
@param mainprompt custom prompt string, or NULL for default
@param pw store for password pointer

@return pointer to data struct, NULL upon error
*/
E2_PWDataRuntime *e2_password_dialog_setup (GtkWidget *box, gboolean confirm, //gboolean tabular,
	gchar *mainprompt, gchar **pw)
{
	E2_PWDataRuntime *rt = ALLOCATE0 (E2_PWDataRuntime);
	CHECKALLOCATEDWARN (rt, return NULL;)

	gchar *realprompt = (mainprompt != NULL) ? mainprompt : _("Enter password:");
	gchar *realpw = (*pw != NULL && **pw != '\0') ? *pw : NULL;

/*	if (tabular)
	{
		rt->table =
		e2_widget_add_table (box, (confirm)? 1:2, 2, FALSE, TRUE, E2_PADDING_SMALL);
#ifdef E2_ASSISTED
		GtkWidget *label =
#endif
		e2_widget_add_mid_label_to_table (rt->table, realprompt, 0.0, 0, 1, 0, 1);
		rt->pwentry1 = e2_widget_add_entry_to_table (rt->table, realpw, 1, 2, 0, 1);
#ifdef E2_ASSISTED
		e2_widget_set_label_relations (GTK_LABEL (label), rt->pwentry1);
#endif
	}
	else
	{
		rt->table = NULL;
*/
#ifdef E2_ASSISTED
		GtkWidget *label =
#endif
		e2_widget_add_label (box, realprompt, 0.5, 0.0, FALSE, E2_PADDING);
		rt->pwentry1 = e2_widget_add_entry (box, realpw, TRUE, (realpw != NULL));
#ifdef E2_ASSISTED
		e2_widget_set_label_relations (GTK_LABEL (label), rt->pwentry1);
#endif
//	}
	rt->plain = plaintext;
#ifdef USE_GTK2_10
	rt->hint = hinted;
#endif
	rt->hide = hidden;
	if (!rt->plain)
	{
		gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry1), FALSE);
		if (rt->hide)
			gtk_entry_set_invisible_char (GTK_ENTRY (rt->pwentry1), 0);
#ifdef USE_GTK2_10
		//setup to handle password hinting
		GtkSettings *defs = gtk_settings_get_default ();
		//log original value
		guint msec;
		g_object_get (G_OBJECT (defs), "gtk-entry-password-hint-timeout", &msec, NULL);
		if (++pwrefcount == 1)
			hinttime = msec;
		msec =  (rt->hint) ? E2_HINT_MSEC : 0;
		g_object_set (G_OBJECT (defs), "gtk-entry-password-hint-timeout", msec, NULL);
#endif
	}
	//handle Return-key presses when the entry is focused
//	g_signal_connect (G_OBJECT (rt->pwentry1), "activate",
//		G_CALLBACK (_e2_pwdlg_activated_cb), rt);
	//handle hint-key or hide-key presses
	g_signal_connect (G_OBJECT (rt->pwentry1), "key-press-event",
		G_CALLBACK (_e2_pwdlg_key_press_cb), rt);

	rt->confirm = confirm;
	if (confirm)
	{
/*		if (tabular)
		{
#ifdef E2_ASSISTED
			label =
#endif
			e2_widget_add_mid_label_to_table (rt->table, _("Confirm password:"), 0.0, 0, 1, 1, 2);
			rt->pwentry2 = e2_widget_add_entry_to_table (rt->table, NULL, 1, 2, 1, 2);
#ifdef E2_ASSISTED
			e2_widget_set_label_relations (GTK_LABEL (label), rt->pwentry2);
#endif
		}
		else
		{
*/
#ifdef E2_ASSISTED
			label =
#endif
			e2_widget_add_label (box, _("Confirm password:"), 0.5, 0.0, FALSE, E2_PADDING);
			rt->pwentry2 = e2_widget_add_entry (box, NULL, TRUE, FALSE);
#ifdef E2_ASSISTED
			e2_widget_set_label_relations (GTK_LABEL (label), rt->pwentry2);
#endif
//		}
		if (!rt->plain)
		{
			gtk_entry_set_visibility (GTK_ENTRY (rt->pwentry2), FALSE);
			if (rt->hide)
				gtk_entry_set_invisible_char (GTK_ENTRY (rt->pwentry2), 0);
		}
		//handle Return-key presses when the entry is focused
//		g_signal_connect (G_OBJECT (rt->pwentry2), "activate",
//			G_CALLBACK (_e2_pwdlg_activated_cb), rt);
		//handle hint-key or hide-key presses
		g_signal_connect (G_OBJECT (rt->pwentry2), "key-press-event",
			G_CALLBACK (_e2_pwdlg_key_press_cb), rt);
	}
	rt->passwd = pw;

	return rt;
}
/**
@brief check whether entered password(s) are ok to use, and log it if so
There is no "editorial" about the merit of the password(s) - anything non-empty
will be accepted
@param rt pointer to password(s) data

@return TRUE if the password(s) are ok
*/
gboolean e2_password_dialog_confirm (E2_PWDataRuntime *rt)
{
	const gchar *pw1;
	pw1 = gtk_entry_get_text (GTK_ENTRY (rt->pwentry1));
	if (*pw1 == '\0' && !rt->confirm)
	{
		gtk_widget_grab_focus (rt->pwentry1);
		return FALSE;
	}
	else	//initial pw not empty or is empty but confirm needed
		if (rt->confirm)
	{
		const gchar *pw2;
		pw2 = gtk_entry_get_text (GTK_ENTRY (rt->pwentry2));

		if (*pw1 != '\0' && *pw2 == '\0')
		{
			gtk_widget_grab_focus (rt->pwentry2);
			return FALSE;
		}
		else if (*pw2 != '\0' && *pw1 == '\0')
		{
			gtk_widget_grab_focus (rt->pwentry1);
			return FALSE;
		}
		else if (!g_str_equal (pw1, pw2))
		{
			e2_dialog_warning (_("Entered passwords are different"));
			gtk_widget_grab_focus (rt->pwentry1);
			return FALSE;
		}
		else if (*pw1 == '\0') //pw1 and pw2 both empty
		{
			gtk_widget_grab_focus (rt->pwentry1);
			return FALSE;
		}
	}

	if (*rt->passwd != NULL)
		g_free (*rt->passwd);
	*rt->passwd = g_strdup (pw1);

	return TRUE;
}
/**
@brief backup session-static settings
@param rt pointer to password data struct
@return
*/
void e2_password_dialog_backup (E2_PWDataRuntime *rt)
{
	plaintext = rt->plain;
	hidden = rt->hide;
#ifdef USE_GTK2_10
	if (--pwrefcount == 0)
	{
		GtkSettings *defs = gtk_settings_get_default ();
		g_object_set (G_OBJECT (defs), "gtk-entry-password-hint-timeout",
			hinttime, NULL);
	}
	hinted = rt->hint;
#endif
}
