/****************************************************************************
 *
 * MODULE:       g.mkfontcap
 * AUTHOR(S):    Paul Kelly
 * PURPOSE:      Generates the font configuration file by scanning various
 *               directories for GRASS stroke and Freetype-compatible fonts.
 *
 * COPYRIGHT:    (C) 2007-2024 by 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.
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include <grass/gis.h>
#include <grass/fontcap.h>
#include <grass/glocale.h>

#include "local_proto.h"

char **searchdirs;
int numsearchdirs;

struct GFONT_CAP *fontcap;
int totalfonts;
int maxfonts;

static const char *standarddirs[] = {
    /* These are the directories that are searched for Freetype-compatible
     * font files by default. They may contain an environment variable
     * *at the start* of the string, if enclosed in ${xxx} syntax. */
    "/usr/lib/X11/fonts",     "/usr/share/X11/fonts",  "/usr/share/fonts",
    "/usr/local/share/fonts", "${HOME}/Library/Fonts", "/Library/Fonts",
    "/System/Library/Fonts",  "${WINDIR}\\Fonts",      NULL};

static void add_search_dir(const char *);
static int compare_fonts(const void *, const void *);

int main(int argc, char *argv[])
{
    struct Flag *tostdout;
    struct Option *extradirs;
    struct GModule *module;

    FILE *outstream;
    char *fontcapfile;
    int i;

    G_set_program_name(argv[0]);
    G_no_gisinit();
    G_set_gisrc_mode(G_GISRC_MODE_MEMORY);

    module = G_define_module();
    G_add_keyword(_("general"));
    module->description = _(
        "Generates the font configuration file by scanning various directories "
        "for fonts.");

    tostdout = G_define_flag();
    tostdout->key = 's';
    tostdout->description =
        _("Write font configuration file to standard output instead of "
          "$GISBASE/etc");

    extradirs = G_define_option();
    extradirs->key = "extradirs";
    extradirs->type = TYPE_STRING;
    extradirs->required = NO;
    extradirs->label = _("List of extra directories to scan");
    extradirs->description =
        _("Comma-separated list of extra directories to scan for "
          "Freetype-compatible fonts as well as the defaults (see "
          "documentation)");

    if (G_parser(argc, argv))
        exit(EXIT_FAILURE);

    if (!tostdout->answer) {
        const char *gisbase = G_gisbase();
        const char *alt_file = getenv("GRASS_FONT_CAP");

        if (alt_file)
            fontcapfile = G_store(alt_file);
        else
            G_asprintf(&fontcapfile, "%s/etc/fontcap", gisbase);

        if (!access(fontcapfile, F_OK)) { /* File exists? */
            if (!G_get_overwrite())
                G_fatal_error(_("Fontcap file %s already exists; use "
                                "--overwrite flag if you "
                                "wish to overwrite it"),
                              fontcapfile);
        }
    }

    searchdirs = NULL;
    numsearchdirs = 0;

    /* Prepare list of directories to search */
    if (extradirs->answer) {
#ifndef HAVE_FT2BUILD_H
        G_warning(_("This GRASS installation was compiled without "
                    "Freetype support, extradirs parameter ignored"));
#endif
        char *str = G_store(extradirs->answer);

        while ((str = strtok(str, ","))) {
            add_search_dir(str);
            str = NULL;
        }
    }
    i = -1;
    while (standarddirs[++i])
        add_search_dir(standarddirs[i]);

    totalfonts = maxfonts = 0;
    fontcap = NULL;

    find_stroke_fonts();
    find_freetype_fonts();

    qsort(fontcap, totalfonts, sizeof(struct GFONT_CAP), compare_fonts);

    if (tostdout->answer)
        outstream = stdout;
    else {
        outstream = fopen(fontcapfile, "w");
        if (outstream == NULL)
            G_fatal_error(_("Cannot open %s for writing: %s"), fontcapfile,
                          strerror(errno));
    }

    for (i = 0; i < totalfonts; i++)
        fprintf(outstream, "%s|%s|%d|%s|%d|%s|\n", fontcap[i].name,
                fontcap[i].longname, fontcap[i].type, fontcap[i].path,
                fontcap[i].index, fontcap[i].encoding);

    fclose(outstream);

    exit(EXIT_SUCCESS);
}

static void add_search_dir(const char *name)
{
    char envvar_name[256];
    char *fullname = NULL;

    if (sscanf(name, "${%255[^}]}", envvar_name) == 1) {
        char *envvar_value = getenv(envvar_name);

        /* N.B. If the envvar isn't set, directory is skipped completely */
        if (envvar_value)
            G_asprintf(&fullname, "%s%s", envvar_value,
                       (name + strlen(envvar_name) + 3));
    }
    else
        fullname = G_store(name);

    if (fullname) {
        searchdirs = (char **)G_realloc(searchdirs,
                                        (numsearchdirs + 1) * sizeof(char *));
        searchdirs[numsearchdirs] = fullname;
        G_convert_dirseps_to_host(searchdirs[numsearchdirs]);
        numsearchdirs++;
    }

    return;
}

static int compare_fonts(const void *a, const void *b)
{
    struct GFONT_CAP *aa = (struct GFONT_CAP *)a;
    struct GFONT_CAP *bb = (struct GFONT_CAP *)b;

    /* Sort first by type, then by name */
    if (aa->type != bb->type)
        return (aa->type > bb->type ? 1 : -1);
    else {
        const char *na = aa->name;
        const char *nb = bb->name;

        return G_strcasecmp(na, nb);
    }
}
