/*
 *  $Id: grain_makesel.c 29972 2026-05-21 17:17:25Z yeti-dn $
 *  Copyright (C) 2014-2026 David Necas (Yeti), Petr Klapetek, Sven Neumann.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net, neumann@jpk.com.
 *
 *  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>

#define RUN_MODES GWY_RUN_IMMEDIATE

static gboolean module_register           (void);
static void     grain_inscribe_discs      (GwyFile *data,
                                           GwyRunModeFlags mode);
static void     grain_circumscribe_circles(GwyFile *data,
                                           GwyRunModeFlags mode);
static void     grain_inscribe_rects      (GwyFile *data,
                                           GwyRunModeFlags mode);
static void     grain_circumscribe_bboxes (GwyFile *data,
                                           GwyRunModeFlags mode);

static GwyModuleInfo module_info = {
    GWY_MODULE_ABI_VERSION,
    &module_register,
    N_("Creates selections visualizing grains."),
    "Yeti <yeti@gwyddion.net>",
    "2.0",
    "David Nečas (Yeti)",
    "2015",
};

GWY_MODULE_QUERY2(module_info, grain_makesel)

static gboolean
module_register(void)
{
    gwy_image_func_register("grain_inscribe_discs",
                            &grain_inscribe_discs,
                            N_("/_Grains/Select _Inscribed Discs"),
                            GWY_ICON_GRAIN_INSCRIBED_CIRCLE,
                            RUN_MODES,
                            GWY_MENU_FLAG_IMAGE | GWY_MENU_FLAG_IMAGE_MASK,
                            N_("Create a selection visualizing discs inscribed into grains"));
    gwy_image_func_register("grain_circumscribe_circles",
                            &grain_circumscribe_circles,
                            N_("/_Grains/Select _Circumscribed Circles"),
                            GWY_ICON_GRAIN_CIRCUMCIRCLE,
                            RUN_MODES,
                            GWY_MENU_FLAG_IMAGE | GWY_MENU_FLAG_IMAGE_MASK,
                            N_("Create a selection visualizing grain circumcircles"));
    gwy_image_func_register("grain_inscribe_rects",
                            &grain_inscribe_rects,
                            N_("/_Grains/Select Inscribed _Rectangles"),
                            GWY_ICON_GRAIN_INSCRIBED_BOX,
                            RUN_MODES,
                            GWY_MENU_FLAG_IMAGE | GWY_MENU_FLAG_IMAGE_MASK,
                            N_("Create a selection visualizing rectangles inscribed into grains"));
    gwy_image_func_register("grain_circumscribe_bboxes",
                            &grain_circumscribe_bboxes,
                            N_("/_Grains/Select _Bounding Boxes"),
                            GWY_ICON_GRAIN_BOUNDING_BOX,
                            RUN_MODES,
                            GWY_MENU_FLAG_IMAGE | GWY_MENU_FLAG_IMAGE_MASK,
                            N_("Create a selection visualizing grain bounding boxes"));

    return TRUE;
}

static GwySelection*
create_selection(GType type, guint *ngrains)
{
    GParamSpecInt *pspec;
    GObjectClass *klass;

    klass = g_type_class_ref(type);
    pspec = (GParamSpecInt*)g_object_class_find_property(klass, "max-objects");
    g_return_val_if_fail(G_IS_PARAM_SPEC_UINT(pspec), NULL);

    if ((gint)*ngrains > pspec->maximum) {
        g_warning("Too many grains for %s, only first %d will be shown.", g_type_name(type), pspec->maximum);
        *ngrains = pspec->maximum;
    }
    return g_object_new(type, "max-objects", *ngrains, NULL);
}

static void
make_circles(GwyFile *data, gint id, GwyField *field, gdouble **rxydata, guint ngrains)
{
    gdouble xoffset = gwy_field_get_xoffset(field), yoffset = gwy_field_get_yoffset(field);
    GwySelection *selection;

    selection = create_selection(GWY_TYPE_SELECTION_ELLIPSE, &ngrains);
    for (guint i = 1; i <= ngrains; i++) {
        gdouble r = rxydata[0][i], x = rxydata[1][i] - xoffset, y = rxydata[2][i] - yoffset;
        gdouble xy[4] = { x - r, y - r, x + r, y + r };
        gwy_selection_set_object(selection, i-1, xy);
    }
    gwy_dict_pass_object(GWY_DICT(data), gwy_file_key_image_selection(id, "ellipse"), selection);
}

static void
exinscribe_discs(GwyFile *data, const GwyGrainQuantity *quantities)
{
    GwyField *field;
    GwyNield *mask;
    gint id;

    gwy_data_browser_get_current(GWY_APP_FIELD, &field,
                                 GWY_APP_MASK_FIELD, &mask,
                                 GWY_APP_FIELD_ID, &id,
                                 0);

    guint ngrains = gwy_nield_max(mask);
    gdouble *circles = g_new(gdouble, 3*(ngrains + 1));
    gdouble *values[3];
    for (guint i = 0; i < 3; i++)
        values[i] = circles + i*(ngrains + 1);

    gwy_nield_grain_quantities(mask, field, quantities, 3, values);
    make_circles(data, id, field, values, ngrains);
    g_free(circles);
}

/* FIXME: It would be nice to have something like that also for minimum and maximum bounding dimensions. */
static void
grain_inscribe_discs(GwyFile *data, GwyRunModeFlags mode)
{
    static const GwyGrainQuantity quantities[] = {
        GWY_GRAIN_INSCRIBED_DISC_R, GWY_GRAIN_INSCRIBED_DISC_X, GWY_GRAIN_INSCRIBED_DISC_Y,
    };

    g_return_if_fail(mode & RUN_MODES);
    exinscribe_discs(data, quantities);
}

static void
grain_circumscribe_circles(GwyFile *data, GwyRunModeFlags mode)
{
    static const GwyGrainQuantity quantities[] = {
        GWY_GRAIN_CIRCUMCIRCLE_R, GWY_GRAIN_CIRCUMCIRCLE_X, GWY_GRAIN_CIRCUMCIRCLE_Y,
    };

    g_return_if_fail(mode & RUN_MODES);
    exinscribe_discs(data, quantities);
}

static void
make_boxes(GwyFile *data, gint id, GwyField *field, const gint *boxes, guint ngrains)
{
    gdouble dx = gwy_field_get_dx(field), dy = gwy_field_get_dy(field);
    GwySelection *selection;

    selection = create_selection(GWY_TYPE_SELECTION_RECTANGLE, &ngrains);
    for (guint i = 1; i <= ngrains; i++) {
        const gint *bb = boxes + 4*i;
        gdouble x = dx*bb[0], y = dy*bb[1], w = dx*bb[2], h = dy*bb[3];
        gdouble xy[4] = { x, y, x + w, y + h };
        gwy_selection_set_object(selection, i-1, xy);
    }
    gwy_dict_pass_object(GWY_DICT(data), gwy_file_key_image_selection(id, "rectangle"), selection);
}

static void
grain_inscribe_rects(GwyFile *data, GwyRunModeFlags mode)
{
    GwyField *field;
    GwyNield *mask;
    gint id;

    g_return_if_fail(mode & RUN_MODES);
    gwy_data_browser_get_current(GWY_APP_FIELD, &field,
                                 GWY_APP_MASK_FIELD, &mask,
                                 GWY_APP_FIELD_ID, &id,
                                 0);
    g_return_if_fail(field && mask);

    guint ngrains;
    gint *boxes = gwy_nield_inscribed_boxes(mask, &ngrains);
    make_boxes(data, id, field, boxes, ngrains);
    g_free(boxes);
}

static void
grain_circumscribe_bboxes(GwyFile *data, GwyRunModeFlags mode)
{
    GwyField *field;
    GwyNield *mask;
    gint id;

    g_return_if_fail(mode & RUN_MODES);
    gwy_data_browser_get_current(GWY_APP_FIELD, &field,
                                 GWY_APP_MASK_FIELD, &mask,
                                 GWY_APP_FIELD_ID, &id,
                                 0);
    g_return_if_fail(field && mask);

    guint ngrains;
    gint *boxes = gwy_nield_bounding_boxes(mask, &ngrains);
    make_boxes(data, id, field, boxes, ngrains);
    g_free(boxes);
}

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