/*
 *  $Id: codevfile.c 29611 2026-03-17 10:31:06Z yeti-dn $
 *  Copyright (C) 2011-2025 David Necas (Yeti).
 *
 *  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.
 */

/**
 * [FILE-MAGIC-FREEDESKTOP]
 * <mime-type type="application/x-code-v-int">
 *   <comment>Code V interferogram data</comment>
 *   <glob pattern="*.int"/>
 *   <glob pattern="*.INT"/>
 * </mime-type>
 **/

/**
 * [FILE-MAGIC-USERGUIDE]
 * Code V interferogram data
 * .int
 * Read
 **/

#include "config.h"
#include <glib/gi18n-lib.h>
#include <string.h>
#include <stdlib.h>
#include <gwy.h>

#include "err.h"

#define EXTENSION ".int"

typedef enum {
    CODEV_INT_SURFACE_DEFORMATION = 1,
    CODEV_INT_WAVEFRONT_DEFORMATION,
    CODEV_INT_INTENSITY_FILTER,
    CODEV_INT_COATING_THICKNESS_VARIATION,
    CODEV_INT_BIREFRINGENCE,
    CODEV_INT_CRYSTAL_AXIS_ORIENTATION,
} CodeVGridDataType;

static gboolean module_register(void);
static gint     detect_file    (const GwyFileDetectInfo *fileinfo,
                                gboolean only_name);
static GwyFile* load_file      (const gchar *filename,
                                GwyRunModeFlags mode,
                                GError **error);

static GwyModuleInfo module_info = {
    GWY_MODULE_ABI_VERSION,
    &module_register,
    N_("Imports Code V interferogram files."),
    "Yeti <yeti@gwyddion.net>",
    "0.5",
    "David Nečas (Yeti)",
    "2011",
};

GWY_MODULE_QUERY2(module_info, codevfile)

static gboolean
module_register(void)
{
    gwy_file_func_register("codevfile",
                           N_("Code V interferogram files (.int)"),
                           detect_file, load_file, NULL, NULL);

    return TRUE;
}

static gint
detect_file(const GwyFileDetectInfo *fileinfo, gboolean only_name)
{
    const gchar *p = fileinfo->head;
    gboolean expect_magic = FALSE;

    if (only_name)
        return g_str_has_suffix(fileinfo->name_lowercase, EXTENSION) ? 10 : 0;

    do {
        if (expect_magic) {
            guint x, y;
            return 100*(sscanf(p, "GRD %u %u ", &x, &y) == 2);
        }
        /* Not a comment means the title and the GRD-line should follow then. */
        if (*p != '!')
            expect_magic = TRUE;
    } while ((p = strstr(p, "\r\n")) && (p += 2));

    return 0;
}

static gchar**
split_line_in_place(gchar *line)
{
    gchar **strs;
    guint i, n = 0;

    for (i = 0; line[i]; i++) {
        if ((!i || g_ascii_isspace(line[i-1]))
            && (line[i] && !g_ascii_isspace(line[i])))
            n++;
    }

    strs = g_new(gchar*, n+1);
    n = 0;
    for (i = 0; line[i]; i++) {
        if ((!i || g_ascii_isspace(line[i-1]) || !line[i-1]) && (line[i] && !g_ascii_isspace(line[i])))
            strs[n++] = line + i;
        else if (i && g_ascii_isspace(line[i]) && !g_ascii_isspace(line[i-1]))
            line[i] = '\0';
    }
    strs[n] = NULL;

#ifdef DEBUG
    for (i = 0; strs[i]; i++)
        gwy_debug("%u: <%s>", i, strs[i]);
#endif

    return strs;
}

static GwyFile*
load_file(const gchar *filename,
          G_GNUC_UNUSED GwyRunModeFlags mode,
          GError **error)
{
    GwyFile *file = NULL;
    GwyDict *meta;
    GwyField *field = NULL;
    CodeVGridDataType type;
    gchar *line, *p, *comment, *end, *buffer = NULL;
    const gchar *unit, *title;
    gchar **fields = NULL;
    gsize size;
    GError *err = NULL;
    gdouble xreal, yreal;
    gint i, xres, yres, no_data_value = 32767;
    guint fi;
    gdouble scale_size, wavelength, q = 1.0, x_scale = 1.0;
    gboolean nearest_neighbour = FALSE;

    if (!g_file_get_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        goto fail;
    }

    /* Skip comments. */
    p = buffer;
    for (line = gwy_str_next_line(&p); line && line[0] == '!'; line = gwy_str_next_line(&p)) {
        gwy_debug("comment <%s>", line);
    }
    if (!line) {
        err_FILE_TYPE(error, "Code V INT");
        goto fail;
    }

    /* The title. */
    comment = line;
    if (!(line = gwy_str_next_line(&p))) {
        err_FILE_TYPE(error, "Code V INT");
        goto fail;
    }
    gwy_debug("comment <%s>", comment);

    fields = split_line_in_place(line);
    if (!fields
        || g_strv_length(fields) < 8
        || !gwy_strequal(fields[0], "GRD")
        || !(xres = atoi(fields[1]))
        || !(yres = atoi(fields[2]))
        || !(type = gwy_stramong(fields[3], "SUR", "WFR", "FIL", "THV", "BIR", "CAO", NULL))
        || !gwy_strequal(fields[4], "WVL")
        || (!(wavelength = g_ascii_strtod(fields[5], &end)) && end == fields[5])) {
        err_FILE_TYPE(error, "Code V INT");
        goto fail;
    }
    gwy_debug("type %u (%s)", type, fields[3]);
    gwy_debug("xres %d, yres %d", xres, yres);
    gwy_debug("wavelength %g", wavelength);
    fi = 6;
    if (gwy_strequal(fields[fi], "NNB")) {
        nearest_neighbour = TRUE;
        fi++;
    }
    gwy_debug("nearest_neighbour %d", nearest_neighbour);

    if (!fields[fi] || !gwy_strequal(fields[fi], "SSZ")) {
        err_FILE_TYPE(error, "Code V INT");
        goto fail;
    }
    fi++;
    if (!(scale_size = g_ascii_strtod(fields[fi], &end)) && end == fields[fi]) {
        err_FILE_TYPE(error, "Code V INT");
        goto fail;
    }
    gwy_debug("scale_size %g", scale_size);
    sanitise_real_size(&scale_size, "SSZ");
    fi++;

    if (fields[fi] && gwy_strequal(fields[fi], "NDA")) {
        fi++;
        if (!fields[fi]) {
            err_FILE_TYPE(error, "Code V INT");
            goto fail;
        }
        no_data_value = atoi(fields[fi]);
        fi++;
    }
    gwy_debug("no_data_value %d", no_data_value);

    if (fields[fi] && gwy_strequal(fields[fi], "XSC")) {
        fi++;
        if (!fields[fi]) {
            err_FILE_TYPE(error, "Code V INT");
            goto fail;
        }
        if (!(x_scale = g_ascii_strtod(fields[fi], &end))
            && end == fields[fi]) {
            err_FILE_TYPE(error, "Code V INT");
            goto fail;
        }
        fi++;
    }
    gwy_debug("x_scale %g", x_scale);
    sanitise_real_size(&x_scale, "XSC");

    /* There may be more stuff but we do not know anything about it. */

    if (err_DIMENSION(error, xres) || err_DIMENSION(error, yres))
        goto fail;

    yreal = 1.0;
    xreal = x_scale*yreal;
    field = gwy_field_new(xres, yres, xreal, yreal, TRUE);

    if (type == CODEV_INT_SURFACE_DEFORMATION) {
        q = 1e-6*wavelength/scale_size;
        unit = "m";
        title = "Surface";
    }
    else if (type == CODEV_INT_WAVEFRONT_DEFORMATION) {
        q = 1e-6*wavelength/scale_size;
        unit = "m";
        title = "Wavefront";
    }
    else {
        g_warning("Don't know how to convert this grid data type to physical units.");
        title = fields[3];
    }

    gwy_unit_set_from_string(gwy_field_get_unit_z(field), unit);

    GwyNield *mask = gwy_field_new_nield_alike(field);
    gdouble *data = gwy_field_get_data(field);
    gint *mdata = gwy_nield_get_data(mask);

    for (i = 0; i < xres*yres; i++, p = end) {
        gint value = strtol(p, &end, 10);

        if (value != no_data_value && (type != CODEV_INT_INTENSITY_FILTER || value >= 0))
            data[i] = q*value;
        else
            mdata[i] = 1;
    }
    mask = gwy_remove_bad_image_data(field, mask);

    file = gwy_file_new_in_construction();

    gwy_file_pass_image(file, 0, field);
    gwy_file_set_title(file, GWY_FILE_IMAGE, 0, title, FALSE);
    gwy_check_nonsquare_image(file, 0);
    if (mask)
        gwy_file_pass_image_mask(file, 0, mask);

    meta = gwy_dict_new_in_construction();
    gwy_dict_set_string_by_name(meta, "Comment", comment);
    gwy_dict_set_string_by_name(meta, "Interpolation", nearest_neighbour ? "NNB" : "Linear");
    gwy_dict_pass_string_by_name(meta, "Wavelength", g_strdup_printf("%g μm", wavelength));
    gwy_file_pass_meta(file, GWY_FILE_IMAGE, 0, meta);

    gwy_log_add_import(file, GWY_FILE_IMAGE, 0, NULL, filename);

fail:
    g_free(fields);
    g_free(buffer);

    return file;
}

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