/*
 *  $Id: xy.c 29467 2026-02-12 18:47:32Z yeti-dn $
 *  Copyright (C) 2025-2026 David Necas (Yeti).
 *  E-mail: yeti@gwyddion.net.
 *
 *  The quicksort algorithm was copied from GNU C library, Copyright (C) 1991, 1992, 1996, 1997, 1999 Free Software
 *  Foundation, Inc.  See below.
 *
 *  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 <string.h>
#include <stdlib.h>

#include "libgwyddion/macros.h"
#include "libgwyddion/math.h"
#include "libgwyddion/serializable-boxed.h"
#include "libgwyddion/serializable-utils.h"
#include "libgwyddion/xy.h"

#define TYPE_NAME "GwyXY"

enum {
    ITEM_X,
    ITEM_Y,
    NUM_ITEMS,
};

static void     itemize  (gpointer boxed,
                          GwySerializableGroup *group);
static gpointer construct(GwySerializableGroup *group,
                          GwyErrorList **error_list);

static const GwySerializableItem serializable_items[NUM_ITEMS] = {
    { .name = "x", .ctype = GWY_SERIALIZABLE_DOUBLE, },
    { .name = "y", .ctype = GWY_SERIALIZABLE_DOUBLE, },
};

GType
gwy_xy_get_type(void)
{
    static GType type = 0;

    if (G_UNLIKELY(!type)) {
        type = g_boxed_type_register_static(TYPE_NAME, (GBoxedCopyFunc)gwy_xy_copy, (GBoxedFreeFunc)gwy_xy_free);
        static const GwySerializableBoxedInfo boxed_info = {
            sizeof(GwyXY), NUM_ITEMS, itemize, construct, NULL, NULL,
        };
        gwy_serializable_boxed_register_static(type, &boxed_info);
    }

    return type;
}

/**
 * gwy_xy_new:
 * @x: X-coordinate.
 * @y: Y-coordinate.
 *
 * Creates Cartesian coordinates in plane.
 *
 * This is mostly useful for language bindings.
 *
 * Returns: New XY structure.  The result should be freed using gwy_xy_free().
 **/
GwyXY*
gwy_xy_new(gdouble x, gdouble y)
{
    GwyXY *xy = g_new(GwyXY, 1);
    xy->x = x;
    xy->y = y;
    return xy;
}

/**
 * gwy_xy_copy:
 * @xy: Cartesian coordinates in plane.
 *
 * Copies Cartesian coordinates in plane.
 *
 * Returns: A copy of @xy. The result should be freed using gwy_xy_free().
 **/
GwyXY*
gwy_xy_copy(const GwyXY *xy)
{
    g_return_val_if_fail(xy, NULL);
    return g_memdup2(xy, sizeof(GwyXY));
}

/**
 * gwy_xy_free:
 * @xy: Cartesian coordinates in plane.
 *
 * Frees Cartesian coordinates in plane created with gwy_xy_copy().
 **/
void
gwy_xy_free(GwyXY *xy)
{
    g_free(xy);
}

static void
itemize(gpointer boxed, GwySerializableGroup *group)
{
    GwyXY *xy = (GwyXY*)boxed;
    gwy_serializable_group_append_double(group, serializable_items + ITEM_X, xy->x);
    gwy_serializable_group_append_double(group, serializable_items + ITEM_Y, xy->y);
}

static gpointer
construct(GwySerializableGroup *group, GwyErrorList **error_list)
{
    GwySerializableItem its[NUM_ITEMS];
    gwy_assign(its, serializable_items, NUM_ITEMS);
    gwy_deserialize_filter_items(its, NUM_ITEMS, group, TYPE_NAME, error_list);

    GwyXY *xy = g_new(GwyXY, 1);
    xy->x = its[ITEM_X].value.v_double;
    xy->y = its[ITEM_Y].value.v_double;
    return xy;
}

#undef gwy_xy_len
#undef gwy_xy_len2
#undef gwy_xy_dist2
#undef gwy_xy_dotprod
#undef gwy_xy_crossprodz

/**
 * gwy_xy_len:
 * @xy: Cartesian coordinates in plane.
 *
 * Computes the length of a plane vector.
 *
 * Returns: The vector length.
 **/
gdouble
gwy_xy_len(const GwyXY *xy)
{
    return _gwy_xy_len_implementation(xy);
}

/**
 * gwy_xy_len2:
 * @xy: Cartesian coordinates in plane.
 *
 * Computes the squared length of a plane vector.
 *
 * Returns: The squared vector length.
 **/
gdouble
gwy_xy_len2(const GwyXY *xy)
{
    return _gwy_xy_len2_implementation(xy);
}

/**
 * gwy_xy_dist2:
 * @xy: Cartesian coordinates in plane.
 * @other: Cartesian coordinates in plane.
 *
 * Computes the squared length of the difference of two plane vectors.
 *
 * Returns: The squared length.
 **/
gdouble
gwy_xy_dist2(const GwyXY *xy, const GwyXY *other)
{
    return _gwy_xy_dist2_implementation(xy, other);
}

/**
 * gwy_xy_dotprod:
 * @xy: Cartesian coordinates in plane.
 * @other: Cartesian coordinates in plane.
 *
 * Computes the inner product of two plane vectors.
 *
 * Returns: The inner product @xy⋅@other.
 **/
gdouble
gwy_xy_dotprod(const GwyXY *xy, const GwyXY *other)
{
    return _gwy_xy_dotprod_implementation(xy, other);
}

/**
 * gwy_xy_crossprodz:
 * @xy: Cartesian coordinates in plane.
 * @other: Cartesian coordinates in plane.
 *
 * Computes the Z-component of a cross-product of two vectors given as plane vectors.
 *
 * The vectors are interpreted the X and Y components of two space vectors which have zero Z-components. Their
 * cross-product then has only one nonzero component: Z. This is what the function calculates.
 *
 * Returns: The Z-component of inner product @xy×@other.
 **/
gdouble
gwy_xy_crossprodz(const GwyXY *xy, const GwyXY *other)
{
    return _gwy_xy_crossprodz_implementation(xy, other);
}

/**
 * SECTION: xy
 * @section_id: GwyXY
 * @title: GwyXY
 * @short_description: Cartesian coordinates in plane
 **/

/**
 * GwyXY:
 * @x: X-coordinate.
 * @y: Y-coordinate.
 *
 * Representation of Cartesian coordinates in plane.
 **/

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