/*
 *  $Id: graph_cut.c 30047 2026-06-11 08:30:45Z yeti-dn $
 *  Copyright (C) 2007-2023 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 <glib/gi18n-lib.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <gwy.h>

enum {
    PARAM_CURVE,
    PARAM_ALL,
    PARAM_RANGE_FROM,
    PARAM_RANGE_TO,
};

typedef struct {
    GwyParams *params;
    GwyGraphModel *gmodel;
} ModuleArgs;

typedef struct {
    ModuleArgs *args;
    GtkWidget *dialog;
    GwyParamTable *table;
    GwyGraphModel *gmodel;
} ModuleGUI;

static gboolean         module_register     (void);
static GwyParamDef*     define_module_params(void);
static void             module_main         (GwyGraph *graph);
static GwyDialogOutcome run_gui             (ModuleArgs *args);
static void             param_changed       (ModuleGUI *gui,
                                             gint id);
static void             execute             (ModuleArgs *args,
                                             GwyFile *data);

static GwyModuleInfo module_info = {
    GWY_MODULE_ABI_VERSION,
    &module_register,
    N_("Cut graph"),
    "Petr Klapetek <klapetek@gwyddion.net>",
    "2.0",
    "David Nečas (Yeti) & Petr Klapetek",
    "2007",
};

GWY_MODULE_QUERY2(module_info, graph_cut)

static gboolean
module_register(void)
{
    gwy_graph_func_register("graph_cut",
                            &module_main,
                            N_("/_Basic Operations/_Cut..."),
                            GWY_ICON_GRAPH_CUT,
                            GWY_MENU_FLAG_GRAPH_CURVE,
                            N_("Extract part of graph into new one"));

    return TRUE;
}

static GwyParamDef*
define_module_params(void)
{
    static GwyParamDef *paramdef = NULL;

    if (paramdef)
        return paramdef;

    paramdef = gwy_param_def_new();
    gwy_param_def_set_function_name(paramdef, gwy_graph_func_current());
    gwy_param_def_add_graph_curve(paramdef, PARAM_CURVE, "curve", NULL);
    gwy_param_def_add_boolean(paramdef, PARAM_ALL, "all", _("Cut _all curves"), TRUE);
    /* Not saved to settings. */
    gwy_param_def_add_double(paramdef, PARAM_RANGE_FROM, NULL, _("Range"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0);
    gwy_param_def_add_double(paramdef, PARAM_RANGE_TO, NULL, NULL, -G_MAXDOUBLE, G_MAXDOUBLE, 0.0);

    return paramdef;
}

static void
module_main(GwyGraph *graph)
{
    GwyFile *data;
    GwyDialogOutcome outcome;
    ModuleArgs args;

    gwy_data_browser_get_current(GWY_APP_FILE, &data, 0);
    gwy_clear1(args);
    args.params = gwy_params_new_from_settings(define_module_params());
    args.gmodel = gwy_graph_get_model(graph);

    outcome = run_gui(&args);
    gwy_params_save_to_settings(args.params);
    if (outcome != GWY_DIALOG_CANCEL)
        execute(&args, data);
    g_object_unref(args.params);
}

static GwyDialogOutcome
run_gui(ModuleArgs *args)
{
    GtkWidget *hbox, *graph;
    GwyDialog *dialog;
    GwyParamTable *table;
    GwyDialogOutcome outcome;
    ModuleGUI gui;

    gwy_clear1(gui);
    gui.args = args;
    gui.gmodel = gwy_graph_model_new_alike(args->gmodel);

    gui.dialog = gwy_dialog_new(_("Cut Graph"));
    dialog = GWY_DIALOG(gui.dialog);
    gwy_dialog_add_stock_buttons(dialog, GWY_STOCK_CANCEL, GWY_STOCK_OK, 0);

    hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
    gtk_container_set_border_width(GTK_CONTAINER(hbox), 4);
    gwy_dialog_add_content(dialog, hbox, FALSE, FALSE, 0);

    graph = gwy_graph_new(gui.gmodel);
    gtk_widget_set_size_request(graph, 480, 360);
    gtk_box_pack_end(GTK_BOX(hbox), graph, TRUE, TRUE, 0);
    gwy_graph_enable_user_input(GWY_GRAPH(graph), FALSE);

    table = gui.table = gwy_param_table_new(args->params);

    gwy_param_table_append_graph_curve(table, PARAM_CURVE, args->gmodel);
    gwy_param_table_append_checkbox(table, PARAM_ALL);
    gwy_create_graph_xrange_with_params(table, PARAM_RANGE_FROM, PARAM_RANGE_TO, GWY_GRAPH(graph), args->gmodel);

    gtk_box_pack_start(GTK_BOX(hbox), gwy_param_table_widget(table), FALSE, TRUE, 0);

    g_signal_connect_swapped(table, "param-changed", G_CALLBACK(param_changed), &gui);
    outcome = gwy_dialog_run(dialog);

    g_object_unref(gui.gmodel);

    return outcome;
}

static void
execute(ModuleArgs *args, GwyFile *data)
{
    GwyParams *params = args->params;
    gint i, k, ndata, cstart, cend;
    GwyGraphModel *gmodel;
    GwyGraphCurveModel *gcmodel, *newgcmodel;
    const gdouble *xdata, *ydata;
    gdouble xfrom = gwy_params_get_double(params, PARAM_RANGE_FROM);
    gdouble xto = gwy_params_get_double(params, PARAM_RANGE_TO);
    GArray *newxdata, *newydata;

    if (gwy_params_get_boolean(params, PARAM_ALL)) {
        cstart = 0;
        cend = gwy_graph_model_get_n_curves(args->gmodel);
    }
    else {
        cstart = gwy_params_get_int(params, PARAM_CURVE);
        cend = cstart+1;
    }

    newxdata = g_array_new(FALSE, FALSE, sizeof(gdouble));
    newydata = g_array_new(FALSE, FALSE, sizeof(gdouble));
    gmodel = gwy_graph_model_new_alike(args->gmodel);
    for (k = cstart; k < cend; k++) {
        gcmodel = gwy_graph_model_get_curve(args->gmodel, k);
        xdata = gwy_graph_curve_model_get_xdata(gcmodel);
        ydata = gwy_graph_curve_model_get_ydata(gcmodel);
        ndata = gwy_graph_curve_model_get_n_data(gcmodel);

        g_array_set_size(newxdata, 0);
        g_array_set_size(newydata, 0);
        for (i = 0; i < ndata; i++) {
            if (xdata[i] >= xfrom && xdata[i] < xto) {
                g_array_append_val(newxdata, xdata[i]);
                g_array_append_val(newydata, ydata[i]);
            }
        }
        if (newxdata->len) {
            newgcmodel = gwy_graph_curve_model_copy(gcmodel);
            gwy_graph_curve_model_set_data(newgcmodel,
                                           &g_array_index(newxdata, gdouble, 0), &g_array_index(newydata, gdouble, 0),
                                           newxdata->len);
            gwy_graph_model_add_curve(gmodel, newgcmodel);
            g_object_unref(newgcmodel);
        }
    }

    /* XXX: This may confuse the user if we do not add anything at all. */
    if (gwy_graph_model_get_n_curves(gmodel)) {
        gint newid = gwy_file_add_graph(data, gmodel);
        gwy_file_set_visible(data, GWY_FILE_GRAPH, newid, TRUE);
    }

    g_object_unref(gmodel);
}

static void
param_changed(ModuleGUI *gui, gint id)
{
    ModuleArgs *args = gui->args;

    if (id < 0 || id == PARAM_CURVE || id == PARAM_ALL) {
        gint i, n, curve = gwy_params_get_int(args->params, PARAM_CURVE);
        gboolean all = gwy_params_get_boolean(args->params, PARAM_ALL);

        gwy_graph_model_remove_all_curves(gui->gmodel);
        if (all) {
            n = gwy_graph_model_get_n_curves(args->gmodel);
            for (i = 0; i < n; i++)
                gwy_graph_model_add_curve(gui->gmodel, gwy_graph_model_get_curve(args->gmodel, i));
        }
        else
            gwy_graph_model_add_curve(gui->gmodel, gwy_graph_model_get_curve(args->gmodel, curve));
    }
}

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