/****************************************************************************
 *
 * MODULE:       r.external
 *
 * AUTHOR(S):    Glynn Clements, based on r.in.gdal
 *               List GDAL layers by Martin Landa <landa.martin gmail.com>
 *               8/2011
 *
 * PURPOSE:      Link raster map into GRASS utilizing the GDAL library.
 *
 * COPYRIGHT:    (C) 2008-2015 by Glynn Clements and the GRASS Development Team
 *
 *               This program is free software under the GNU General Public
 *               License (>=v2). Read the file COPYING that comes with GRASS
 *               for details.
 *
 *****************************************************************************/

#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>

#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/imagery.h>
#include <grass/glocale.h>

#include <gdal.h>
#include <ogr_srs_api.h>
#include <cpl_conv.h>

#include "proto.h"

int main(int argc, char *argv[])
{
    const char *input, *source, *output;
    char *title;
    struct Cell_head cellhd;
    GDALDatasetH hDS;
    GDALRasterBandH hBand;
    struct GModule *module;
    struct {
        struct Option *input, *source, *output, *band, *title;
    } parm;
    struct {
        struct Flag *o, *j, *f, *e, *h, *v, *t, *a, *m, *r;
    } flag;
    int min_band, max_band, band;
    struct band_info info;
    int flip;
    struct Ref reference;

    G_gisinit(argv[0]);

    module = G_define_module();
    G_add_keyword(_("raster"));
    G_add_keyword(_("import"));
    G_add_keyword(_("external"));
    module->description =
        _("Links GDAL supported raster data as a pseudo GRASS raster map.");

    parm.input = G_define_standard_option(G_OPT_F_INPUT);
    parm.input->description = _("Name of raster file to be linked");
    parm.input->required = NO;
    parm.input->guisection = _("Input");

    parm.source = G_define_option();
    parm.source->key = "source";
    parm.source->description = _("Name of non-file GDAL data source");
    parm.source->required = NO;
    parm.source->type = TYPE_STRING;
    parm.source->key_desc = "name";
    parm.source->guisection = _("Input");

    parm.output = G_define_standard_option(G_OPT_R_OUTPUT);

    parm.band = G_define_option();
    parm.band->key = "band";
    parm.band->type = TYPE_INTEGER;
    parm.band->required = NO;
    parm.band->description = _("Band to select (default is all bands)");
    parm.band->guisection = _("Input");

    parm.title = G_define_option();
    parm.title->key = "title";
    parm.title->key_desc = "phrase";
    parm.title->type = TYPE_STRING;
    parm.title->required = NO;
    parm.title->description = _("Title for resultant raster map");
    parm.title->guisection = _("Metadata");

    flag.f = G_define_flag();
    flag.f->key = 'f';
    flag.f->description = _("List supported formats and exit");
    flag.f->guisection = _("Print");
    flag.f->suppress_required = YES;

    flag.o = G_define_flag();
    flag.o->key = 'o';
    flag.o->label = _("Override projection check (use current project's CRS)");
    flag.o->description = _("Assume that the dataset has the same coordinate "
                            "reference system as the current project");

    flag.j = G_define_flag();
    flag.j->key = 'j';
    flag.j->description = _("Perform projection check only and exit");
    flag.j->suppress_required = YES;

    flag.e = G_define_flag();
    flag.e->key = 'e';
    flag.e->label = _("Extend region extents based on new dataset");
    flag.e->description =
        _("Also updates the default region if in the PERMANENT mapset");

    flag.a = G_define_flag();
    flag.a->key = 'a';
    flag.a->label = _("Auto-adjustment for lat/lon");
    flag.a->description =
        _("Attempt to fix small precision errors in resolution and extents");

    flag.h = G_define_flag();
    flag.h->key = 'h';
    flag.h->description = _("Flip horizontally");

    flag.v = G_define_flag();
    flag.v->key = 'v';
    flag.v->description = _("Flip vertically");

    flag.t = G_define_flag();
    flag.t->key = 't';
    flag.t->label =
        _("List available bands including band type in dataset and exit");
    flag.t->description = _("Format: band number,type,projection check");
    flag.t->guisection = _("Print");
    flag.t->suppress_required = YES;

    flag.m = G_define_flag();
    flag.m->key = 'm';
    flag.m->label = _("Read data range from metadata");
    flag.m->description = _(
        "WARNING: metadata are sometimes approximations with wrong data range");

    flag.r = G_define_flag();
    flag.r->key = 'r';
    flag.r->label = _("Create fast link without data range");
    flag.r->description = _(
        "WARNING: some modules do not work correctly without known data range");

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

    GDALAllRegister();

    if (flag.f->answer) {
        list_formats();
        exit(EXIT_SUCCESS);
    }

    input = parm.input->answer;
    source = parm.source->answer;
    output = parm.output->answer;

    flip = 0;
    if (flag.h->answer)
        flip |= FLIP_H;
    if (flag.v->answer)
        flip |= FLIP_V;

    if (parm.title->answer) {
        title = G_store(parm.title->answer);
        G_strip(title);
    }
    else
        title = NULL;

    if (!input && !source)
        G_fatal_error(_("%s= or %s= must be given"), parm.input->key,
                      parm.source->key);

    if (input && source)
        G_fatal_error(_("%s= and %s= are mutually exclusive"), parm.input->key,
                      parm.source->key);

    if (input && !G_is_absolute_path(input)) {
        char path[GPATH_MAX], *cwd;

        cwd = CPLGetCurrentDir();
        if (!cwd)
            G_fatal_error(_("Unable to get current working directory"));

        G_snprintf(path, GPATH_MAX, "%s%c%s", cwd, HOST_DIRSEP, input);
        input = G_store(path);
        CPLFree(cwd);
    }

    if (!input)
        input = source;

    hDS = GDALOpen(input, GA_ReadOnly);
    if (hDS == NULL)
        return 1;

    setup_window(&cellhd, hDS, &flip);

    if (flag.t->answer) {
        list_bands(&cellhd, hDS);
        /* close the GDALDataset to avoid segfault in libgdal */
        GDALClose(hDS);
        exit(EXIT_SUCCESS);
    }

    check_projection(&cellhd, hDS, NULL, 0, flag.o->answer, flag.j->answer);

    if (flag.a->answer && cellhd.proj == PROJECTION_LL) {
        G_adjust_Cell_head(&cellhd, 1, 1);
        G_adjust_window_ll(&cellhd);
    }

    Rast_set_window(&cellhd);

    if (parm.band->answer)
        min_band = max_band = atoi(parm.band->answer);
    else
        min_band = 1, max_band = GDALGetRasterCount(hDS);

    G_verbose_message(_("Proceeding with import..."));

    if (max_band > min_band) {
        if (I_find_group(output) == 1)
            G_warning(
                _("Imagery group <%s> already exists and will be overwritten."),
                output);
        I_init_group_ref(&reference);
    }

    info.have_minmax = !flag.r->answer;
    if (info.have_minmax && flag.m->answer)
        info.have_minmax = 2;
    for (band = min_band; band <= max_band; band++) {
        char *output2, *title2 = NULL;

        G_message(_("Reading band %d of %d..."), band, GDALGetRasterCount(hDS));

        hBand = GDALGetRasterBand(hDS, band);
        if (!hBand)
            G_fatal_error(_("Selected band (%d) does not exist"), band);

        if (max_band > min_band) {
            G_asprintf(&output2, "%s.%d", output, band);
            if (title)
                G_asprintf(&title2, "%s (band %d)", title, band);
            G_debug(1, "Adding raster map <%s> to group <%s>", output2, output);
            I_add_file_to_group_ref(output2, G_mapset(), &reference);
        }
        else {
            output2 = G_store(output);
            if (title)
                title2 = G_store(title);
        }

        query_band(hBand, output2, &cellhd, &info);
        create_map(input, band, output2, &cellhd, &info, title, flip);
        transfer_colormap(hBand, output2);

        G_free(output2);
        G_free(title2);
    }

    /* close the GDALDataset to avoid segfault in libgdal */
    GDALClose(hDS);

    if (flag.e->answer)
        update_default_window(&cellhd);

    /* Create the imagery group if multiple bands are imported */
    if (max_band > min_band) {
        I_put_group_ref(output, &reference);
        I_put_group(output);
        G_message(_("Imagery group <%s> created"), output);
    }

    exit(EXIT_SUCCESS);
}
