/*
 *  $Id: burleigh_bii.c 29571 2026-03-05 15:49:32Z yeti-dn $
 *  Copyright (C) 2008-2025 David Necas (Yeti), Petr Klapetek, Jan Horak.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net, xhorak@gmail.com.
 *
 *  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.
 */

/* XXX: Unfortunately, BM6 is Windows 3.x bitmap and we cannot claim ownership of that. */
/**
 * [FILE-MAGIC-FREEDESKTOP]
 * <mime-type type="application/x-burleigh-bii-spm">
 *   <comment>Burleigh BII SPM data</comment>
 *   <magic priority="30">
 *     <match type="string" offset="0" value="BM6"/>
 *   </magic>
 *   <glob pattern="*.bii"/>
 *   <glob pattern="*.BII"/>
 * </mime-type>
 **/

/**
 * [FILE-MAGIC-USERGUIDE]
 * Burleigh BII
 * .bii
 * Read
 **/

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

#include "err.h"

#define EXTENSION "bii"
#define MAGIC "BM6"
#define MAGIC_SIZE (sizeof(MAGIC)-1)
#define SIGNATURE "Burleigh Instruments"

#define Nanometre 1e-9

enum {
    FILE_IMG_SIZE_POS = 0x12,
    FILE_IMG_DATA_START_POS = 0x36,
    FILE_IMG_XY_SCALE_POS = 0x74,
    FILE_IMG_Z_SCALE_POS = 0xCC,
};

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 Burleigh BII binary data files."),
    "Jan Hořák <xhorak@gmail.com>, Yeti <yeti@gwyddion.net>",
    "0.4",
    "Jan Hořák & David Nečas (Yeti) & Petr Klapetek",
    "2008",
};

GWY_MODULE_QUERY2(module_info, burleigh_bii)

static gboolean
module_register(void)
{
    gwy_file_func_register("burleigh_bii",
                           N_("Burleigh Image Studio files (.bii)"),
                           detect_file, load_file, NULL, NULL);

    return TRUE;
}

static gint
detect_file(const GwyFileDetectInfo *fileinfo, gboolean only_name)
{
    if (only_name)
        return g_str_has_suffix(fileinfo->name_lowercase, EXTENSION) ? 10 : 0;

    if (fileinfo->file_size < FILE_IMG_DATA_START_POS
        || fileinfo->buffer_len < MAGIC_SIZE
        || strncmp(fileinfo->head, MAGIC, MAGIC_SIZE) != 0)
        return 0;

    /* We are fairly sure if Burleigh claims the file is theirs. */
    if (gwy_memmem(fileinfo->tail, fileinfo->buffer_len, SIGNATURE, sizeof(SIGNATURE)-1))
        return 95;

    /* It might be.  Note 30 is lower than the generic pixmap loader score. */
    return 30;
}

static GwyFile*
load_file(const gchar *filename,
          G_GNUC_UNUSED GwyRunModeFlags mode,
          GError **error)
{
    const guchar *p, *buffer = NULL;
    gsize size = 0;
    GError *err = NULL;
    gint width, height, data_end_offset;
    gdouble x_scale, y_scale, z_scale;
    GwyField *dfield;
    GwyFile *file;

    if (!gwy_map_file_contents(filename, &buffer, &size, &err)) {
        err_GET_FILE_CONTENTS(error, &err);
        return NULL;
    }
    if (size < FILE_IMG_DATA_START_POS) {
        err_TOO_SHORT(error);
        gwy_unmap_file_contents(buffer);
        return NULL;
    }
    /* get field dimensions from file */
    p = buffer + FILE_IMG_SIZE_POS;
    width = gwy_get_gint32_le(&p);
    height = gwy_get_gint32_le(&p);
    // get file offset where field ends
    data_end_offset = width * height * 2 + FILE_IMG_DATA_START_POS;
    // get scaling factors of axes
    if (err_SIZE_MISMATCH(error, data_end_offset + FILE_IMG_Z_SCALE_POS + 8, size, FALSE)) {
        gwy_unmap_file_contents(buffer);
        return NULL;
    }
    p = buffer + data_end_offset + FILE_IMG_XY_SCALE_POS;
    x_scale = gwy_get_gdouble_le(&p);
    sanitise_real_size(&x_scale, "x size");
    y_scale = gwy_get_gdouble_le(&p);
    sanitise_real_size(&y_scale, "y size");
    p = buffer + data_end_offset + FILE_IMG_Z_SCALE_POS;
    z_scale = gwy_get_gdouble_le(&p);

    //FIXME: all axes always in nm?
    gwy_debug("dfield: w=%d h=%d, scale: x=%g y=%g z=%g",
              width, height, x_scale, y_scale, z_scale);
    // create new field
    dfield = gwy_field_new(width, height, x_scale*Nanometre, y_scale*Nanometre, FALSE);
    gwy_unit_set_from_string(gwy_field_get_unit_xy(dfield), "m");
    gwy_unit_set_from_string(gwy_field_get_unit_z(dfield), "m");

    // set pointer to 16bit signed integers to start position of data definition in file and fill field
    gwy_convert_raw_data(buffer + FILE_IMG_DATA_START_POS, width*height, 1,
                         GWY_RAW_DATA_SINT16, GWY_BYTE_ORDER_LITTLE_ENDIAN,
                         gwy_field_get_data(dfield), z_scale*Nanometre, 0.0);
    gwy_field_flip(dfield, FALSE, TRUE);
    gwy_unmap_file_contents(buffer);

    // fill file
    file = gwy_file_new_in_construction();
    gwy_file_pass_image(file, 0, dfield);
    gwy_image_title_fall_back(file, 0);
    gwy_log_add_import(file, GWY_FILE_IMAGE, 0, NULL, filename);

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