/** \file font.c
 * Font selection and loading.
 */

/*  XTrkCad - Model Railroad CAD
 *  Copyright (C) 2005 Dave Bullis
 *
 *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

#define GTK_DISABLE_SINGLE_INCLUDES
#define GDK_DISABLE_DEPRECATED
#define GTK_DISABLE_DEPRECATED
#define GSEAL_ENABLE

#include <gtk/gtk.h>
#include <gdk/gdk.h>

#include "wlib.h"
#include "gtkint.h"
#include "i18n.h"

/*
 * Macro for debug purposes. Possible debug macro values:
 *
 *   0 - no messages to console (use this value when building in release mode)
 *   1 - send errors
 *   2 - send details
 *   3 - send more details
 */
#define WLIB_FONT_DEBUG 0

static gchar sampleText[] = "AbCdE0129!@$&()[]{}";

static GtkWidget *fontSelectionDialog;


/*****************************************************************************
 * FONT HANDLERS
 */

#define FW_MEDIUM	(0)
#define FW_BOLD		(1)
#define FS_REGULAR	(0)
#define FS_ITALIC	(1)

/* absoluteFontSize was introduced to keep the font size information synchron
 * between the Dt.size of ctext.c and it's drop list on the status bar and
 * the font size coming from the gtk font dialog which is located in this file */
int absoluteFontSize = 18;

struct wFont_t {
	PangoFontDescription *fontDescription;
};

static wFont_p standardFonts[F_HELV-F_TIMES+1][2][2];
static wFont_p curFont = NULL;

/**
 * Callback for font selection dialog
 *
 * \param fontSelectionDialog IN dialog
 * \param response IN response code from dialog
 * \param data IN unused
 * \return
 */

static void fontSelectionDialogCallback(GtkFontSelectionDialog
                                        *fontSelectionDialog, gint response, gpointer data)
{
	if (response == GTK_RESPONSE_APPLY || response == GTK_RESPONSE_OK) {
		gchar *fontName;

		fontName = gtk_font_selection_dialog_get_font_name(fontSelectionDialog);
		wPrefSetString("font", "name", fontName);
		pango_font_description_free(curFont->fontDescription);
		curFont->fontDescription = pango_font_description_from_string(fontName);
		absoluteFontSize = (pango_font_description_get_size(
		                            curFont->fontDescription))/PANGO_SCALE;
#if WLIB_FONT_DEBUG >= 2
		fprintf(stderr, "new font selection:\n");
		fprintf(stderr, "  font name \"%s\"\n", fontName);
		fprintf(stderr, "  font size is %d\n",
		        pango_font_description_get_size(curFont->fontDescription)/PANGO_SCALE);
		fprintf(stderr, "  font size is absolute %d\n",
		        pango_font_description_get_size_is_absolute(curFont->fontDescription));
#endif
		g_free(fontName);
	}

	if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_CANCEL) {
		gtk_widget_hide(GTK_WIDGET(fontSelectionDialog));
	}
}

static wBool_t fontInitted = FALSE;

static wBool_t fontInit()
{
	const char *fontNames[] = {
		"times 18",
		"times italic 18",
		"times bold 18",
		"times bold italic 18",
		"helvetica 18",
		"helvetica oblique 18",
		"helvetica bold 18",
		"helvetica bold oblique 18",
	};
	int s = 0;
	int i, j, k;

	for (i = F_TIMES; i <= F_HELV; ++i) {
		for (j = FW_MEDIUM; j <= FW_BOLD; ++j) {
			for (k = FS_REGULAR; k <= FS_ITALIC; ++k) {
				PangoFontDescription *fontDescription = pango_font_description_from_string(
				                fontNames[s++]);
				wFont_p standardFont = (wFont_p) malloc(sizeof(struct wFont_t));
				standardFont->fontDescription = fontDescription;
				standardFonts[i-F_TIMES][j][k] = standardFont;
			}
		}
	}

	if (curFont == NULL) {
		curFont = (wFont_p) malloc(sizeof(struct wFont_t));

		if (curFont == NULL) {
			return FALSE;
		}

		const char *fontName = wPrefGetString("font", "name");
		curFont->fontDescription = pango_font_description_from_string(
		                                   fontName ? fontName : "helvetica 18");
		absoluteFontSize = (int) PANGO_PIXELS(pango_font_description_get_size(
		                curFont->fontDescription));
	}

	fontInitted = TRUE;
	return TRUE;
}


static double fontFactor = 1.0;

#define FONTSIZE_TO_PANGOSIZE(fs) ((gint) ((fs) * (fontFactor) + .5))

/**
 * Create a Pango layout with a specified font and font size
 *
 * \param widget IN
 * \param cairo IN cairo context
 * \param fp IN font
 * \param fs IN size
 * \param s IN ???
 * \param width_p OUT width of layout
 * \param height_p OUT height of layout
 * \param ascent_p OUT ascent of layout
 * \param descent_p OUT descent of layout
 * \return    the created Pango layout
 */

PangoLayout *wlibFontCreatePangoLayout(GtkWidget *widget,
                                       void *cairo,
                                       wFont_p fp,
                                       wFontSize_t fs,
                                       const char *s,
                                       wDrawPix_t *width_p,
                                       wDrawPix_t *height_p,
                                       wDrawPix_t *ascent_p,
                                       wDrawPix_t *descent_p,
                                       wDrawPix_t *baseline_p)
{
	if (!fontInitted) {
		fontInit();
	}

	PangoLayout *layout = NULL;
	gchar *utf8 = wlibConvertInput(s);
	/* RPH -- pango_cairo_create_layout() is missing in CentOS 4.8.
	          CentOS 4.8 only has GTK 2.4.13 and Pango 1.6.0 and does not have
	          libpangocairo at all.
	          pango_cairo_create_layout() was introduced with Pango 1.10. */
#if PANGO_VERSION_MAJOR >= 1 && PANGO_VERSION_MINOR >= 10

	if (cairo != NULL) {
		layout = pango_cairo_create_layout((cairo_t *) cairo);
		pango_layout_set_text(layout, utf8, -1);
	} else
#endif
		layout = gtk_widget_create_pango_layout(widget, utf8);

	PangoFontDescription *fontDescription = (fp ? fp : curFont)->fontDescription;
	PangoContext *context;
	PangoFontMetrics *metrics;
	/* set attributes */
	pango_font_description_set_size(fontDescription,
	                                FONTSIZE_TO_PANGOSIZE(fs) * PANGO_SCALE);
	pango_layout_set_font_description(layout, fontDescription);
	/* get layout measures */
	gint width_i, height_i;
	pango_layout_get_size(layout, &width_i, &height_i);
	*width_p = width_i / PANGO_SCALE;
	*height_p = height_i / PANGO_SCALE;
	context = gtk_widget_create_pango_context(widget);
	metrics = pango_context_get_metrics(context, fontDescription,
	                                    pango_context_get_language(context));
	*baseline_p = pango_layout_get_baseline(layout) / PANGO_SCALE;
	*ascent_p  = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE;
	*descent_p = pango_font_metrics_get_descent(metrics) / PANGO_SCALE;
	pango_font_metrics_unref(metrics);
	g_object_unref(context);
#if WLIB_FONT_DEBUG >= 3
	fprintf(stderr, "font layout created:\n");
	fprintf(stderr, "  widget:         %p\n", widget);
	//fprintf(stderr, "  font description:%p\n", fp);
	fprintf(stderr, "  font size:      %f\n", fs);
	fprintf(stderr, "  layout text:    \"%s\" (utf8)\n", utf8);
	fprintf(stderr, "  layout width:   %d\n", *width_p);
	fprintf(stderr, "  layout height:  %d\n", *height_p);
	fprintf(stderr, "  layout ascent:  %d (pixels)\n", *ascent_p);
	fprintf(stderr, "  layout descent: %d (pixels)\n", *descent_p);
#endif
	return layout;
}

/**
 * Destroy a previously allocated  Pango layout
 * \return
 */

void wlibFontDestroyPangoLayout(PangoLayout *layout)
{
	g_object_ref_sink(layout);
	g_object_unref(layout);
}

/**
 * Initialize font data
 * \return
 */

void wInitializeFonts()
{
	if (!fontInitted) {
		fontInit();
	}
}

/**
 * Initialize and run the font dialog
 *
 * \param title IN dialog box title
 * \return
 */

void wSelectFont(
        const char * title)
{
	if (!fontInitted) {
		fontInit();
	}

	if (fontSelectionDialog == NULL) {
		fontSelectionDialog = gtk_font_selection_dialog_new(_("Font Select"));
		gtk_window_set_position(GTK_WINDOW(fontSelectionDialog), GTK_WIN_POS_MOUSE);
		gtk_window_set_modal(GTK_WINDOW(fontSelectionDialog), TRUE);
		gtk_font_selection_dialog_set_preview_text(GTK_FONT_SELECTION_DIALOG(
		                        fontSelectionDialog), sampleText);
		g_signal_connect(G_OBJECT(fontSelectionDialog), "response",
		                 G_CALLBACK(fontSelectionDialogCallback), NULL);
		g_signal_connect(G_OBJECT(fontSelectionDialog), "destroy",
		                 G_CALLBACK(gtk_widget_destroyed), &fontSelectionDialog);
	}

	gtk_window_set_title(GTK_WINDOW(fontSelectionDialog), title);

	if (curFont != NULL) {
		gchar *fontName;

		/* the curFont description contains the latest font info
		 * which is depended on the current scale
		 * overwrite it with the absoluteFontSize */
		pango_font_description_set_size(curFont->fontDescription,
		                                FONTSIZE_TO_PANGOSIZE(absoluteFontSize) * PANGO_SCALE);
		fontName = pango_font_description_to_string(curFont->fontDescription);
		gtk_font_selection_dialog_set_font_name(GTK_FONT_SELECTION_DIALOG(
		                fontSelectionDialog), fontName);
		g_free(fontName);
	}

	gtk_widget_show(fontSelectionDialog);
}

/**
 * Get the current font
 *
 * \return    describe the return value
 */

static wFont_p wlibSelectedFont(void)
{
	if (!fontInitted) {
		fontInit();
	}

	return curFont;
}

/**
 * Get the default font size
 *
 * \return    describe the return value
 */

wFontSize_t wSelectedFontSize(void)
{
	if (!fontInitted) {
		fontInit();
	}

#if WLIB_FONT_DEBUG >= 3
	fprintf(stderr, "the font size of current font description is: %d\n",
	        pango_font_description_get_size(curFont->fontDescription)/PANGO_SCALE);
	fprintf(stderr, "the font size of absoluteFontSize is: %d\n",absoluteFontSize);
#endif
	return absoluteFontSize;
}

/**
 * Set the default font size
 *
 * \param size IN font size
 * \return    describe the return value
 */

void wSetSelectedFontSize(wFontSize_t size)
{
	absoluteFontSize = size;
}

/**
 * get the Pango font description as a string from a font definition.
 * If the font definition is NULL, a default font is return. This is
 * the current font if one is set. If not the first font from the font
 * list is returned.
 *
 * \param fp IN the font definition
 * \return    the font description
 */

const char *wlibFontTranslate(wFont_p fp)
{
	static gchar *fontName = NULL;

	if (fontName != NULL) {
		g_free(fontName);
	}

	if (!fontInitted) {
		fontInit();
	}

	if (fp == NULL) {
		fp = wlibSelectedFont();
	}

	if (fp == NULL) {
		fp = standardFonts[0][FW_MEDIUM][FS_REGULAR];
	}

	fontName = pango_font_description_to_string(fp->fontDescription);
#if WLIB_FONT_DEBUG >= 2
	fprintf(stderr, "font translation: ");
	fprintf(stderr, "  \"%s\"\n", fontName);
#endif
	return (const char *) fontName;
}

/**
 * Return description for one of the standard fonts
 *
 * \return
 */

wFont_p wStandardFont(int face, wBool_t bold, wBool_t italic)
{
	if (!fontInitted) {
		fontInit();
	}

	return standardFonts[face-F_TIMES][bold][italic];
}
