/*
 *  $Id: module-cmap.c 29973 2026-05-21 17:19:43Z yeti-dn $
 *  Copyright (C) 2021-2025 David Necas (Yeti).
 *  E-mail: yeti@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 <string.h>
#include "libgwyddion/macros.h"
#include "libgwyddion/utils.h"
#include "libgwyddion/dict.h"
#include "libgwyapp/module-cmap.h"
#include "libgwyapp/module-internal.h"

/* The curve map function information */
typedef struct {
    const gchar *name;
    const gchar *menu_path;
    const gchar *icon_name;
    const gchar *tooltip;
    GwyRunModeFlags run;
    guint sens_mask;
    GwyProcessFunc func;
} GwyProcessFuncInfo;

static GHashTable *cmap_funcs = NULL;
static GPtrArray *call_stack = NULL;

/**
 * gwy_cmap_func_register:
 * @name: Name of function to register.  It should be a valid identifier and if a module registers only one function,
 *        module and function names should be the same.
 * @func: The function itself.
 * @menu_path: Menu path under Curve Map menu.  The menu path should be marked translatabe, but passed untranslated
 *             (to allow merging of translated and untranslated submenus).
 * @icon_name: Stock icon id for toolbar.
 * @run: Supported run modes.  Curve map data processing functions can have two run modes: %GWY_RUN_IMMEDIATE (no
 *       questions asked) and %GWY_RUN_INTERACTIVE (a modal dialog with parameters).
 * @sens_mask: Sensitivity mask (a combination of #GwyMenuSensFlags flags). Usually it contains
 *             #GWY_MENU_FLAG_CMAP, possibly other requirements.
 * @tooltip: Tooltip for this function.
 *
 * Registers a curve map data processing function.
 *
 * Note: the string arguments are not copied as modules are not expected to vanish.  If they are constructed
 * (non-constant) strings, do not free them. Should modules ever become unloadable they will get a chance to clean-up.
 *
 * Returns: Normally %TRUE; %FALSE on failure.
 **/
gboolean
gwy_cmap_func_register(const gchar *name,
                       GwyProcessFunc func,
                       const gchar *menu_path,
                       const gchar *icon_name,
                       GwyRunModeFlags run,
                       guint sens_mask,
                       const gchar *tooltip)
{
    GwyProcessFuncInfo *func_info;

    g_return_val_if_fail(name, FALSE);
    g_return_val_if_fail(func, FALSE);
    g_return_val_if_fail(menu_path, FALSE);
    g_return_val_if_fail(run & GWY_RUN_MASK, FALSE);
    gwy_debug("name = %s, menu path = %s, run = %d, func = %p", name, menu_path, run, func);

    if (!cmap_funcs) {
        gwy_debug("Initializing...");
        cmap_funcs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
        call_stack = g_ptr_array_new();
    }

    if (!gwy_ascii_strisident(name, "_-", NULL))
        g_warning("Function name `%s' is not a valid identifier. It may be rejected in future.", name);
    if (g_hash_table_lookup(cmap_funcs, name)) {
        g_warning("Duplicate function `%s', keeping only first", name);
        return FALSE;
    }

    func_info = g_new0(GwyProcessFuncInfo, 1);
    func_info->name = name;
    func_info->func = func;
    func_info->menu_path = menu_path;
    func_info->icon_name = icon_name;
    func_info->tooltip = tooltip;
    func_info->run = run;
    func_info->sens_mask = sens_mask;

    g_hash_table_insert(cmap_funcs, (gpointer)func_info->name, func_info);
    if (!_gwy_module_add_registered_function(GWY_MODULE_PREFIX_CMAP, name)) {
        g_hash_table_remove(cmap_funcs, func_info->name);
        return FALSE;
    }

    return TRUE;
}

/**
 * gwy_cmap_func_run:
 * @name: Curve map data processing function name.
 * @data: Data (a #GwyFile).
 * @run: How the function should be run.
 *
 * Runs a curve map processing function identified by @name.
 **/
void
gwy_cmap_func_run(const gchar *name,
                  GwyFile *data,
                  GwyRunModeFlags run)
{
    GwyProcessFuncInfo *func_info;

    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_if_fail(func_info);
    g_return_if_fail(run & func_info->run);
    g_ptr_array_add(call_stack, func_info);
    func_info->func(data, run);
    g_return_if_fail(call_stack->len);
    g_ptr_array_set_size(call_stack, call_stack->len-1);
}

/**
 * gwy_cmap_func_foreach:
 * @function: Function to run for each curve map function.  It will get function name (constant string owned by module
 *            system) as its first argument, @user_data as the second argument.
 * @user_data: Data to pass to @function.
 *
 * Calls a function for each curve map function.
 **/
void
gwy_cmap_func_foreach(GwyNameFunc function,
                      gpointer user_data)
{
    foreach_func(cmap_funcs, function, user_data);
}

/**
 * gwy_cmap_func_exists:
 * @name: Curve map data processing function name.
 *
 * Checks whether a curve map processing function exists.
 *
 * Returns: %TRUE if function @name exists, %FALSE otherwise.
 **/
gboolean
gwy_cmap_func_exists(const gchar *name)
{
    return cmap_funcs && g_hash_table_lookup(cmap_funcs, name);
}

/**
 * gwy_cmap_func_get_run_types:
 * @name: Curve map data processing function name.
 *
 * Returns run modes supported by a curve map processing function.
 *
 * Returns: The run mode bit mask.
 **/
GwyRunModeFlags
gwy_cmap_func_get_run_types(const gchar *name)
{
    GwyProcessFuncInfo *func_info;

    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_val_if_fail(func_info, 0);

    return func_info->run;
}

/**
 * gwy_cmap_func_get_menu_path:
 * @name: Curve map data processing function name.
 *
 * Returns the menu path of a curve map processing function.
 *
 * The returned menu path is only the tail part registered by the function, i.e., without any leading "/Curve Map".
 *
 * Returns: The menu path.  The returned string is owned by the module.
 **/
const gchar*
gwy_cmap_func_get_menu_path(const gchar *name)
{
    GwyProcessFuncInfo *func_info;

    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_val_if_fail(func_info, 0);

    return func_info->menu_path;
}

/**
 * gwy_cmap_func_get_icon_name:
 * @name: Curve map data processing function name.
 *
 * Gets icon name of a curve map processing function.
 *
 * Returns: The icon name.  The returned string is owned by the module.
 **/
const gchar*
gwy_cmap_func_get_icon_name(const gchar *name)
{
    GwyProcessFuncInfo *func_info;

    g_return_val_if_fail(cmap_funcs, NULL);
    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_val_if_fail(func_info, NULL);

    return func_info->icon_name;
}

/**
 * gwy_cmap_func_get_tooltip:
 * @name: Curve map data processing function name.
 *
 * Gets tooltip for a curve map processing function.
 *
 * Returns: The tooltip.  The returned string is owned by the module.
 **/
const gchar*
gwy_cmap_func_get_tooltip(const gchar *name)
{
    GwyProcessFuncInfo *func_info;

    g_return_val_if_fail(cmap_funcs, NULL);
    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_val_if_fail(func_info, NULL);

    return func_info->tooltip;
}

/**
 * gwy_cmap_func_get_sensitivity_mask:
 * @name: Curve map data processing function name.
 *
 * Gets menu sensititivy mask for a curve map processing function.
 *
 * Returns: The menu item sensitivity mask (a combination of #GwyMenuSensFlags flags).
 **/
guint
gwy_cmap_func_get_sensitivity_mask(const gchar *name)
{
    GwyProcessFuncInfo *func_info;

    func_info = g_hash_table_lookup(cmap_funcs, name);
    g_return_val_if_fail(func_info, 0);

    return func_info->sens_mask;
}

/**
 * gwy_cmap_func_current:
 *
 * Obtains the name of currently running curve map processing function.
 *
 * If no curve map processing function is currently running, %NULL is returned.
 *
 * If multiple nested functions are running (which is not usual but technically possible), the innermost function name
 * is returned.
 *
 * Returns: The name of currently running curve map processing function or %NULL.
 **/
const gchar*
gwy_cmap_func_current(void)
{
    GwyProcessFuncInfo *func_info;

    if (!call_stack || !call_stack->len)
        return NULL;

    func_info = (GwyProcessFuncInfo*)g_ptr_array_index(call_stack, call_stack->len-1);
    return func_info->name;
}

gboolean
_gwy_cmap_func_remove(const gchar *name)
{
    gwy_debug("%s", name);
    if (!g_hash_table_remove(cmap_funcs, name)) {
        g_warning("Cannot remove function %s", name);
        return FALSE;
    }
    return TRUE;
}

/**
 * SECTION: module-cmap
 * @title: Curve map modules
 * @short_description: Curve map data processing modules
 *
 * Curve map data processing modules implement function processing curve map data represented with #GwyLawn.  They
 * reigster functions that get a #GwyFile with data and either modify it or create a new data from it. In this
 * regard, they are quite similar to regular (two-dimensional) data processing functions but they live in separate
 * menus, toolbars, etc.
 **/

/**
 * GwyProcessFuncInfo:
 * @name: An unique curve map data processing function name.
 * @menu_path: A path under "/Curve Map" where the function should appear. It must start with "/".
 * @cmap: The function itself.
 * @run: Possible run-modes for this function.
 * @sens_flags: Sensitivity flags.  Curve map data processing function should include, in general,
 *              %GWY_MENU_FLAG_CMAP.  Functions constructing synthetic data from nothing do not have to specify
 *              even %GWY_MENU_FLAG_CMAP.
 *
 * Information about one curve map data processing function.
 **/

/* 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 : */
