/*
 *  $Id: lawn.c 29817 2026-04-16 06:14:53Z yeti-dn $
 *  Copyright (C) 2025 David Nečas (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 "tests/testlibgwy.h"

static void
lawn_assert_equal(GObject *object, GObject *reference)
{
    g_assert_true(GWY_IS_LAWN(object));
    g_assert_true(GWY_IS_LAWN(reference));

    GwyLawn *lawn = GWY_LAWN(object), *lawn_ref = GWY_LAWN(reference);
    g_assert_cmpint(gwy_lawn_get_xres(lawn), ==, gwy_lawn_get_xres(lawn_ref));
    g_assert_cmpint(gwy_lawn_get_yres(lawn), ==, gwy_lawn_get_yres(lawn_ref));
    g_assert_cmpint(gwy_lawn_get_n_curves(lawn), ==, gwy_lawn_get_n_curves(lawn_ref));
    g_assert_cmpint(gwy_lawn_get_n_segments(lawn), ==, gwy_lawn_get_n_segments(lawn_ref));
    g_assert_cmpfloat(gwy_lawn_get_xreal(lawn), ==, gwy_lawn_get_xreal(lawn_ref));
    g_assert_cmpfloat(gwy_lawn_get_yreal(lawn), ==, gwy_lawn_get_yreal(lawn_ref));
    g_assert_cmpfloat(gwy_lawn_get_xoffset(lawn), ==, gwy_lawn_get_xoffset(lawn_ref));
    g_assert_cmpfloat(gwy_lawn_get_yoffset(lawn), ==, gwy_lawn_get_yoffset(lawn_ref));
    g_assert_true(gwy_unit_equal(gwy_lawn_get_unit_xy(lawn), gwy_lawn_get_unit_xy(lawn_ref)));
    guint ncurves = gwy_lawn_get_n_curves(lawn_ref), nsegments = gwy_lawn_get_n_segments(lawn_ref);
    for (guint k = 0; k < ncurves; k++) {
        g_assert_true(gwy_unit_equal(gwy_lawn_get_unit_curve(lawn, k), gwy_lawn_get_unit_curve(lawn_ref, k)));
        g_assert_cmpstr(gwy_lawn_get_curve_label(lawn, k), ==, gwy_lawn_get_curve_label(lawn_ref, k));
    }
    for (guint k = 0; k < nsegments; k++) {
        g_assert_cmpstr(gwy_lawn_get_segment_label(lawn, k), ==, gwy_lawn_get_segment_label(lawn_ref, k));
    }
    guint xres = gwy_lawn_get_xres(lawn), yres = gwy_lawn_get_yres(lawn);
    for (guint i = 0; i < yres; i++) {
        for (guint j = 0; j < xres; j++) {
            gint curvelength, curvelength_ref;
            const gdouble *data = gwy_lawn_get_curves_data_const(lawn, j, i, &curvelength);
            const gdouble *ref_data = gwy_lawn_get_curves_data_const(lawn_ref, j, i, &curvelength_ref);
            g_assert_cmpint(curvelength, ==, curvelength_ref);
            g_assert((data && ref_data) || !curvelength_ref);
            for (guint k = 0; k < curvelength_ref*ncurves; k++)
                g_assert_cmpfloat(data[k], ==, ref_data[k]);

            guint curve_nsegments, curve_nsegments_ref;
            const gint *segments = gwy_lawn_get_segments(lawn, j, i, &curve_nsegments);
            const gint *segments_ref = gwy_lawn_get_segments(lawn_ref, j, i, &curve_nsegments_ref);
            g_assert_cmpint(curve_nsegments, ==, (gint)nsegments);
            g_assert_cmpint(curve_nsegments_ref, ==, (gint)nsegments);
            g_assert((segments && segments_ref) || !nsegments);
            for (guint k = 0; k < 2*nsegments; k++)
                g_assert_cmpint(segments[k], ==, segments_ref[k]);
        }
    }
}

static void
lawn_assert_empty(GwyLawn *lawn)
{
    g_assert_true(GWY_IS_LAWN(lawn));
    guint xres = gwy_lawn_get_xres(lawn), yres = gwy_lawn_get_yres(lawn);
    guint ncurves = gwy_lawn_get_n_curves(lawn);
    for (guint i = 0; i < yres; i++) {
        for (guint j = 0; j < xres; j++) {
            gint curvelength;
            g_assert_null(gwy_lawn_get_curves_data_const(lawn, j, i, &curvelength));
            g_assert_cmpint(curvelength, ==, 0);
            for (guint k = 0; k < ncurves; k++) {
                g_assert_null(gwy_lawn_get_curve_data_const(lawn, j, i, k, &curvelength));
                g_assert_cmpint(curvelength, ==, 0);
            }
        }
    }
}

void
test_lawn_basic(void)
{
    GwyLawn *lawn = gwy_lawn_new(3, 4, 1.6, 7.5, 2, 0);
    g_assert_cmpint(gwy_lawn_get_xres(lawn), ==, 3);
    g_assert_cmpint(gwy_lawn_get_yres(lawn), ==, 4);
    g_assert_cmpint(gwy_lawn_get_n_curves(lawn), ==, 2);
    g_assert_cmpint(gwy_lawn_get_n_segments(lawn), ==, 0);
    g_assert_cmpfloat(gwy_lawn_get_xreal(lawn), ==, 1.6);
    g_assert_cmpfloat(gwy_lawn_get_yreal(lawn), ==, 7.5);
    g_assert_cmpfloat(gwy_lawn_get_dx(lawn), ==, 1.6/3);
    g_assert_cmpfloat(gwy_lawn_get_dy(lawn), ==, 7.5/4);
    g_assert_cmpfloat(gwy_lawn_get_xoffset(lawn), ==, 0);
    g_assert_cmpfloat(gwy_lawn_get_yoffset(lawn), ==, 0);
    lawn_assert_empty(lawn);

    gwy_lawn_set_xreal(lawn, 31);
    gwy_lawn_set_yreal(lawn, 39);
    gwy_lawn_set_xoffset(lawn, -11);
    gwy_lawn_set_yoffset(lawn, 77);
    g_assert_cmpfloat(gwy_lawn_get_xreal(lawn), ==, 31.0);
    g_assert_cmpfloat(gwy_lawn_get_yreal(lawn), ==, 39.0);
    g_assert_cmpfloat(gwy_lawn_get_dx(lawn), ==, 31.0/3);
    g_assert_cmpfloat(gwy_lawn_get_dy(lawn), ==, 39.0/4);
    g_assert_cmpfloat(gwy_lawn_get_xoffset(lawn), ==, -11);
    g_assert_cmpfloat(gwy_lawn_get_yoffset(lawn), ==, 77);

    g_assert_finalize_object(lawn);
}

void
test_lawn_curve_data(void)
{
    enum { ncurves = 2 };
    const gint xres = 3, yres = 4;
    GwyLawn *lawn = gwy_lawn_new(xres, yres, 1.6, 7.5, ncurves, 0);

    static const gdouble curvesdata[ncurves*5] = {
        1.0, -1.0, G_PI, 6e6, 0.1235,
        0.0, 0.00044, G_LN2, 1.0/3.0, GWY_SQRT3,
    };

    gint col1 = 1, row1 = 2;
    gwy_lawn_set_curves(lawn, col1, row1, 5, curvesdata, NULL);

    /* Set the curve data swapped. */
    gint col2 = 0, row2 = 3;
    gwy_lawn_set_curves(lawn, col2, row2, 5, curvesdata, NULL);
    gwy_lawn_set_curve_data(lawn, col2, row2, 0, curvesdata + 5);
    gwy_lawn_set_curve_data(lawn, col2, row2, 1, curvesdata);

    for (guint i = 0; i < yres; i++) {
        for (guint j = 0; j < xres; j++) {
            gint curvelength;
            const gdouble *d = gwy_lawn_get_curves_data_const(lawn, j, i, &curvelength);
            if (i == row1 && j == col1) {
                g_assert_nonnull(d);
                g_assert_cmpint(curvelength, ==, 5);
                for (guint k = 0; k < ncurves*5; k++)
                    g_assert_cmpfloat(d[k], ==, curvesdata[k]);
            }
            else if (i == row2 && j == col2) {
                g_assert_nonnull(d);
                g_assert_cmpint(curvelength, ==, 5);
                for (guint k = 0; k < ncurves*5; k++)
                    g_assert_cmpfloat(d[k], ==, curvesdata[(k + 5) % (ncurves*5)]);
            }
            else {
                g_assert_null(d);
                g_assert_cmpint(curvelength, ==, 0);
            }
        }
    }

    gwy_lawn_clear(lawn);
    lawn_assert_empty(lawn);
    g_assert_finalize_object(lawn);
}

static GwyLawn*
create_lawn_for_serialisation(void)
{
    gint xres = 7, yres = 5, ncurves = 3;
    GwyLawn *lawn = gwy_lawn_new(xres, yres, 26.0, 1.6, ncurves, 0);
    gwy_lawn_set_xoffset(lawn, G_LN2);
    gwy_lawn_set_yoffset(lawn, GWY_SQRT3);
    for (guint i = 0; i < yres; i++) {
        for (guint j = 0; j < xres; j++) {
            guint curvelength = (i*i + j) % 6;
            gdouble data[ncurves*curvelength];
            for (guint k = 0; k < ncurves*curvelength; k++)
                data[k] = sqrt(i) - sqrt(j) + 0.3*k;
            gwy_lawn_set_curves(lawn, j, i, curvelength, data, NULL);
        }
    }

    return lawn;
}

static void
set_lawn_units_for_serialisation(GwyLawn *lawn)
{
    g_assert_cmpint(gwy_lawn_get_n_curves(lawn), ==, 3);
    gwy_unit_set_from_string(gwy_lawn_get_unit_xy(lawn), "m");
    gwy_unit_set_from_string(gwy_lawn_get_unit_curve(lawn, 0), "V");
    gwy_unit_set_from_string(gwy_lawn_get_unit_curve(lawn, 2), "");

    gwy_lawn_set_curve_label(lawn, 1, "Unknown");
    gwy_lawn_set_curve_label(lawn, 2, "Current");
}

static void
set_lawn_segmentation_for_serialisation(GwyLawn *lawn)
{
    gint xres = gwy_lawn_get_xres(lawn), yres = gwy_lawn_get_yres(lawn);
    gint nsegments = 3;

    gint *segments = g_new(gint, 2*xres*yres*nsegments);
    for (gint i = 0; i < 2*xres*yres*nsegments; i++)
        segments[i] = i;

    gwy_lawn_set_segments(lawn, nsegments, segments);
    g_free(segments);

    gwy_lawn_set_segment_label(lawn, 0, "First");
    gwy_lawn_set_segment_label(lawn, 2, "Last");
}

void
test_lawn_serialization(void)
{
    GwyLawn *lawn = create_lawn_for_serialisation();
    serialize_object_and_back(G_OBJECT(lawn), lawn_assert_equal, FALSE, NULL);

    set_lawn_units_for_serialisation(lawn);
    serialize_object_and_back(G_OBJECT(lawn), lawn_assert_equal, FALSE, NULL);

    set_lawn_segmentation_for_serialisation(lawn);
    serialize_object_and_back(G_OBJECT(lawn), lawn_assert_equal, FALSE, NULL);

    gwy_lawn_clear(lawn);
    serialize_object_and_back(G_OBJECT(lawn), lawn_assert_equal, FALSE, NULL);

    g_assert_finalize_object(lawn);
}

void
test_lawn_copy(void)
{
    GwyLawn *lawn = create_lawn_for_serialisation();
    serializable_test_copy(GWY_SERIALIZABLE(lawn), lawn_assert_equal);

    set_lawn_units_for_serialisation(lawn);
    serializable_test_copy(GWY_SERIALIZABLE(lawn), lawn_assert_equal);

    set_lawn_segmentation_for_serialisation(lawn);
    serializable_test_copy(GWY_SERIALIZABLE(lawn), lawn_assert_equal);

    gwy_lawn_clear(lawn);
    serializable_test_copy(GWY_SERIALIZABLE(lawn), lawn_assert_equal);

    g_assert_finalize_object(lawn);
}

void
test_lawn_assign(void)
{
    GwyLawn *lawn = create_lawn_for_serialisation();
    serializable_test_assign(GWY_SERIALIZABLE(lawn), NULL, lawn_assert_equal);

    set_lawn_units_for_serialisation(lawn);
    serializable_test_assign(GWY_SERIALIZABLE(lawn), NULL, lawn_assert_equal);

    set_lawn_segmentation_for_serialisation(lawn);
    serializable_test_assign(GWY_SERIALIZABLE(lawn), NULL, lawn_assert_equal);

    gwy_lawn_clear(lawn);
    serializable_test_assign(GWY_SERIALIZABLE(lawn), NULL, lawn_assert_equal);

    /* TODO: Test assigning to a lawn which already contains stuff. */

    g_assert_finalize_object(lawn);
}

void
test_lawn_curve_labels(void)
{
    GwyLawn *lawn = gwy_lawn_new(5, 3, 11.0, 0.4, 4, 0);

    for (gint i = 0; i < 4; i++)
        g_assert_null(gwy_lawn_get_curve_label(lawn, i));

    gwy_lawn_set_curve_label(lawn, 3, "Labelled curve");
    for (gint i = 0; i < 4; i++)
        g_assert_nonnull(gwy_lawn_get_curve_label(lawn, i));

    gwy_lawn_clear_curve_labels(lawn);
    for (gint i = 0; i < 4; i++)
        g_assert_null(gwy_lawn_get_curve_label(lawn, i));

    g_assert_finalize_object(lawn);
}

void
test_lawn_segment_labels(void)
{
    GwyLawn *lawn = gwy_lawn_new(5, 3, 11.0, 0.4, 1, 3);

    for (gint i = 0; i < 3; i++)
        g_assert_null(gwy_lawn_get_segment_label(lawn, i));

    gwy_lawn_set_segment_label(lawn, 1, "Labelled segment");
    for (gint i = 0; i < 3; i++)
        g_assert_nonnull(gwy_lawn_get_segment_label(lawn, i));

    gwy_lawn_clear_segment_labels(lawn);
    for (gint i = 0; i < 3; i++)
        g_assert_null(gwy_lawn_get_segment_label(lawn, i));

    g_assert_finalize_object(lawn);
}

void
test_lawn_find_curve(void)
{
    GwyLawn *lawn = gwy_lawn_new(5, 3, 11.0, 0.4, 5, 0);

    // The silly case.
    g_assert_cmpint(gwy_lawn_find_curve_by_label(lawn, NULL), ==, 0);

    gwy_lawn_set_curve_label(lawn, 3, "Current");
    gwy_lawn_set_curve_label(lawn, 2, "Current");
    gwy_lawn_set_curve_label(lawn, 4, "Topography");
    g_assert_cmpint(gwy_lawn_find_curve_by_label(lawn, NULL), ==, -1);
    g_assert_cmpint(gwy_lawn_find_curve_by_label(lawn, "Definitely not a curve name"), ==, -1);
    g_assert_cmpint(gwy_lawn_find_curve_by_label(lawn, "Current"), ==, 2);
    g_assert_cmpint(gwy_lawn_find_curve_by_label(lawn, "Topography"), ==, 4);

    g_assert_finalize_object(lawn);
}

void
test_lawn_find_segment(void)
{
    GwyLawn *lawn = gwy_lawn_new(5, 3, 11.0, 0.4, 1, 5);

    // The silly case.
    g_assert_cmpint(gwy_lawn_find_segment_by_label(lawn, NULL), ==, 0);

    gwy_lawn_set_segment_label(lawn, 3, "Hold");
    gwy_lawn_set_segment_label(lawn, 2, "Hold");
    gwy_lawn_set_segment_label(lawn, 4, "Retract");
    g_assert_cmpint(gwy_lawn_find_segment_by_label(lawn, NULL), ==, -1);
    g_assert_cmpint(gwy_lawn_find_segment_by_label(lawn, "Definitely not a segment name"), ==, -1);
    g_assert_cmpint(gwy_lawn_find_segment_by_label(lawn, "Hold"), ==, 2);
    g_assert_cmpint(gwy_lawn_find_segment_by_label(lawn, "Retract"), ==, 4);

    g_assert_finalize_object(lawn);
}

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