/*
 * r3.in.v5d - program for data conversion from the V5D format
 * of the VIS5D visualization software to 3D raster GRASS
 * data format.
 *
 * Copyright Jaroslav Hofierka
 * GeoModel,s.r.o., Bratislava, Slovakia 2000
 *
 * Comments or bug reports please send to hofierka@geomodel.sk
 *
 * 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 <grass/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "binio.h"
#include "v5d.h"
#include <grass/gis.h>
#include <grass/raster3d.h>
#include <grass/glocale.h>

/*---------------------------------------------------------------------------*/

void *map = NULL;

/*---------------------------------------------------------------------------*/
static void fatalError(char *errorMsg)
{
    if (map != NULL) {
        /* should unopen map here! */
    }

    Rast3d_fatal_error("%s", errorMsg);
}

/*---------------------------------------------------------------------------*/

typedef struct {
    struct Option *input, *output, *nv;
} paramType;

static paramType param;

static void setParams(void)
{
    param.input = G_define_standard_option(G_OPT_F_INPUT);
    param.input->description = _("Name of V5D raster file to be imported");

    param.output = G_define_standard_option(G_OPT_R3_OUTPUT);

    param.nv = G_define_standard_option(G_OPT_M_NULL_VALUE);
    param.nv->answer = "none";
}

/*---------------------------------------------------------------------------*/

static void getParams(char **input, char **output, int *convertNull,
                      double *nullValue)
{
    *input = param.input->answer;
    *output = param.output->answer;
    *convertNull = (strcmp(param.nv->answer, "none") != 0);
    if (*convertNull)
        if (sscanf(param.nv->answer, "%lf", nullValue) != 1)
            fatalError("getParams: NULL-value value invalid");
}

/*---------------------------------------------------------------------------*/

void convert(char *openFile, RASTER3D_Region *region, int convertNull,
             double nullValue UNUSED)
{
    v5dstruct v5d;
    int time, var;
    int nrncnl, cnt;
    int x, y, z;
    float value;
    float res_r, res_c, res_l;
    float *data1;

    if (!v5dOpenFile(openFile, &v5d)) {
        printf("Error: couldn't open %s for reading\n", openFile);
        exit(0);
    }

    /* Eventually change to write the time and/or vars series of 3d raster
     * maps.. */
    /*   for (time=0; time<v5d.NumTimes; time++) {

       for (var=0; var<v5d.NumVars; var++) {
     */

    for (time = 0; time < 1; time++) {

        for (var = 0; var < 1; var++) {

            nrncnl = v5d.Nr * v5d.Nc * v5d.Nl[var];
            region->rows = v5d.Nr;
            region->cols = v5d.Nc;
            region->depths = v5d.Nl[var];
            region->north = v5d.ProjArgs[0];
            res_r = v5d.ProjArgs[2];
            res_c = v5d.ProjArgs[3];
            res_l = v5d.VertArgs[1];
            region->south = region->north - region->rows * res_r;
            region->west = v5d.ProjArgs[1];
            region->east = region->west + region->cols * res_c;
            region->bottom = v5d.VertArgs[0];
            region->top = region->bottom + region->depths * res_l;

            data1 = (float *)G_malloc(nrncnl * sizeof(float));
            if (!data1)
                G_fatal_error("Not enough memory for data1");

            if (!v5dReadGrid(&v5d, time, var, data1)) {
                printf("Error while reading grid (time=%d,var=%s)\n", time + 1,
                       v5d.VarName[var]);
                exit(0);
            }
            cnt = 0;

            for (z = 0; z < region->depths; z++) {
                for (y = 0; y < region->rows; y++) {
                    for (x = 0; x < region->cols; x++) {
                        value = data1[cnt++];
                        if (convertNull && IS_MISSING(value))
                            Rast3d_set_null_value(&value, 1, FCELL_TYPE);
                        Rast3d_put_float(map, x, y, z, value);
                    }
                }
            }

            G_free(data1);
        }
    }

    v5dCloseFile(&v5d);
}

/*---------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
    char *input, *output;
    int convertNull;
    double nullValue;
    int useTypeDefault, type, useCompressionDefault, doCompression;
    int usePrecisionDefault, precision, useDimensionDefault, tileX, tileY,
        tileZ;
    RASTER3D_Region region;
    struct GModule *module;

    map = NULL;

    G_gisinit(argv[0]);
    module = G_define_module();
    G_add_keyword(_("raster3d"));
    G_add_keyword(_("import"));
    G_add_keyword(_("voxel"));
    module->description = _("Import 3-dimensional Vis5D files.");

    setParams();
    Rast3d_set_standard3d_input_params();

    if (G_parser(argc, argv))
        exit(1);

    getParams(&input, &output, &convertNull, &nullValue);
    if (!Rast3d_get_standard3d_params(
            &useTypeDefault, &type, &useCompressionDefault, &doCompression,
            &usePrecisionDefault, &precision, &useDimensionDefault, &tileX,
            &tileY, &tileZ))
        fatalError("main: error getting standard parameters");

    Rast3d_get_window(&region);
    map = Rast3d_open_cell_new(output, FCELL_TYPE, RASTER3D_USE_CACHE_XY,
                               &region);
    if (map == NULL)
        fatalError(_("Unable to open 3D raster map"));

    convert(input, &region, convertNull, nullValue);

    if (!Rast3d_close(map))
        fatalError(_("Unable to close 3D raster map"));
    map = NULL;

    return 0;
}

/*---------------------------------------------------------------------------*/
