/*
 *  $Id: gwyddion.c 29839 2026-04-20 08:20:21Z yeti-dn $
 *  Copyright (C) 2003-2026 David Necas (Yeti), Petr Klapetek.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net.
 *
 *  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 "config.h"
#include <fftw3.h>
#include <glib/gi18n-lib.h>

#include "libgwyddion/gwyddion.h"

#include "libgwyddion/internal.h"
#include "libgwyddion/omp.h"

static gboolean types_initialized = FALSE;

static void init_fftw(void);
static void init_i18n(void);

/**
 * gwy_init:
 *
 * Basic initialisation of libgwyddion.
 *
 * The function primarily registers libgwyddion types, making their names can be translated to #GType, which is
 * necessary for deserialisation. It also performs other basic initialisation.
 *
 * It ensures the availbility of built-in resources, such as the default #GwyGradient or built-in fitting functions.
 * However, it does not load resources from disk; use gwy_load_resources() for that.
 *
 * You have to call this function from the main thread before using objects from libgwyddion. However, most programs
 * do not use libgwyddion alone. If you use a higher-level Gwyddion library, call its initialisation function instead.
 *
 * It is safe to call this function more than once, subsequent calls are no-op.
 **/
void
gwy_init(void)
{
    if (types_initialized)
        return;

    init_fftw();
    init_i18n();

    /* Disable nesting since it is essentially always bad if we do not do something like a slowly branching recursion
     * (which we don't) and even then some sane limits would have to be set. GOMP sets both max-active-levels and
     * thread-limit to something like 2^{31}-1, which is hardly sane. Library users can override this later, but at
     * their own risk.
     *
     * It seems omp_get_max_threads() still returns counts >= 1 inside an active parallel region.  This means we
     * consume extra resources when preallocating memory for threads, for instance.  To really prevent nesting we
     * would have to do it manually using omp_get_active_levels()...
     */
#ifdef _OPENMP
    omp_set_max_active_levels(1);
#endif

    /* Peek all serializable classes. The functions will generally return NULLs because no objects of the type exist
     * yet. However, it does force registration of the GTypes, which is necessary to get the GType from type name in
     * deserialisation. Otherwise GLib would have no idea the type exists. */
    g_type_class_peek(GWY_TYPE_UNIT);
    g_type_class_peek(GWY_TYPE_DICT);
    g_type_class_peek(GWY_TYPE_INVENTORY);
    g_type_class_peek(GWY_TYPE_RESOURCE);
    g_type_class_peek(GWY_TYPE_STRING_LIST);
    g_type_class_peek(GWY_TYPE_XY);
    g_type_class_peek(GWY_TYPE_XYZ);
    g_type_class_peek(GWY_TYPE_RGBA);
    g_type_class_peek(GWY_TYPE_ENUM);
    g_type_class_peek(GWY_TYPE_VALUE_FORMAT);
    g_type_class_peek(GWY_TYPE_SELECTION);
    g_type_class_peek(GWY_TYPE_SELECTION_AXIS);
    g_type_class_peek(GWY_TYPE_SELECTION_ELLIPSE);
    g_type_class_peek(GWY_TYPE_SELECTION_LATTICE);
    g_type_class_peek(GWY_TYPE_SELECTION_LINE);
    g_type_class_peek(GWY_TYPE_SELECTION_PATH);
    g_type_class_peek(GWY_TYPE_SELECTION_POINT);
    g_type_class_peek(GWY_TYPE_SELECTION_QUAD);
    g_type_class_peek(GWY_TYPE_SELECTION_RANGE);
    g_type_class_peek(GWY_TYPE_SELECTION_RECTANGLE);
    g_type_class_peek(GWY_TYPE_LINE);
    g_type_class_peek(GWY_TYPE_NIELD);
    g_type_class_peek(GWY_TYPE_FIELD);
    g_type_class_peek(GWY_TYPE_BRICK);
    g_type_class_peek(GWY_TYPE_SPECTRA);
    g_type_class_peek(GWY_TYPE_SCATTER);
    g_type_class_peek(GWY_TYPE_LAWN);
    g_type_class_peek(GWY_TYPE_TRIANGULATION);
    g_type_class_peek(GWY_TYPE_PEAKS);
    g_type_class_peek(GWY_TYPE_SPLINE);

    /* Initialise resources, including loading all builtins (which are assumed to be fast). */
    GType resource_types[] = {
        GWY_TYPE_GRADIENT,
        GWY_TYPE_GL_MATERIAL,
        GWY_TYPE_NLFIT_PRESET,
        GWY_TYPE_FD_CURVE_PRESET,
        GWY_TYPE_SHAPE_FIT_PRESET,
        GWY_TYPE_GRAIN_VALUE,
        GWY_TYPE_CDLINE,
        GWY_TYPE_TIP_MODEL,
    };
    for (guint i = 0; i < G_N_ELEMENTS(resource_types); i++) {
        GwyResourceClass *klass = g_type_class_ref(resource_types[i]);
        if (klass->setup_builtins)
            klass->setup_builtins(gwy_resource_class_get_inventory(klass));
        g_type_class_unref(klass);
    }

    types_initialized = TRUE;
}

static void
init_i18n(void)
{
    gchar *locdir;

    locdir = gwy_find_self_path("locale", NULL);
    bindtextdomain(GETTEXT_PACKAGE, locdir);
    g_free(locdir);
    if (!bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"))
        g_critical("Cannot bind gettext `%s' codeset to UTF-8", GETTEXT_PACKAGE);
}

/**
 * gwy_load_resources:
 *
 * Load external libgwyddion resources from disk.
 *
 * This function has to be called from the main thread and gwy_init() must be called first. However, most programs do
 * not use libgwyddion alone. If you use a higher-level Gwyddion library (libgwyui or libgwyapp), its initialisation
 * functions take care of loading the resources.
 *
 * It is safe to call this function more than once. Subsequent calls are no-op. Resources are not reloaded from disk.
 **/
void
gwy_load_resources(void)
{
    static gboolean resources_loaded = FALSE;

    g_assert(types_initialized);
    if (resources_loaded)
        return;

    /* Load resources which can have a non-builtin form from disk. */
    GType resource_types[] = {
        GWY_TYPE_GRADIENT,
        GWY_TYPE_GL_MATERIAL,
        GWY_TYPE_GRAIN_VALUE,
    };
    for (guint i = 0; i < G_N_ELEMENTS(resource_types); i++)
        gwy_resource_class_load(g_type_class_peek(resource_types[i]));

    resources_loaded = TRUE;
}

static void
init_fftw(void)
{
    /* Function fftw_init_threads() must be called before any other FFTW function.  We cannot really ensure someone
     * else has not called some FFTW function before.  This at least ensures we do not. */
#ifdef HAVE_FFTW_WITH_OPENMP
    if (!fftw_init_threads())
        g_warning("Cannot initialise FFTW threads.");
#endif

    G_GNUC_UNUSED gboolean ok = fftw_import_system_wisdom();
    gwy_debug("FFTW3 system wisdom imported: %d", ok);
}

/**
 * gwy_tune_algorithms:
 * @key: Identification of the parameter to set.
 * @value: String representation of new value of the parameter.
 *
 * Modifies internal tunable parameters of libgwyddion algorithms.
 *
 * Do not use this method in applications.
 *
 * The function enables the tuning of internal algorithm parameters or choosing a specific backend algorithm for
 * operations which have multiple. It exists for testing and benchmarking. It is not defined what parameters might
 * exist, what are the valid values and what effect changing them might have. Tests and benchmarks that use it are
 * always bound to a specific version of Gwyddion.
 **/
void
gwy_tune_algorithms(const gchar *key,
                    const gchar *value)
{
    g_return_if_fail(key);
    if (gwy_strequal(key, "rank-filter-method"))
        _gwy_tune_rank_filter_method(value);
    else
        g_warning("Unknown tunable parameter %s.", key);
}

/**
 * SECTION:gwyddion
 * @title: gwyddion
 * @short_description: Base functions, library initialisation
 * @see_also: #GwySerializable
 *
 * Gwyddion classes has to be initialised before they can be safely deserialised. The function gwy_type_init()
 * performs this initialisation.
 **/

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
