/*
 *  $Id: pygwy.override 23993 2021-08-16 14:44:58Z yeti-dn $
 *  Copyright (C) 2008 Jan Horak, 2015-2018 David Necas (Yeti)
 *  E-mail: xhorak@gmail.com, 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.
 *
 *  Description: This file contains overriden functions which cannot be
 *  automatically generated by  pygwy-codegen.
 */

%%
headers
#include <Python.h>
#include <pygobject.h>
#include <pygtk/pygtk.h>
#include <libprocess/gwyprocess.h>
#include <libgwyddion/gwyddion.h>
#include <libgwymodule/gwymodule.h>
#include <libdraw/gwydraw.h>
#include <libgwydgets/gwydgets.h>
#include <app/gwyapp.h>
#include <app/gwymoduleutils-file.h>
#include <app/gwymoduleutils.h>

typedef gboolean GwyBooleanOutArg;
typedef gint GwyIntOutArg;
typedef gint GwyIntInOutArg;
typedef gdouble GwyDoubleOutArg;
typedef gdouble GwyDoubleInOutArg;
typedef GwyRGBA GwyRGBAOutArg;

typedef GwyNLFitter GwyMathNLFit;

#define gwy_si_value_format_new gwy_si_unit_value_format_new
#define gwy_si_value_format_copy gwy_si_unit_value_format_copy
#define gwy_si_value_format_free gwy_si_unit_value_format_free
#define gwy_si_value_format_clone gwy_si_unit_value_format_clone
#define gwy_si_value_format_set_units gwy_si_unit_value_format_set_units

/* Helpers for array impedance matching.  ATM we create temporary GArrays.
 * Here we do not bother with typing and just use GArray; wrap_calls.c has
 * typedefs for various array types to make them tranparent. */
static GArray*
create_double_garray_from_sequence(PyObject *object)
{
    GArray *array;
    gdouble *a;
    gsize n, i;

    if (!PySequence_Check(object))
        return NULL;

    n = PySequence_Size(object);
    array = g_array_new(FALSE, FALSE, sizeof(gdouble));
    g_array_set_size(array, n);
    a = (gdouble*)array->data;
    for (i = 0; i < n; i++) {
        PyObject *item = PySequence_GetItem(object, i);
        if (PyFloat_Check(item))
            a[i] = PyFloat_AsDouble(item);
        else if (PyNumber_Check(item)) {
            PyObject *number = PyNumber_Float(item);
            a[i] = PyFloat_AsDouble(number);
            Py_DECREF(number);
        }
        else {
            g_array_free(array, TRUE);
            return NULL;
        }
    }
    return array;
}

static PyObject*
create_list_consume_double_garray(GArray *array)
{
    PyObject *list;
    const gdouble *a;
    gsize n, i;

    n = array->len;
    if (!(list = PyList_New(n))) {
        g_array_free(array, TRUE);
        /* Should we raise an exception, or who does it? */
        return NULL;
    }
    a = (const gdouble*)array->data;
    for (i = 0; i < n; i++)
        PyList_SetItem(list, i, PyFloat_FromDouble(a[i]));
    g_array_free(array, TRUE);
    return list;
}

static GArray*
create_int_garray_from_sequence(PyObject *object)
{
    GArray *array;
    gint *a;
    gsize n, i;

    if (!PySequence_Check(object))
        return NULL;

    n = PySequence_Size(object);
    array = g_array_new(FALSE, FALSE, sizeof(gint));
    g_array_set_size(array, n);
    a = (gint*)array->data;
    for (i = 0; i < n; i++) {
        PyObject *item = PySequence_GetItem(object, i);
        if (PyInt_Check(item))
            a[i] = PyInt_AsLong(item);
        else if (PyLong_Check(item))
            a[i] = PyLong_AsLong(item);
        else {
            g_array_free(array, TRUE);
            return NULL;
        }
    }
    return array;
}

static PyObject*
create_list_consume_int_garray(GArray *array)
{
    PyObject *list;
    const gint *a;
    gsize n, i;

    n = array->len;
    if (!(list = PyList_New(n))) {
        g_array_free(array, TRUE);
        /* Should we raise an exception, or who does it? */
        return NULL;
    }
    a = (const gint*)array->data;
    for (i = 0; i < n; i++)
        PyList_SetItem(list, i, PyInt_FromLong(a[i]));
    g_array_free(array, TRUE);
    return list;
}

static gchar*
extract_stringlike_as_new_string(PyObject *value)
{
    if (PyString_Check(value))
        return g_strdup(PyString_AsString(value));

    if (PyUnicode_Check(value)) {
        PyObject *utf8value = PyUnicode_AsUTF8String(value);
        gchar *ret;

        if (!utf8value)
            return NULL;
        ret = g_strdup(PyString_AsString(utf8value));
        Py_DECREF(utf8value);
        return ret;
    }

    return NULL;
}

static void
free_string_array(GArray *array)
{
    gsize i, n = array->len;
    gchar *s;

    for (i = 0; i < n; i++) {
        s = g_array_index(array, gchar*, i);
        if (!s)
            break;
        g_free(s);
    }
    g_array_free(array, TRUE);
}

static GArray*
create_string_garray_from_sequence(PyObject *object)
{
    GArray *array;
    gchar **a;
    gsize n, i;

    if (!PySequence_Check(object))
        return NULL;

    n = PySequence_Size(object);
    array = g_array_new(FALSE, TRUE, sizeof(gchar*));
    g_array_set_size(array, n);
    a = (gchar**)array->data;
    for (i = 0; i < n; i++) {
        PyObject *item = PySequence_GetItem(object, i);
        if (!(a[i] = extract_stringlike_as_new_string(item))) {
            free_string_array(array);
            return NULL;
        }
    }
    return array;
}

static PyObject*
create_list_consume_string_garray(GArray *array)
{
    PyObject *list;
    const gchar **a;
    gsize n, i;

    n = array->len;
    if (!(list = PyList_New(n))) {
        free_string_array(array);
        /* Should we raise an exception, or who does it? */
        return NULL;
    }
    a = (const gchar**)array->data;
    for (i = 0; i < n; i++)
        PyList_SetItem(list, i, PyString_FromString(a[i]));
    free_string_array(array);
    return list;
}

static PyObject*
create_list_consume_const_string_garray(GArray *array)
{
    PyObject *list;
    const gchar **a;
    gsize n, i;

    n = array->len;
    if (!(list = PyList_New(n))) {
        g_array_free(array, TRUE);
        /* Should we raise an exception, or who does it? */
        return NULL;
    }
    a = (const gchar**)array->data;
    for (i = 0; i < n; i++)
        PyList_SetItem(list, i, PyString_FromString(a[i]));
    g_array_free(array, TRUE);
    return list;
}

static PyObject*
create_list_consume_data_field_garray(GArray *array)
{
    PyObject *list, *obj;
    GwyDataField **a;
    gsize n, i;

    n = array->len;
    a = (GwyDataField**)array->data;
    if (!(list = PyList_New(n))) {
        for (i = 0; i < n; i++)
            g_object_unref(a[i]);
        g_array_free(array, TRUE);
        /* Should we raise an exception, or who does it? */
        return NULL;
    }
    for (i = 0; i < n; i++) {
        obj = pygobject_new((GObject*)a[i]);
        g_return_val_if_fail(obj, list);
        PyList_SetItem(list, i, obj);
    }
    g_array_free(array, TRUE);
    return list;
}

static PyObject*
create_string_consume_gstring(GString *str)
{
    PyObject *pystr;

    if (G_UNLIKELY(!str)) {
        Py_INCREF(Py_None);
        return Py_None;
    }
    pystr = PyString_FromStringAndSize(str->str, str->len);
    g_string_free(str, TRUE);
    return pystr;
}

static int
assign_number_as_double(PyObject *item, gdouble *value, const gchar *pname)
{
    gchar *error_message;

    if (PyFloat_Check(item)) {
        *value = PyFloat_AsDouble(item);
        return 0;
    }

    /* This can be questionable but allows assigning integers or fractions. */
    if (PyNumber_Check(item)) {
        PyObject *number = PyNumber_Float(item);
        *value = PyFloat_AsDouble(number);
        Py_DECREF(number);
        return 0;
    }

    error_message = g_strconcat(pname, " must be a float", NULL);
    PyErr_SetString(PyExc_TypeError, error_message);
    g_free(error_message);
    return -1;
}

static int
assign_number_as_int(PyObject *item, gint *value, const gchar *pname)
{
    gchar *error_message;

    if (PyInt_Check(item)) {
        *value = PyInt_AsLong(item);
        return 0;
    }

    error_message = g_strconcat(pname, " must be an integer", NULL);
    PyErr_SetString(PyExc_TypeError, error_message);
    g_free(error_message);
    return -1;
}

static void
append_to_list_of_containers(GwyContainer *data, gpointer list)
{
    GObject *gobj = (GObject*)data;
    PyObject *obj;

    obj = (PyObject*)pygobject_new(gobj);
    g_return_if_fail(obj);
    /* NB: Unlike PyList_SetItem(), PyList_Append() does not steal the
     * reference. */
    if (PyList_Append((PyObject*)list, obj) < 0)
        g_warning("Could not append container to python list of containers.");
    Py_DECREF(obj);
}

/*
 * Ignore all get-type() and free() functions.
 *
 * Also include things we define here in ignore-glob because otherwise the
 * show up as not wrapped in the report.
 */
%%
modulename gwy

%%
include
   boxed.override

%%
import gobject.GObject as PyGObject_Type
import gtk.Table as PyGtkTable_Type
import gtk.Widget as PyGtkWidget_Type
import gtk.Dialog as PyGtkDialog_Type
import gtk.Button as PyGtkButton_Type
import gtk.DrawingArea as PyGtkDrawingArea_Type
import gtk.Object as PyGtkObject_Type
import gtk.Window as PyGtkWindow_Type
import gtk.Layout as PyGtkLayout_Type
import gtk.TreeView as PyGtkTreeView_Type
import gtk.VBox as PyGtkVBox_Type
import gtk.Statusbar as PyGtkStatusbar_Type
import gtk.ComboBox as PyGtkComboBox_Type
import gtk.Tooltips as PyGtkTooltips_Type
import gtk.Orientation as PyGtkOrientation_Type
import gtk.PositionType as PyGtkPositionType_Type
import gtk.TreeIter as PyGtkTreeIter_Type
import gtk.ListStore as PyGtkListStore_Type
import gtk.UpdateType as PyGtkUpdateType_Type
import gtk.AccelGroup as PyGtkAccelGroup_Type
import gtk.gdk.Drawable as PyGdkDrawable_Type
import gtk.gdk.Pixbuf as PyGdkPixbuf_Type
import gtk.gdk.GC as PyGdkGC_Type
import gtk.gdk.LineStyle as PyGdkLineStyle_Type

%%
ignore
    Container.set_const_string
    Container.set_const_string_by_name

%%
ignore-glob
    gwy_*_get_type
    gwy_*_free
    gwy_container_gis_*

%%
ignore-type
   GwyPlainTool

%%
define gwy_app_data_browser_get_current kwargs
/**
 * gwy_app_data_browser_get_current:
 * @obj_type: Data item type.  I{(L{AppWhat})}
 *
 * Get current data item of given type.
 *
 * Depending on what you request, the function can return data object
 * themselves, integer keys, ids, ...
 *
 * Returns: The current object or None.
**/
static PyObject *
_wrap_gwy_app_data_browser_get_current(PyObject *self, PyObject *args, PyObject *kwargs)
{
   static char *kwlist[] = { "obj_type", NULL };
   GObject *gobj;
   PyObject *py_obj_type = NULL;
   GwyAppWhat obj_type;
   gint iobj;
   GQuark qobj;

   if (!PyArg_ParseTupleAndKeywords(args, kwargs,
                                    "O:gwy_app_data_browser_get_current",
                                    kwlist, &py_obj_type))
       return NULL;
   if (pyg_enum_get_value(GWY_TYPE_APP_WHAT, py_obj_type, (gpointer)&obj_type))
       return NULL;

   switch (obj_type) {
       case GWY_APP_DATA_FIELD_ID:
       case GWY_APP_GRAPH_MODEL_ID:
       case GWY_APP_SPECTRA_ID:
       case GWY_APP_BRICK_ID:
       case GWY_APP_SURFACE_ID:
       case GWY_APP_CONTAINER_ID:
       case GWY_APP_LAWN_ID:
       case GWY_APP_PAGE:
       // integer
       gwy_app_data_browser_get_current(obj_type, &iobj, NULL);
       return PyInt_FromLong(iobj);

       case GWY_APP_DATA_FIELD_KEY:
       case GWY_APP_MASK_FIELD_KEY:
       case GWY_APP_SHOW_FIELD_KEY:
       case GWY_APP_GRAPH_MODEL_KEY:
       case GWY_APP_SPECTRA_KEY:
       case GWY_APP_BRICK_KEY:
       case GWY_APP_SURFACE_KEY:
       case GWY_APP_LAWN_KEY:
       // quark
       gwy_app_data_browser_get_current(obj_type, &qobj, NULL);
       return PyLong_FromLong(qobj);

       case GWY_APP_CONTAINER:
       case GWY_APP_DATA_FIELD:
       case GWY_APP_MASK_FIELD:
       case GWY_APP_SHOW_FIELD:
       case GWY_APP_GRAPH_MODEL:
       case GWY_APP_SPECTRA:
       case GWY_APP_BRICK:
       case GWY_APP_SURFACE:
       case GWY_APP_LAWN:
       case GWY_APP_DATA_VIEW:
       case GWY_APP_GRAPH:
       case GWY_APP_VOLUME_VIEW:
       case GWY_APP_XYZ_VIEW:
       case GWY_APP_CURVE_MAP_VIEW:
       // gobject
       gwy_app_data_browser_get_current(obj_type, &gobj, NULL);
       return pygobject_new(gobj);
   }

   PyErr_SetString(PyExc_NotImplementedError,
                   "Cannot recognize object type for conversion.");
   return NULL;
}

%%
define gwy_app_data_browser_get_containers noargs
/**
 * gwy_app_data_browser_get_containers:
 *
 * Get list of containers of current data browser.
 *
 * Returns: list of containers L{Container} object
**/
static PyObject *
_wrap_gwy_app_data_browser_get_containers(PyGObject *self)
{
    PyObject *list = PyList_New(0);
    gwy_app_data_browser_foreach(append_to_list_of_containers, (gpointer)list);
    return list;
}

%%
define GwyDataLine.get_data_pointer noargs
/**
 * gwy_data_line_get_data_pointer:
 *
 * Gets pointer to data which the data line contains.
 *
 * Returns: integer pointing to the raw data of the data field
**/
static PyObject *
_wrap_gwy_data_line_get_data_pointer(PyGObject *self)
{
    gdouble* data;
    unsigned long ptr;

    data = gwy_data_line_get_data(GWY_DATA_LINE(self->obj));
    ptr = (unsigned long int)data;

    return PyLong_FromUnsignedLong(ptr);
}

%%
define GwyDataField.get_data_pointer noargs
/**
 * gwy_data_field_get_data_pointer:
 *
 * Gets pointer to data which the data field contains.
 *
 * Returns: integer pointing to the raw data of the data field
**/
static PyObject *
_wrap_gwy_data_field_get_data_pointer(PyGObject *self)
{
    gdouble* data;
    unsigned long ptr;

    data = gwy_data_field_get_data(GWY_DATA_FIELD(self->obj));
    ptr = (unsigned long int)data;

    return PyLong_FromUnsignedLong(ptr);
}

%%
define GwyBrick.get_data_pointer noargs
/**
 * gwy_brick_get_data_pointer:
 *
 * Gets pointer to data which the brick contains.
 *
 * Returns: integer pointing to the raw data of the brick
**/
static PyObject *
_wrap_gwy_brick_get_data_pointer(PyGObject *self)
{
    gdouble* data;
    unsigned long ptr;

    data = gwy_brick_get_data(GWY_BRICK(self->obj));
    ptr = (unsigned long int)data;

    return PyLong_FromUnsignedLong(ptr);
}

%%
override-slot GwyContainer.tp_as_mapping
static Py_ssize_t _map_gwy_container_length(PyGObject *cont)
{
    return gwy_container_get_n_items(GWY_CONTAINER(cont->obj));
}

static PyObject*
_map_gwy_container_subscript(PyGObject *cont, PyObject *key)
{
    GQuark quark;

    if (PyString_Check(key)) {
        const gchar *name = PyString_AsString(key);
        quark = g_quark_try_string(name);
        /* There cannot be anything in the container if the quark does not
         * exist.  Avoid new quark creation in such case. */
        if (!quark) {
            PyErr_SetString(PyExc_KeyError, "Nothing stored here");
            return NULL;
        }
    }
    else if (PyInt_Check(key)) {
        quark = PyInt_AS_LONG(key);
    }
    else {
        PyErr_SetString(PyExc_TypeError, "Expected key to be a string or int.");
        return NULL;
    }

    if (gwy_container_contains((GWY_CONTAINER(cont->obj)), quark)) {
        GValue val = gwy_container_get_value((GWY_CONTAINER(cont->obj)), quark);
        PyObject *ret = pyg_value_as_pyobject(&val, 0);

        g_value_unset(&val);
        return ret;
    }

    PyErr_SetString(PyExc_KeyError, "Nothing stored here");
    return NULL;
}

static int
_map_gwy_container_ass_subscript(PyGObject *cont, PyObject *key, PyObject *v)
{
    GQuark quark;

    if (PyString_Check(key)) {
        const gchar *name = PyString_AsString(key);
        quark = g_quark_from_string(name);
    }
    else if (PyInt_Check(key)) {
        quark = PyInt_AS_LONG(key);
    }
    else {
        PyErr_SetString(PyExc_TypeError, "Expected key to be a string or int.");
        return -1;
    }

    if (v) {
        GValue val;
        GType g_type;
        PyObject* py_type;

        py_type = PyObject_Type(v);
        g_type = pyg_type_from_object(py_type);
        Py_DECREF(py_type);
        if (!g_type)
            return -1;

        gwy_clear(&val, 1);
        g_value_init(&val, G_TYPE_FUNDAMENTAL(g_type));
        pyg_value_from_pyobject(&val, v);
        gwy_container_set_value((GWY_CONTAINER(cont->obj)), quark, &val, NULL);
        g_value_unset(&val);
    }
    else {
        gwy_container_remove((GWY_CONTAINER(cont->obj)), quark);
    }

    return 0;
}

static const PyMappingMethods _wrap_gwy_container_tp_as_mapping = {
    (lenfunc)_map_gwy_container_length,
    (binaryfunc)_map_gwy_container_subscript,
    (objobjargproc)_map_gwy_container_ass_subscript
};

%%
define GwyContainer.get_value_by_name kwargs
static PyObject *
_wrap_gwy_container_get_value_by_name(PyGObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"name", NULL};
    char* name;
    GValue val;
    PyObject* obj;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"s:gwy.Container.get_value_by_name", kwlist, &name))
        return NULL;
    val = gwy_container_get_value_by_name((GWY_CONTAINER(self->obj)), name);
    obj = pyg_value_as_pyobject(&val, 0);

    return obj;
}

%%
define GwyContainer.set_value_by_name kwargs
static PyObject *
_wrap_gwy_container_set_value_by_name(PyGObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"name", "value", NULL};
    char* name;
    GValue val;
    PyObject* v;
    GType g_type;
    PyObject* py_type;

    gwy_clear(&val, 1);
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"sO:gwy.Container.set_value_by_name", kwlist, &name, &v))
        return NULL;

    py_type = PyObject_Type(v);
    g_type = pyg_type_from_object(py_type);
    Py_DECREF(py_type);
    if (!g_type)
        return NULL;

    g_value_init(&val, G_TYPE_FUNDAMENTAL(g_type));
    pyg_value_from_pyobject(&val, v);
    gwy_container_set_value_by_name((GWY_CONTAINER(self->obj)), name, &val, NULL);

    Py_RETURN_NONE;
}

%%
define GwyContainer.get_value kwargs
static PyObject *
_wrap_gwy_container_get_value(PyGObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"key", NULL};
    guint key;  /* In fact, GQuark */
    GValue val;
    PyObject* obj;

    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"I:gwy.Container.get_value", kwlist, &key))
        return NULL;
    val = gwy_container_get_value((GWY_CONTAINER(self->obj)), (GQuark)key);
    obj = pyg_value_as_pyobject(&val, 0);

    return obj;
}

%%
define GwyContainer.set_value_by_name kwargs
static PyObject *
_wrap_gwy_container_set_value_by_name(PyGObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"key", "value", NULL};
    guint key;
    GValue val;
    PyObject* v;
    GType g_type;
    PyObject* py_type;

    gwy_clear(&val, 1);
    if (!PyArg_ParseTupleAndKeywords(args, kwargs,"IO:gwy.Container.get_value", kwlist, &key, &v))
        return NULL;

    py_type = PyObject_Type(v);
    g_type = pyg_type_from_object(py_type);
    Py_DECREF(py_type);
    if (!g_type)
        return NULL;
    g_value_init(&val, G_TYPE_FUNDAMENTAL(g_type));
    pyg_value_from_pyobject(&val,v);
    gwy_container_set_value((GWY_CONTAINER(self->obj)), (GQuark)key, &val, NULL);

    Py_RETURN_NONE;
}

%%
override-slot GwyDataLine.tp_as_sequence

static ssize_t
_sq_gwy_data_line_length(PyGObject *self)
{
    GwyDataLine *dline = GWY_DATA_LINE(self->obj);
    return dline->res;
}

static PyObject*
_sq_gwy_data_line_item(PyGObject *self, Py_ssize_t i)
{
    GwyDataLine *dline = GWY_DATA_LINE(self->obj);
    gint res = gwy_data_line_get_res(dline);

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "DataLine index out of range");
        return NULL;
    }
    return PyFloat_FromDouble(dline->data[i]);
}

static int
_sq_gwy_data_line_ass_item(PyGObject *self, Py_ssize_t i, PyObject *item)
{
    GwyDataLine *dline = GWY_DATA_LINE(self->obj);
    gint res = gwy_data_line_get_res(dline);

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "DataLine index out of range");
        return -1;
    }
    if (assign_number_as_double(item, dline->data + i, "DataLine item") == 0) {
        //gwy_data_line_invalidate(dline);
        return 0;
    }
    return -1;
}

static const PySequenceMethods _wrap_gwy_data_line_tp_as_sequence = {
    (lenfunc)_sq_gwy_data_line_length,
    NULL,
    NULL,
    (ssizeargfunc)_sq_gwy_data_line_item,
    NULL,
    (ssizeobjargproc)_sq_gwy_data_line_ass_item,
    NULL,
    NULL,
    NULL,
    NULL,
};

%%
override-slot GwyDataField.tp_as_sequence

static ssize_t
_sq_gwy_data_field_length(PyGObject *self)
{
    GwyDataField *dfield = GWY_DATA_FIELD(self->obj);
    return dfield->xres * dfield->yres;
}

static PyObject*
_sq_gwy_data_field_item(PyGObject *self, Py_ssize_t i)
{
    GwyDataField *dfield = GWY_DATA_FIELD(self->obj);
    gint res = dfield->xres * dfield->yres;

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "DataField index out of range");
        return NULL;
    }
    return PyFloat_FromDouble(dfield->data[i]);
}

static int
_sq_gwy_data_field_ass_item(PyGObject *self, Py_ssize_t i, PyObject *item)
{
    GwyDataField *dfield = GWY_DATA_FIELD(self->obj);
    gint res = dfield->xres * dfield->yres;

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "DataField index out of range");
        return -1;
    }
    if (assign_number_as_double(item, dfield->data + i, "DataField item") == 0) {
        gwy_data_field_invalidate(dfield);
        return 0;
    }
    return -1;
}

static const PySequenceMethods _wrap_gwy_data_field_tp_as_sequence = {
    (lenfunc)_sq_gwy_data_field_length,
    NULL,
    NULL,
    (ssizeargfunc)_sq_gwy_data_field_item,
    NULL,
    (ssizeobjargproc)_sq_gwy_data_field_ass_item,
    NULL,
    NULL,
    NULL,
    NULL,
};

%%
override-slot GwyBrick.tp_as_sequence

static ssize_t
_sq_gwy_brick_length(PyGObject *self)
{
    GwyBrick *brick = GWY_BRICK(self->obj);
    return brick->xres * brick->yres * brick->zres;
}

static PyObject*
_sq_gwy_brick_item(PyGObject *self, Py_ssize_t i)
{
    GwyBrick *brick = GWY_BRICK(self->obj);
    gint res = brick->xres * brick->yres * brick->zres;

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "Brick index out of range");
        return NULL;
    }
    return PyFloat_FromDouble(brick->data[i]);
}

static int
_sq_gwy_brick_ass_item(PyGObject *self, Py_ssize_t i, PyObject *item)
{
    GwyBrick *brick = GWY_BRICK(self->obj);
    gint res = brick->xres * brick->yres * brick->zres;

    if (i < 0 || i >= res) {
        PyErr_SetString(PyExc_IndexError, "Brick index out of range");
        return -1;
    }
    if (assign_number_as_double(item, brick->data + i, "Brick item") == 0) {
        //gwy_brick_invalidate(brick);
        return 0;
    }
    return -1;
}

static const PySequenceMethods _wrap_gwy_brick_tp_as_sequence = {
    (lenfunc)_sq_gwy_brick_length,
    NULL,
    NULL,
    (ssizeargfunc)_sq_gwy_brick_item,
    NULL,
    (ssizeobjargproc)_sq_gwy_brick_ass_item,
    NULL,
    NULL,
    NULL,
    NULL,
};

%%
override-slot GwySelection.tp_as_sequence

static ssize_t
_sq_gwy_selection_length(PyGObject *self)
{
    GwySelection *selection = GWY_SELECTION(self->obj);
    return gwy_selection_get_data(selection, NULL);
}

static PyObject*
_sq_gwy_selection_item(PyGObject *self, Py_ssize_t i)
{
    GwySelection *selection = GWY_SELECTION(self->obj);
    guint n = gwy_selection_get_data(selection, NULL);
    GwyDoubleArray *array;

    if (i < 0 || i >= n) {
        PyErr_SetString(PyExc_IndexError, "Selection index out of range");
        return NULL;
    }
    array = gwy_selection_get_object_pygwy(selection, i);
    return create_list_consume_double_garray(array);
}

static int
_sq_gwy_selection_ass_item(PyGObject *self, Py_ssize_t i, PyObject *item)
{
    GwySelection *selection = GWY_SELECTION(self->obj);
    guint n = gwy_selection_get_data(selection, NULL);
    guint maxn = gwy_selection_get_max_objects(selection);
    GwyDoubleArray *array;

    if (i < 0 || i >= maxn || i > n) {
        PyErr_SetString(PyExc_IndexError, "Selection index out of range");
        return -1;
    }
    array = create_double_garray_from_sequence(item);
    if (!array || array->len != gwy_selection_get_object_size(selection)) {
        PyErr_SetString(PyExc_ValueError,
                        "Selection object must be a sequence of coordinates");
        if (array)
            g_array_free(array, TRUE);
        return -1;
    }
    gwy_selection_set_object(selection, i, (gdouble*)array->data);
    g_array_free(array, TRUE);
    return 0;
}

static const PySequenceMethods _wrap_gwy_selection_tp_as_sequence = {
    (lenfunc)_sq_gwy_selection_length,
    NULL,
    NULL,
    (ssizeargfunc)_sq_gwy_selection_item,
    NULL,
    (ssizeobjargproc)_sq_gwy_selection_ass_item,
    NULL,
    NULL,
    NULL,
    NULL,
};

%%
override-slot GwyGraphModel.tp_as_sequence

static ssize_t
_sq_gwy_graph_model_length(PyGObject *self)
{
    GwyGraphModel *gmodel = GWY_GRAPH_MODEL(self->obj);
    return gwy_graph_model_get_n_curves(gmodel);
}

static PyObject*
_sq_gwy_graph_model_item(PyGObject *self, Py_ssize_t i)
{
    GwyGraphModel *gmodel = GWY_GRAPH_MODEL(self->obj);
    gint n = gwy_graph_model_get_n_curves(gmodel);

    if (i < 0 || i >= n) {
        PyErr_SetString(PyExc_IndexError, "GraphModel index out of range");
        return NULL;
    }
    return pygobject_new((GObject*)gwy_graph_model_get_curve(gmodel, i));
}

static int
_sq_gwy_graph_model_ass_item(PyGObject *self, Py_ssize_t i, PyObject *item)
{
    GwyGraphModel *gmodel = GWY_GRAPH_MODEL(self->obj);
    PyGObject *py_gcmodel = (PyGObject*)item;
    gint n = gwy_graph_model_get_n_curves(gmodel);

    if (i < 0 || i >= n) {
        PyErr_SetString(PyExc_IndexError, "GraphModel index out of range");
        return -1;
    }

    if (!item
        || !pygobject_check(py_gcmodel, &PyGwyGraphCurveModel_Type)) {
        PyErr_SetString(PyExc_TypeError,
                        "GraphModel item must be a GraphCurveModel");
        return -1;
    }

    gwy_graph_model_replace_curve(gmodel, i,
                                  GWY_GRAPH_CURVE_MODEL(py_gcmodel->obj));
    return 0;
}

static const PySequenceMethods _wrap_gwy_graph_model_tp_as_sequence = {
    (lenfunc)_sq_gwy_graph_model_length,
    NULL,
    NULL,
    (ssizeargfunc)_sq_gwy_graph_model_item,
    NULL,
    (ssizeobjargproc)_sq_gwy_graph_model_ass_item,
    NULL,
    NULL,
    NULL,
    NULL,
};

%%
override-attr GwySIValueFormat.units
static PyObject*
_wrap_gwy_si_value_format__get_units(PyGBoxed *self, void *closure)
{
    GwySIValueFormat *vf = pyg_boxed_get(self, GwySIValueFormat);
    const guchar *s = (const guchar*)vf->units;
    gboolean is_ascii = TRUE;
    guint i;

    /* g_str_is_ascii() requires GLib 2.40+ */
    for (i = 0; s[i]; i++) {
        if (s[i] & 0x80) {
            is_ascii = FALSE;
            break;
        }
    }

    if (is_ascii) {
        return PyString_FromStringAndSize(vf->units_gstring->str,
                                          vf->units_gstring->len);
    }
    return PyUnicode_FromString(vf->units_gstring->str);
}

static int
_wrap_gwy_si_value_format__set_units(PyGBoxed *self, PyObject *value, void *closure)
{
    GwySIValueFormat *vf = pyg_boxed_get(self, GwySIValueFormat);
    gchar *s;

    if ((s = extract_stringlike_as_new_string(value))) {
        gwy_si_value_format_set_units(vf, s);
        g_free(s);
        return 0;
    }
    PyErr_SetString(PyExc_TypeError, "Units must be a string.");
    return -1;
}

/* vim: set ft=c cin et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
