/*
 *  $Id: graph-curve-model.c 30146 2026-06-25 12:51:18Z 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
assert_curve_content(GwyGraphCurveModel *curve,
                     const gdouble *ref_xdata, const gdouble *ref_ydata, guint ref_n, gdouble eps)
{
    g_assert_true(GWY_IS_GRAPH_CURVE_MODEL(curve));
    guint n = gwy_graph_curve_model_get_n_data(curve);
    g_assert_cmpuint(n, ==, ref_n);
    const gdouble *xdata = gwy_graph_curve_model_get_xdata(curve);
    const gdouble *ydata = gwy_graph_curve_model_get_ydata(curve);
    if (ref_n) {
        g_assert_nonnull(xdata);
        g_assert_nonnull(ydata);
    }

    if (eps > 0.0) {
        for (guint i = 0; i < ref_n; i++) {
            g_assert_cmpfloat_with_epsilon(xdata[i], ref_xdata[i], eps);
            g_assert_cmpfloat_with_epsilon(ydata[i], ref_ydata[i], eps);
        }
    }
    else {
        for (guint i = 0; i < ref_n; i++) {
            g_assert_cmpfloat(xdata[i], ==, ref_xdata[i]);
            g_assert_cmpfloat(ydata[i], ==, ref_ydata[i]);
        }
    }
}

void
curve_model_assert_equal(GObject *object, GObject *reference)
{
    g_assert_true(GWY_IS_GRAPH_CURVE_MODEL(object));
    g_assert_true(GWY_IS_GRAPH_CURVE_MODEL(reference));

    GwyGraphCurveModel *curve = GWY_GRAPH_CURVE_MODEL(object), *curve_ref = GWY_GRAPH_CURVE_MODEL(reference);
    g_assert_cmpint(gwy_graph_curve_model_get_n_data(curve), ==, gwy_graph_curve_model_get_n_data(curve_ref));
    assert_properties_equal(object, reference);
    assert_curve_content(curve,
                         gwy_graph_curve_model_get_xdata(curve_ref),
                         gwy_graph_curve_model_get_ydata(curve_ref),
                         gwy_graph_curve_model_get_n_data(curve_ref),
                         0.0);
}

void
test_curve_model_basic(void)
{
    GwyGraphCurveModel *curve = gwy_graph_curve_model_new();

    const GwyRGBA color = { 0.4, 0.12, 0.9, 1.0 };
    g_object_set(curve,
                 "description", "This curve",
                 "mode", GWY_GRAPH_CURVE_LINE_POINTS,
                 "point-type", GWY_SYMBOL_TRIANGLE_RIGHT,
                 "line-style", GWY_LINE_DASH_DOT,
                 "color", &color,
                 NULL);

    GwySymbolType point_type;
    GwyLineStyle line_style;
    GwyGraphCurveMode mode;
    gchar *description;
    GwyRGBA *curve_color;

    g_object_get(curve,
                 "description", &description,
                 "mode", &mode,
                 "point-type", &point_type,
                 "line-style", &line_style,
                 "color", &curve_color,
                 NULL);

    g_assert_cmpstr(description, ==, "This curve");
    g_assert_cmpuint(mode, ==, GWY_GRAPH_CURVE_LINE_POINTS);
    g_assert_cmpuint(point_type, ==, GWY_SYMBOL_TRIANGLE_RIGHT);
    g_assert_cmpuint(line_style, ==, GWY_LINE_DASH_DOT);
    g_assert_true(gwy_serializable_boxed_equal(GWY_TYPE_RGBA, curve_color, &color));
    g_free(description);

    gwy_graph_curve_model_get_color(curve, curve_color);
    g_assert_true(gwy_serializable_boxed_equal(GWY_TYPE_RGBA, curve_color, &color));

    gwy_rgba_free(curve_color);

    g_assert_finalize_object(curve);
}

void
test_curve_model_data(void)
{
    const gdouble xdata[5] = { 1, 2, 3, 4, 5 };
    const gdouble ydata[5] = { 7.7, -3.3, 11.2, 5.0, 0.2 };
    const gdouble interleaved[10] = { 1, 7.7, 2, -3.3, 3, 11.2, 4, 5.0, 5, 0.2 };
    const gdouble xordered3[5] = { 2, 1, 3 };
    const gdouble yordered3[5] = { -3.3, 7.7, 11.2 };

    GwyGraphCurveModel *curve = gwy_graph_curve_model_new();
    assert_curve_content(curve, NULL, NULL, 0, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));
    gwy_graph_curve_model_enforce_order(curve);
    assert_curve_content(curve, NULL, NULL, 0, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));

    gwy_graph_curve_model_set_data(curve, xdata, ydata, 5);
    assert_curve_content(curve, xdata, ydata, 5, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));
    gwy_graph_curve_model_enforce_order(curve);
    assert_curve_content(curve, xdata, ydata, 5, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));

    gwy_graph_curve_model_set_data(curve, ydata, xdata, 3);
    assert_curve_content(curve, ydata, xdata, 3, 0.0);
    g_assert_false(gwy_graph_curve_model_is_ordered(curve));
    gwy_graph_curve_model_enforce_order(curve);
    assert_curve_content(curve, yordered3, xordered3, 3, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));

    gwy_graph_curve_model_set_data_interleaved(curve, interleaved, 5);
    assert_curve_content(curve, xdata, ydata, 5, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));
    gwy_graph_curve_model_enforce_order(curve);
    assert_curve_content(curve, xdata, ydata, 5, 0.0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));

    gwy_graph_curve_model_set_data(curve, ydata, xdata, 5);
    gwy_graph_curve_model_set_data(curve, ydata, xdata, 0);
    g_assert_true(gwy_graph_curve_model_is_ordered(curve));

    g_assert_finalize_object(curve);
}

static GwyGraphCurveModel*
create_curve_for_serialisation(void)
{
    GwyGraphCurveModel *curve = gwy_graph_curve_model_new();
    gdouble *xydata = g_new(gdouble, 2*7);
    for (guint i = 0; i < 7; i++) {
        xydata[2*i] = sqrt(i) - G_PI;
        xydata[2*i + 1] = sin(4*i);
    }
    gwy_graph_curve_model_set_data_interleaved(curve, xydata, 7);
    g_free(xydata);

    return curve;
}

static void
set_curve_properties_for_serialisation(GwyGraphCurveModel *curve)
{
    /* FIXME GTK3: We used to ignore alpha here (not store it in GWY files at all). So it is the expected behaviour
     * for now and do not test values α ≠ 1 until we break the file format. */
    const GwyRGBA color = { 0.6, 0.0, 0.4, 1.0 };
    g_object_set(curve,
                 "description", "Another curve",
                 "mode", GWY_GRAPH_CURVE_LINE,
                 "point-type", GWY_SYMBOL_ASTERISK,
                 "line-style", GWY_LINE_DASH_SPARSE,
                 "line-width", 2.0,
                 "point-size", 11.0,
                 "color", &color,
                 NULL);
}

void
test_curve_model_serialization(void)
{
    GwyGraphCurveModel *curve = create_curve_for_serialisation();
    serialize_object_and_back(G_OBJECT(curve), curve_model_assert_equal, FALSE, NULL);

    set_curve_properties_for_serialisation(curve);
    serialize_object_and_back(G_OBJECT(curve), curve_model_assert_equal, FALSE, NULL);

    g_assert_finalize_object(curve);
}

void
test_curve_model_copy(void)
{
    GwyGraphCurveModel *curve = create_curve_for_serialisation();
    serializable_test_copy(GWY_SERIALIZABLE(curve), curve_model_assert_equal);

    set_curve_properties_for_serialisation(curve);
    serializable_test_copy(GWY_SERIALIZABLE(curve), curve_model_assert_equal);

    g_assert_finalize_object(curve);
}

void
test_curve_model_assign(void)
{
    GwyGraphCurveModel *curve = create_curve_for_serialisation();
    serializable_test_assign(GWY_SERIALIZABLE(curve), NULL, curve_model_assert_equal);

    set_curve_properties_for_serialisation(curve);
    serializable_test_assign(GWY_SERIALIZABLE(curve), NULL, curve_model_assert_equal);

    GwyGraphCurveModel *another = gwy_graph_curve_model_new();
    g_object_set(another,
                 "description", "Different one",
                 "mode", GWY_GRAPH_CURVE_LINE_POINTS,
                 "point-type", GWY_SYMBOL_FILLED_TRIANGLE_LEFT,
                 "line-style", GWY_LINE_DOT,
                 "line-width", 3.0,
                 "point-size", 16.0,
                 NULL);
    serializable_test_assign(GWY_SERIALIZABLE(curve), GWY_SERIALIZABLE(another), curve_model_assert_equal);
    g_assert_finalize_object(another);

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