/*
 *  $Id: xyz_crop.c 30111 2026-06-22 11:02:00Z yeti-dn $
 *  Copyright (C) 2016-2023 David Necas (Yeti).
 *
 *  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 <gtk/gtk.h>
#include <gwy.h>

#define RUN_MODES (GWY_RUN_INTERACTIVE)

enum {
    PREVIEW_SIZE = 360,
    MAXORDER = 10
};

enum {
    PARAM_KEEPOFFSET
};

typedef enum {
    METHOD_FULL      = 0,
    METHOD_NEIGHBORS = 1,
} ZDriftMethodType;

typedef struct {
    GwyParams *params;
    GwyScatter *scatter;
    GwyField *field;
    gdouble xy[4];
} ModuleArgs;

typedef struct {
    ModuleArgs *args;
    GtkWidget *dialog;
    GwyParamTable *table_options;
    GwyVectorLayer *vlayer;
    GwySelection *selection;
    GwyRectSelectionLabels *rlabels;
    gdouble rsel[4];
    gint isel[4];
} ModuleGUI;

static gboolean         module_register          (void);
static GwyParamDef*     define_module_params     (void);
static GwyScatter*      execute                  (ModuleArgs *args);
static GwyDialogOutcome run_gui                  (ModuleArgs *args,
                                                  GwyFile *data,
                                                  gint id);
static void             param_changed            (ModuleGUI *gui,
                                                  gint id);
static void             module_main              (GwyFile *data,
                                                  GwyRunModeFlags mode);
static void             selection_changed        (ModuleGUI *gui);
static void             rect_updated             (ModuleGUI *gui);
static void             update_selected_rectangle(ModuleGUI *gui);

static GwyModuleInfo module_info = {
    GWY_MODULE_ABI_VERSION,
    &module_register,
    N_("XYZ data crop."),
    "Petr Klapetek <klapetek@gwyddion.net>",
    "2.0",
    "Petr Klapetek",
    "2023",
};

GWY_MODULE_QUERY2(module_info, xyz_crop)

static gboolean
module_register(void)
{
    gwy_xyz_func_register("xyz_crop",
                          module_main,
                          N_("/Crop..."),
                          NULL,
                          RUN_MODES,
                          GWY_MENU_FLAG_XYZ,
                          N_("Crop"));

    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_xyz_func_current());
    gwy_param_def_add_boolean(paramdef, PARAM_KEEPOFFSET, "keepoffsets", _("_Keep offsets"), FALSE);

    return paramdef;
}

static void
module_main(GwyFile *data, GwyRunModeFlags mode)
{
    GwyDialogOutcome outcome = GWY_DIALOG_PROCEED;
    ModuleArgs args;
    gwy_clear1(args);
    GwyScatter *result = NULL;
    gint id, newid;

    g_return_if_fail(mode & RUN_MODES);

    gwy_data_browser_get_current(GWY_APP_SCATTER, &args.scatter,
                                 GWY_APP_SCATTER_ID, &id,
                                 0);
    g_return_if_fail(GWY_IS_SCATTER(args.scatter));

    args.params = gwy_params_new_from_settings(define_module_params());
    args.field = gwy_field_new(10, 10, 10, 10, FALSE);

    /* TODO: Select the channel to preview better. Maybe add a combo… */
    gwy_preview_scatter_to_field(args.scatter, 0, args.field,
                                 PREVIEW_SIZE, PREVIEW_SIZE,
                                 GWY_PREVIEW_SCATTER_FILL);

    if (mode == GWY_RUN_INTERACTIVE) {
        outcome = run_gui(&args, data, id);
        gwy_params_save_to_settings(args.params);
        if (outcome == GWY_DIALOG_CANCEL)
            goto end;
    }

    if (outcome == GWY_DIALOG_PROCEED)
        result = execute(&args);

    if (result) {
        newid = gwy_file_add_xyz(data, result);
        gwy_file_set_visible(data, GWY_FILE_XYZ, newid, TRUE);
        gwy_file_set_title(data, GWY_FILE_XYZ, newid, _("Cropped"), TRUE);
        gwy_file_sync_items(data, GWY_FILE_XYZ, id,
                            data, GWY_FILE_XYZ, newid,
                            GWY_FILE_ITEM_PALETTE, FALSE);
        gwy_log_add(data, GWY_FILE_XYZ, id, newid);
        g_object_unref(result);
    }

end:
    g_object_unref(args.params);
}

static GwyDialogOutcome
run_gui(ModuleArgs *args, GwyFile *data, gint id)
{
    GwyDialog *dialog;
    ModuleGUI gui;
    GwyParamTable *table;
    GtkWidget *vbox, *hbox, *dataview;
    GwyDialogOutcome outcome;

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

    gui.args = args;

    dataview = gwy_create_preview(args->field, NULL, PREVIEW_SIZE);
    gwy_setup_data_view(GWY_DATA_VIEW(dataview), data, GWY_FILE_XYZ, id, GWY_FILE_ITEM_PALETTE);
    gui.selection = gwy_create_preview_vector_layer(GWY_DATA_VIEW(dataview), GWY_TYPE_LAYER_RECTANGLE, 1, TRUE);
    g_object_ref(gui.selection);
    gui.vlayer = gwy_data_view_get_interactive_layer(GWY_DATA_VIEW(dataview));
    g_object_ref(gui.vlayer);
    g_signal_connect_swapped(gui.selection, "changed", G_CALLBACK(selection_changed), &gui);

    hbox = gwy_create_dialog_preview_hbox(GTK_DIALOG(dialog), GWY_DATA_VIEW(dataview), FALSE);

    vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
    gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);

    gui.rlabels = gwy_rect_selection_labels_new(TRUE, G_CALLBACK(rect_updated), &gui);
    gtk_box_pack_start(GTK_BOX(vbox), gwy_rect_selection_labels_get_table(gui.rlabels), FALSE, FALSE, 0);

    table = gui.table_options = gwy_param_table_new(args->params);
    gwy_param_table_append_checkbox(table, PARAM_KEEPOFFSET);
    gtk_box_pack_start(GTK_BOX(vbox), gwy_param_table_widget(table), FALSE, FALSE, 0);

    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);

    g_signal_connect_swapped(gui.table_options, "param-changed", G_CALLBACK(param_changed), &gui);

    outcome = gwy_dialog_run(dialog);
    g_object_unref(gui.vlayer);
    g_object_unref(gui.selection);

    return outcome;
}

static void
selection_changed(ModuleGUI *gui)
{
    gint i;
    gdouble xy[4];

    if (!gwy_selection_get_data(gui->selection, NULL)) {
        gtk_dialog_set_response_sensitive(GTK_DIALOG(gui->dialog), GTK_RESPONSE_OK, FALSE);
    }
    else {
        gtk_dialog_set_response_sensitive(GTK_DIALOG(gui->dialog), GTK_RESPONSE_OK, TRUE);
        gwy_selection_get_object(gui->selection, 0, xy);
        for (i = 0; i < 4; i++)
            gui->args->xy[i] = xy[i];
    }
    update_selected_rectangle(gui);
}

static void
rect_updated(ModuleGUI *gui)
{
    gwy_rect_selection_labels_select(gui->rlabels, gui->selection, gui->args->field);
}

static void
update_selected_rectangle(ModuleGUI *gui)
{
    gwy_rect_selection_labels_fill(gui->rlabels, gui->selection, gui->args->field, gui->rsel, gui->isel);
}


static void
param_changed(ModuleGUI *gui, G_GNUC_UNUSED gint id)
{
    gwy_dialog_invalidate(GWY_DIALOG(gui->dialog));
}

static GwyScatter*
execute(ModuleArgs *args)
{
    gboolean keep = gwy_params_get_boolean(args->params, PARAM_KEEPOFFSET);
    GwyScatter *scatter = args->scatter;

    if ((args->xy[2] - args->xy[0]) == 0 || (args->xy[3] - args->xy[1]) == 0)
        return NULL;

    gdouble dfxoff = gwy_field_get_xoffset(args->field);
    gdouble dfyoff = gwy_field_get_yoffset(args->field);
    gdouble xfrom = args->xy[0] + dfxoff;
    gdouble yfrom = args->xy[1] + dfyoff;
    gdouble xto = args->xy[2] + dfxoff;
    gdouble yto = args->xy[3] + dfyoff;

    GwyScatter *result = gwy_scatter_new_part(scatter, xfrom, xto, yfrom, yto);
    if (keep)
        gwy_scatter_scale_xy(result, 1.0, 1.0, -xfrom, -yfrom);

    return result;
}

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