#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>
#include "guiutils.h"
#include "fsd.h"

#include "images/icon_fonts_20x20.xpm"


typedef struct _fsd_data_struct		fsd_data_struct;
typedef struct _fsd_prompt_struct	fsd_prompt_struct;

/*
 *	Font Selection Dialog:
 */
struct _fsd_data_struct {

	gboolean	initialized,
			map_state;
	GtkWidget	*fsd;		/* GtkFontSelectionDialog */

};
#define FSD_DATA(p)	((fsd_data_struct *)(p))

/*
 *	Font Selection Dialog Prompt:
 */
struct _fsd_prompt_struct {

	GtkWidget	*toplevel,
			*label,
			*entry,
			*button;

	gchar		*label_text;

};
#define FSD_PROMPT(p)	((fsd_prompt_struct *)(p))
#define FSD_PROMPT_DATA_KEY	"fsd_prompt_data"


static void FSDOKCB(GtkWidget *widget, gpointer data);
static void FSDCancelCB(GtkWidget *widget, gpointer data);
static gint FSDDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);

gint FSDInit(void);
void FSDSetStyle(GtkRcStyle *rc_style);
void FSDSetTransientFor(GtkWidget *w);
gboolean FSDIsQuery(void);
void FSDBreakQuery(void);
gboolean FSDGetResponse(
	const gchar *title,
	const gchar *ok_label, const gchar *cancel_label,
	gchar *start_font_name,
	gchar **font_name_rtn
);
void FSDMap(void);
void FSDUnmap(void);
void FSDShutdown(void);

static void FSDPromptDataDestroyCB(gpointer data);
static void FSDPromptSelectCB(GtkWidget *widget, gpointer data);
GtkWidget *FSDPromptNew(
	const gchar *label, gint label_width,
	gint entry_width,
	gpointer client_data,
	void (*func_cb)(GtkWidget *, gpointer)
);
GtkWidget *FSDPromptNewSimple(
	const gchar *label, gint label_width,
	gint entry_width
);
GtkWidget *FSDPromptGetEntry(GtkWidget *w);
GtkWidget *FSDPromptGetButton(GtkWidget *w);
gchar *FSDPromptGetFontName(GtkWidget *w);
void FSDPromptSetFontName(GtkWidget *w, const gchar *font_name);


static gint block_loop_level;
static fsd_data_struct fsd_data;
static gboolean fsd_got_user_response;
static gchar *response_font_name;


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	OK callback.
 */
static void FSDOKCB(GtkWidget *widget, gpointer data)
{
	gboolean font_loaded;
	GtkFontSelectionDialog *fsd;
	fsd_data_struct *fsdd = FSD_DATA(data);
	if(fsdd == NULL)
	    return;

	if(!fsdd->initialized)
	    return;

	fsd = GTK_FONT_SELECTION_DIALOG(fsdd->fsd);
	if(fsd == NULL)
	    return;

	/* Set user response code to TRUE, indicating OK */
	fsd_got_user_response = TRUE;

	/* Check if the selected font was loaded properly */
	font_loaded = (gtk_font_selection_dialog_get_font(fsd) != NULL) ?
	    TRUE : FALSE;

	/* Get font name, the font name returned by
	 * gtk_font_selection_dialog_get_font_name() is a copy so we
	 * need to delete the previous font name
	 */
	g_free(response_font_name);
	response_font_name = gtk_font_selection_dialog_get_font_name(fsd);

	/* Unmap */
	FSDUnmap();

	/* Break out of blocking loop */
	gtk_main_quit();
	block_loop_level--;
}

/*
 *	Cancel callback.
 */
static void FSDCancelCB(GtkWidget *widget, gpointer data)
{
	fsd_data_struct *fsdd = FSD_DATA(data);
	if(fsdd == NULL)
	    return;

	if(!fsdd->initialized)
	    return;

	/* Set user response code to FALSE, indicating Cancel */
	fsd_got_user_response = FALSE;

	/* Unmap */
	FSDUnmap();

	/* Break out of blocking loop */
	gtk_main_quit();
	block_loop_level--;
}           

/*
 *	Font Selection Dialog toplevel GtkWindow "delete_event" signal
 *	callback.
 */
static gint FSDDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	FSDCancelCB(widget, data);
	return(TRUE);
}


/*
 *	Initializes Font Selection Dialog.
 */
int FSDInit(void)
{
	GtkWidget *w;
	GtkFontSelectionDialog *fsd;
	fsd_data_struct *fsdd = &fsd_data;


	/* Reset local globals */
	block_loop_level = 0;
	fsd_got_user_response = FALSE;

	g_free(response_font_name);
	response_font_name = NULL;

	memset(fsdd, 0x00, sizeof(fsd_data_struct));

	/* Reset values */
	fsdd->initialized = TRUE;
	fsdd->map_state = FALSE;

	/* Create the font selection dialog */
	fsdd->fsd = w = gtk_font_selection_dialog_new("Select Font");
	fsd = GTK_FONT_SELECTION_DIALOG(w);
	gtk_signal_connect(
	    GTK_OBJECT(w), "delete_event",
	    GTK_SIGNAL_FUNC(FSDDeleteEventCB), fsdd
	);

	/* Get GtkFontSelection widget */
	w = fsd->fontsel;

/* Connect some signals to the GtkFontSelection widget? */

	/* OK Button */
	gtk_signal_connect(
	    GTK_OBJECT(fsd->ok_button), "clicked",
	    GTK_SIGNAL_FUNC(FSDOKCB), fsdd
	);
	gtk_widget_show(fsd->ok_button);

	/* Hide apply button */
	if(fsd->apply_button != NULL)
	    gtk_widget_hide(fsd->apply_button);

	/* Cancel Button */
	gtk_signal_connect(
	    GTK_OBJECT(fsd->cancel_button), "clicked",
	    GTK_SIGNAL_FUNC(FSDCancelCB), fsdd
	);
	gtk_widget_show(fsd->cancel_button);

	return(0);
}

/*
 *	Sets the Font Selection Dialog's style.
 */
void FSDSetStyle(GtkRcStyle *rc_style)
{
	GtkWidget *w;
	fsd_data_struct *fsdd = &fsd_data;

	if(!fsdd->initialized)
	    return;

	w = fsdd->fsd;
	if(w != NULL)
	{
	    if(rc_style != NULL)
	    {
		gtk_widget_modify_style(w, rc_style);
	    }
	    else
	    {
		rc_style = gtk_rc_style_new();
		gtk_widget_modify_style_recursive(w, rc_style);
		GTK_RC_STYLE_UNREF(rc_style)
	    }
	}
}

/*
 *	Sets the Font Selection Dialog to be a transient for the
 *	specified GtkWindow.
 *
 *	If w is NULL then transient for will be unset.
 */
void FSDSetTransientFor(GtkWidget *w)
{
	fsd_data_struct *fsdd = &fsd_data;

	if(!fsdd->initialized)
	    return;

	if(fsdd->fsd != NULL)
	{
	    if(w != NULL)
	    {
		/* Given widget if not NULL, must be a window */
		if(!GTK_IS_WINDOW(GTK_OBJECT(w)))
		    return;

		gtk_window_set_modal(
		    GTK_WINDOW(fsdd->fsd), TRUE
		);
		gtk_window_set_transient_for(
		    GTK_WINDOW(fsdd->fsd), GTK_WINDOW(w)
		);
	    }
	    else
	    {
		gtk_window_set_modal(
		    GTK_WINDOW(fsdd->fsd), FALSE
		);
		gtk_window_set_transient_for(
		    GTK_WINDOW(fsdd->fsd), NULL
		);
	    }
	}
}

/*
 *	Returns TRUE if currently blocking for query.
 */
gboolean FSDIsQuery(void)
{
	if(block_loop_level > 0)
	    return(TRUE);
	else
	    return(FALSE);
}

/*
 *	Ends query if any and returns a not available response.
 */
void FSDBreakQuery(void)
{
	fsd_got_user_response = FALSE;

	/* Break out of an additional blocking loops */
	while(block_loop_level > 0)
	{
	    gtk_main_quit();
	    block_loop_level--;
	}
	block_loop_level = 0;
}

/*
 *	Maps the Font Selection Dialog and sets up the inital values.
 *
 *	Returns TRUE if a font was selected or FALSE if user canceled.
 *
 *	For most values that are set NULL, the value is left unchanged.
 *	All given values are coppied.
 *
 *	All returned pointer values should be considered statically
 *	allocated, do not deallocate them.
 */
gboolean FSDGetResponse(
	const gchar *title,
	const gchar *ok_label, const gchar *cancel_label,
	gchar *start_font_name,
	gchar **font_name_rtn
)
{
	GtkWidget *w;
	fsd_data_struct *fsdd = &fsd_data;


	/* Do not handle response if already waiting for a response,
	 * return with a not available response code
	 */
	if(block_loop_level > 0)
	{
	    if(font_name_rtn != NULL)
		*font_name_rtn = NULL;

	    return(FALSE);
	}

	/* Reset global responses values */
	fsd_got_user_response = FALSE;

	g_free(response_font_name);
	response_font_name = NULL;

	/* Reset returns */
	if(font_name_rtn != NULL)
	    (*font_name_rtn) = NULL;


	/* Font selection dialog must be initialized */
	if(!fsdd->initialized)
	    return(fsd_got_user_response);

	/* Get pointer to font selection dialog widget */
	w = fsdd->fsd;
	if(w == NULL)
	    return(fsd_got_user_response);

	/* Update title if specified */
	if(title != NULL)
	    gtk_window_set_title(GTK_WINDOW(w), title);

	/* Update initial start font name if specified */
	if(start_font_name != NULL)
	    gtk_font_selection_dialog_set_font_name(
		GTK_FONT_SELECTION_DIALOG(w),
		start_font_name
	    );

	/* Map font selection dialog as needed */
	FSDMap();

	/* Block GUI untill response */
	block_loop_level++;
	gtk_main();

	/* Unmap font selection dialog just in case it was not unmapped
	 * from any of the callbacks
	 */
	FSDUnmap();

	/* Break out of an additional blocking loops */
	while(block_loop_level > 0)
	{
	    gtk_main_quit();
	    block_loop_level--;
	}
	block_loop_level = 0;


	/* Begin setting returns */

	/* Font name return */
	if(font_name_rtn != NULL)
	    *font_name_rtn = response_font_name;

	return(fsd_got_user_response);
}


/*
 *	Maps the Font Selection Dialog.
 */
void FSDMap(void)
{
	GtkWidget *w;
	fsd_data_struct *fsdd = &fsd_data;

	if(!fsdd->initialized)
	    return;

	w = fsdd->fsd;
	gtk_widget_show_raise(w);
	fsdd->map_state = TRUE;
}

/*
 *	Unmaps the Font Selection Dialog.
 */
void FSDUnmap(void)
{
	fsd_data_struct *fsdd = &fsd_data;

	if(!fsdd->initialized)
	    return;

	if(fsdd->map_state)
	{
	    GtkWidget *w = fsdd->fsd;

	    if(w != NULL)
		gtk_widget_hide(w);

	    fsdd->map_state = FALSE;
	}
}

/*
 *	Shuts down the Font Selection Dialog.
 */
void FSDShutdown(void)
{
	GtkWidget **w;
	fsd_data_struct *fsdd = &fsd_data;


	/* Reset local globals */
	fsd_got_user_response = FALSE;

	g_free(response_font_name);
	response_font_name = NULL;

	/* Break out of an additional blocking loops */
	while(block_loop_level > 0)
	{
	    gtk_main_quit();
	    block_loop_level--;
	}
	block_loop_level = 0;


	/* Is color selection dialog initialized? */
	if(fsdd->initialized)
	{
#define DO_DESTROY_WIDGET	\
{ if(*w != NULL) { gtk_widget_destroy(*w); *w = NULL; } }

	    /* Begin destroying widgets */

	    w = &fsdd->fsd;
	    DO_DESTROY_WIDGET

#undef DO_DESTROY_WIDGET
	}

	/* Clear font selection dialog structure */
	memset(fsdd, 0x00, sizeof(fsd_data_struct));
}


/*
 *	FSD Prompt GtkObject data "destroy" signal callback.
 */
static void FSDPromptDataDestroyCB(gpointer data)
{
	fsd_prompt_struct *p = FSD_PROMPT(data);
	if(p == NULL)
	    return;

	g_free(p->label_text);
	g_free(p);
}

/*
 *	FSD Prompt select callback.
 */
static void FSDPromptSelectCB(GtkWidget *widget, gpointer data)
{
	gboolean status;
	gchar *cur_font_name, *font_name;
	GtkWidget *w, *toplevel;
	fsd_prompt_struct *p = FSD_PROMPT(data);
	if((p == NULL) || FSDIsQuery())
	    return;

	w = p->entry;
	toplevel = gtk_widget_get_toplevel(p->toplevel);

	cur_font_name = STRDUP(
	    gtk_entry_get_text(GTK_ENTRY(w))
	);

	FSDSetTransientFor(toplevel);
	status = FSDGetResponse(
#if defined(PROG_LANGUAGE_SPANISH)
"El Tipo De Letra Selecto",
"Selecto",
"Cancele",
#elif defined(PROG_LANGUAGE_FRENCH)
"Jeu De Caractères Privilégié",
"Privilégié",
"Annuler",
#elif defined(PROG_LANGUAGE_GERMAN)
"Erlesene Schriftart",
"Erlesen",
"Heben",
#elif defined(PROG_LANGUAGE_ITALIAN)
"Scegliere Font",
"Scegliere",
"Annullare",
#elif defined(PROG_LANGUAGE_DUTCH)
"Uitgezocht Lettertype",
"Uitgezocht",
"Annuleer",
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"Selecione Fonte",
"Selecione",
"Cancelamento",
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Utvalgt Font",
"Utvalgt",
"Kanseller",
#else
"Select Font",
"Select",
"Cancel",
#endif
	    cur_font_name,
	    &font_name
	);
	FSDSetTransientFor(NULL);
	if(status && !STRISEMPTY(font_name))
	{
	    gtk_entry_set_text(GTK_ENTRY(w), font_name);
	    gtk_entry_set_position(GTK_ENTRY(w), -1);
	}

	g_free(cur_font_name);
}

/*
 *	Creates a new FSD Prompt.
 */
GtkWidget *FSDPromptNew(
	const gchar *label, gint label_width,
	gint entry_width,
	gpointer client_data,
	void (*func_cb)(GtkWidget *, gpointer)
)
{
	const gint border_minor = 2;
	GtkWidget *w;
	fsd_prompt_struct *p = FSD_PROMPT(
	    g_malloc0(sizeof(fsd_prompt_struct))
	);

	/* Toplevel */
	p->toplevel = w = gtk_hbox_new(FALSE, border_minor);
	gtk_object_set_data_full(
	    GTK_OBJECT(w), FSD_PROMPT_DATA_KEY,
	    p, FSDPromptDataDestroyCB
	);

	/* Label */
	if(!STRISEMPTY(label))
	{
	    GtkWidget *parent = w = gtk_alignment_new(1.0f, 0.5f, 0.0f, 0.0f);
	    gtk_widget_set_usize(w, label_width, -1);
	    gtk_box_pack_start(GTK_BOX(p->toplevel), w, FALSE, FALSE, 0);
	    gtk_widget_show(w);

	    p->label = w = gtk_label_new(label);
	    p->label_text = STRDUP(label);
	    gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_RIGHT);
	    gtk_container_add(GTK_CONTAINER(parent), w);
	    gtk_widget_show(w);
	}

	/* Entry */
	p->entry = w = gtk_entry_new();
	gtk_entry_set_editable(GTK_ENTRY(w), FALSE);
	if(entry_width > 0)
	{
	    gtk_widget_set_usize(w, entry_width, -1);
	    gtk_box_pack_start(GTK_BOX(p->toplevel), w, FALSE, FALSE, 0);
	}
	else
	{
	    gtk_box_pack_start(GTK_BOX(p->toplevel), w, TRUE, TRUE, 0);
	}
	gtk_widget_show(w);

	/* Button */
	p->button = w = GUIButtonPixmap(
	    (guint8 **)icon_fonts_20x20_xpm
	);
	gtk_box_pack_start(GTK_BOX(p->toplevel), w, FALSE, FALSE, 0);
	GUISetWidgetTip(w,
#if defined(PROG_LANGUAGE_SPANISH)
"El clic para escoger el tipo de letra"
#elif defined(PROG_LANGUAGE_FRENCH)
"Le déclic pour choisir le jeu de caractères"
#elif defined(PROG_LANGUAGE_GERMAN)
"Klicken, schriftart auszuwählen"
#elif defined(PROG_LANGUAGE_ITALIAN)
"Lo scatto di scegliere il font"
#elif defined(PROG_LANGUAGE_DUTCH)
"Klik lettertype te selecteren"
#elif defined(PROG_LANGUAGE_PORTUGUESE)
"O estalido selecionar fonte"
#elif defined(PROG_LANGUAGE_NORWEGIAN)
"Klikk velge ut font"
#else
"Click to select font"
#endif
	);
	gtk_widget_show(w);

	if(func_cb != NULL)
	    gtk_signal_connect(
		GTK_OBJECT(p->button), "clicked",
		GTK_SIGNAL_FUNC(func_cb), client_data
	    );

	return(p->toplevel);
}

/*
 *	Creates a new FSD Prompt with the default select signal
 *	connected to the default handler.
 */
GtkWidget *FSDPromptNewSimple(
	const gchar *label, gint label_width,
	gint entry_width
)
{
	GtkWidget *w = FSDPromptNew(
	    label, label_width, entry_width, NULL, NULL
	);
	fsd_prompt_struct *p = FSD_PROMPT(
	    GTK_OBJECT_GET_DATA(w, FSD_PROMPT_DATA_KEY)
	);
	if(p->button != NULL)
	    gtk_signal_connect(
		GTK_OBJECT(p->button), "clicked",
		GTK_SIGNAL_FUNC(FSDPromptSelectCB), p
	    );
	return(w);
}

/*
 *	Gets the FSD Prompt's entry widget.
 */
GtkWidget *FSDPromptGetEntry(GtkWidget *w)
{
	fsd_prompt_struct *p = FSD_PROMPT(
	    GTK_OBJECT_GET_DATA(w, FSD_PROMPT_DATA_KEY)
	);
	return((p != NULL) ? p->entry : NULL);
}

/*
 *	Gets the FSD Prompt's button widget.
 */
GtkWidget *FSDPromptGetButton(GtkWidget *w)
{
	fsd_prompt_struct *p = FSD_PROMPT(
	    GTK_OBJECT_GET_DATA(w, FSD_PROMPT_DATA_KEY)
	);
	return((p != NULL) ? p->button : NULL);
}

/*
 *	Gets the FSD Prompt's currently selected font name.
 */
gchar *FSDPromptGetFontName(GtkWidget *w)
{
	fsd_prompt_struct *p = FSD_PROMPT(
	    GTK_OBJECT_GET_DATA(w, FSD_PROMPT_DATA_KEY)
	);
	return((p != NULL) ?
	    gtk_entry_get_text(GTK_ENTRY(p->entry)) : NULL
	);
}

/*
 *	Sets the FSD Prompts font name.
 */
void FSDPromptSetFontName(GtkWidget *w, const gchar *font_name)
{
	fsd_prompt_struct *p = FSD_PROMPT(
	    GTK_OBJECT_GET_DATA(w, FSD_PROMPT_DATA_KEY)
	);
	GtkEntry *entry = (GtkEntry *)((p != NULL) ?
	    p->entry : NULL
	);
	if(entry != NULL)
	{
	    gtk_entry_set_text(entry, (font_name != NULL) ? font_name : "");
	    gtk_entry_set_position(entry, -1);
	}
}
