/*!
   \file lib/cairodriver/text.c

   \brief GRASS cairo display driver - text subroutines

   (C) 2007-2008 by Lars Ahlzen and the GRASS Development Team

   This program is free software under the GNU General Public License
   (>=v2). Read the file COPYING that comes with GRASS for details.

   \author Lars Ahlzen <lars ahlzen.com> (original contributor)
   \author Glynn Clements
 */

#include <grass/glocale.h>
#include "cairodriver.h"

#if CAIRO_HAS_FT_FONT
#include <cairo-ft.h>
#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 10, 0) || defined(CAIRO_HAS_FC_FONT)
#define USE_FONTCONFIG 1
#include <fontconfig/fontconfig.h>
#else
#define USE_FONTCONFIG 0
#endif
#endif /* CAIRO_HAS_FT_FONT */

#ifdef HAVE_ICONV_H
#include <iconv.h>
#endif

static char *convert(const char *in)
{
    size_t ilen, olen;
    char *out;

    ilen = strlen(in);
    olen = 3 * ilen + 1;

    out = G_malloc(olen);

#ifdef HAVE_ICONV_H
    {
        const char *encoding = font_get_encoding();
        char *p1 = (char *)in;
        char *p2 = out;
        size_t ret;
        iconv_t cd;

        if ((cd = iconv_open("UTF-8", encoding)) == (iconv_t)-1)
            G_fatal_error(_("Unable to convert from <%s> to UTF-8"), encoding);

        ret = iconv(cd, &p1, &ilen, &p2, &olen);

        iconv_close(cd);

        *p2++ = '\0';

        if (ret > 0)
            G_warning(_("Some characters could not be converted to UTF-8"));
    }
#else
    {
        const unsigned char *p1 = (const unsigned char *)in;
        unsigned char *p2 = (unsigned char *)out;
        int i, j;

        for (i = j = 0; i < ilen; i++) {
            int c = p1[i];

            if (c < 0x80)
                p2[j++] = c;
            else {
                p2[j++] = 0xC0 + (c >> 6);
                p2[j++] = 0x80 + (c & 0x3F);
            }
        }

        p2[j++] = '\0';
    }
#endif

    return out;
}

static void set_matrix(void)
{
    static cairo_matrix_t mat;

    if (matrix_valid)
        return;

    cairo_matrix_init_identity(&mat);
    cairo_matrix_scale(&mat, text_size_x, text_size_y);
    cairo_matrix_rotate(&mat, -text_rotation * M_PI / 180);

    cairo_set_font_matrix(cairo, &mat);

    matrix_valid = 1;
}

/*!
   \brief Draw text

   \param str string to be drawn
 */
void Cairo_Text(const char *str)
{
    char *utf8 = convert(str);

    if (!utf8)
        return;

    set_matrix();

    cairo_move_to(cairo, cur_x, cur_y);
    cairo_show_text(cairo, utf8);

    G_free(utf8);

    ca.modified = 1;
}

/*
   \brief Get text bounding box

   \param str string
   \param[out] t,b,l,r top, bottom, left, right corner
 */
void Cairo_text_box(const char *str, double *t, double *b, double *l, double *r)
{
    char *utf8 = convert(str);
    cairo_text_extents_t ext;

    if (!utf8)
        return;

    set_matrix();

    cairo_text_extents(cairo, utf8, &ext);

    G_free(utf8);

    *l = cur_x + ext.x_bearing;
    *r = cur_x + ext.x_bearing + ext.width;
    *t = cur_y + ext.y_bearing;
    *b = cur_y + ext.y_bearing + ext.height;
}

static void set_font_toy(const char *name)
{
    char *font = G_store(name);
    cairo_font_weight_t weight = CAIRO_FONT_WEIGHT_NORMAL;
    cairo_font_slant_t slant = CAIRO_FONT_SLANT_NORMAL;

    for (;;) {
        char *p = strrchr(font, '-');

        if (!p)
            break;

        if (G_strcasecmp(p, "-bold") == 0)
            weight = CAIRO_FONT_WEIGHT_BOLD;
        else if (strcasecmp(p, "-italic") == 0)
            slant = CAIRO_FONT_SLANT_ITALIC;
        else if (G_strcasecmp(p, "-oblique") == 0)
            slant = CAIRO_FONT_SLANT_OBLIQUE;
        else
            break;

        *p = '\0';
    }

    cairo_select_font_face(cairo, font, slant, weight);

    G_free(font);
}

#if USE_FONTCONFIG

static void fc_init(void)
{
    static int initialized;

    if (!initialized) {
        FcInit();
        initialized = 1;
    }
}

static void set_font_fc(const char *name)
{
    static cairo_font_face_t *face;
    FcPattern *pattern;
    FcResult result;

    fc_init();

    if (face) {
        cairo_font_face_destroy(face);
        face = NULL;
    }

    pattern = FcNameParse((FcChar8 *)name);
    FcDefaultSubstitute(pattern);
    FcConfigSubstitute(FcConfigGetCurrent(), pattern, FcMatchPattern);
    pattern = FcFontMatch(FcConfigGetCurrent(), pattern, &result);
    face = cairo_ft_font_face_create_for_pattern(pattern);
    cairo_set_font_face(cairo, face);
}

static void font_list_fc(char ***list, int *count, int verbose)
{
    FcPattern *pattern;
    FcObjectSet *objset;
    FcFontSet *fontset;
    char **fonts = *list;
    int num_fonts = *count;
    int i;

    fc_init();

    pattern = FcPatternCreate();
    objset = FcObjectSetBuild(FC_FAMILY, FC_STYLE, (char *)NULL);
    fontset = FcFontList(NULL, pattern, objset);

    fonts = G_realloc(fonts, (num_fonts + fontset->nfont) * sizeof(char *));

    for (i = 0; i < fontset->nfont; i++) {
        char buf[1024];
        FcPattern *pat = fontset->fonts[i];
        FcChar8 *family = (FcChar8 *)"", *style = (FcChar8 *)"";

        FcPatternGetString(pat, FC_FAMILY, 0, &family);
        FcPatternGetString(pat, FC_STYLE, 0, &style);

        if (verbose)
            sprintf(buf, "%s:%s|%s:%s|%d|%s|%d|%s|", family, style, family,
                    style, GFONT_DRIVER, "", 0, "utf-8");
        else
            sprintf(buf, "%s:%s", family, style);

        fonts[num_fonts++] = G_store(buf);
    }

    FcObjectSetDestroy(objset);
    FcPatternDestroy(pattern);
    FcFontSetDestroy(fontset);

    *list = fonts;
    *count = num_fonts;
}

#endif

static const char *toy_fonts[12] = {
    "sans",  "sans-italic",  "sans-bold",  "sans-bold-italic",
    "serif", "serif-italic", "serif-bold", "serif-bold-italic",
    "mono",  "mono-italic",  "mono-bold",  "mono-bold-italic",
};

static const int num_toy_fonts = 12;

static int is_toy_font(const char *name)
{
    int i;

    for (i = 0; i < num_toy_fonts; i++)
        if (G_strcasecmp(name, toy_fonts[i]) == 0)
            return 1;

    return 0;
}

/*!
   \brief Set font

   \param name font name
 */
void Cairo_set_font(const char *name)
{
#if USE_FONTCONFIG
    if (is_toy_font(name))
        set_font_toy(name);
    else
        set_font_fc(name);
#else
    set_font_toy(name);
#endif
}

static void font_list_toy(char ***list, int *count, int verbose)
{
    char **fonts = *list;
    int num_fonts = *count;
    int i;

    fonts = G_realloc(fonts, (num_fonts + num_toy_fonts) * sizeof(char *));

    for (i = 0; i < num_toy_fonts; i++) {
        char buf[256];

        if (verbose)
            sprintf(buf, "%s|%s|%d|%s|%d|%s|", toy_fonts[i], toy_fonts[i],
                    GFONT_DRIVER, "", 0, "utf-8");
        else
            strcpy(buf, toy_fonts[i]);
        fonts[num_fonts++] = G_store(buf);
    }

    *list = fonts;
    *count = num_fonts;
}

/*!
   \brief Get list of fonts

   \param[out] list font list
   \param[out] count number of items in the list
 */
void Cairo_font_list(char ***list, int *count)
{
    font_list_toy(list, count, 0);
#if USE_FONTCONFIG
    font_list_fc(list, count, 0);
#endif
}

/*!
   \brief Get fonts into

   \param[out] list font list
   \param[out] count number of items in the list
 */
void Cairo_font_info(char ***list, int *count)
{
    font_list_toy(list, count, 1);
#if USE_FONTCONFIG
    font_list_fc(list, count, 1);
#endif
}
