/****************************************************************************
 *
 * MODULE:       front end
 * AUTHOR(S):    Charles Ehlschlaeger, CERL (original contributor)
 *               Brad Douglas <rez touchofmadness.com>,
 *               Hamish Bowman <hamish_b yahoo.com>
 *               Markus Metz <markus.metz.giswork gmail.com>
 * PURPOSE:      Hydrological analysis
 * COPYRIGHT:    (C) 1999-2009 by 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 <stdio.h>
#include <string.h>
#include <grass/gis.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include <grass/spawn.h>

static void write_hist(char *, char *, char *, int, int);

static const char *new_argv[22];
static int new_argc;

static void do_opt(const struct Option *opt)
{
    char *buf;

    if (!opt->answer)
        return;
    buf = G_malloc(strlen(opt->key) + 1 + strlen(opt->answer) + 1);
    snprintf(buf, GPATH_MAX, "%s=%s", opt->key, opt->answer);
    new_argv[new_argc++] = buf;
}

int main(int argc, char *argv[])
{
    char command[GPATH_MAX];
    int ret;
    struct Option *opt1;
    struct Option *opt2;
    struct Option *opt3;
    struct Option *opt4;
    struct Option *opt5;
    struct Option *opt6;
    struct Option *opt7;
    struct Option *opt8;
    struct Option *opt9;
    struct Option *opt10;
    struct Option *opt11;
    struct Option *opt12;
    struct Option *opt13;
    struct Option *opt14;
    struct Option *opt15;
    struct Option *opt16;
    struct Option *opt17;
    struct Option *opt18;
    struct Option *opt19;
    struct Flag *flag_sfd;
    struct Flag *flag_flow;
    struct Flag *flag_seg;
    struct Flag *flag_abs;
    struct Flag *flag_flat;
    struct GModule *module;

    G_gisinit(argv[0]);

    /* Set description */
    module = G_define_module();
    G_add_keyword(_("raster"));
    G_add_keyword(_("hydrology"));
    G_add_keyword(_("watershed"));
    G_add_keyword(_("accumulation"));
    G_add_keyword(_("drainage"));
    G_add_keyword(_("stream network"));
    G_add_keyword(_("stream power index"));
    G_add_keyword(_("topographic index"));
    module->description =
        _("Calculates hydrological parameters and RUSLE factors.");

    opt1 = G_define_standard_option(G_OPT_R_ELEV);
    opt1->guisection = _("Inputs");

    opt2 = G_define_standard_option(G_OPT_R_INPUT);
    opt2->key = "depression";
    opt2->label = _("Name of input depressions raster map");
    opt2->description =
        _("All non-NULL and non-zero cells are considered as real depressions");
    opt2->required = NO;
    opt2->guisection = _("Inputs");

    opt3 = G_define_standard_option(G_OPT_R_INPUT);
    opt3->key = "flow";
    opt3->description =
        _("Name of input raster representing amount of overland flow per cell");
    opt3->required = NO;
    opt3->guisection = _("Inputs");

    opt4 = G_define_standard_option(G_OPT_R_INPUT);
    opt4->key = "disturbed_land";
    opt4->label = _("Name of input raster map percent of disturbed land");
    opt4->description = _("For USLE");
    opt4->required = NO;
    opt4->guisection = _("Inputs");

    opt5 = G_define_standard_option(G_OPT_R_INPUT);
    opt5->key = "blocking";
    opt5->label = _("Name of input raster map blocking overland surface flow");
    opt5->description = _("For USLE. All non-NULL and non-zero cells are "
                          "considered as blocking terrain.");
    opt5->required = NO;
    opt5->guisection = _("Inputs");

    opt19 = G_define_standard_option(G_OPT_R_INPUT);
    opt19->key = "retention";
    opt19->description =
        _("Name of input raster map with percentages for flow accumulation.");
    opt19->required = NO;
    opt19->guisection = _("Inputs");

    opt6 = G_define_option();
    opt6->key = "threshold";
    opt6->description = _("Minimum size of exterior watershed basin");
    opt6->required = NO;
    opt6->type = TYPE_INTEGER;
    opt6->guisection = _("Inputs");

    opt7 = G_define_option();
    opt7->key = "max_slope_length";
    opt7->label = _("Maximum length of surface flow in map units");
    opt7->description = _("For USLE");
    opt7->required = NO;
    opt7->type = TYPE_DOUBLE;
    opt7->guisection = _("Inputs");

    opt8 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt8->key = "accumulation";
    opt8->label = _("Name for output accumulation raster map");
    opt8->description = _("Number of cells that drain through each cell");
    opt8->required = NO;
    opt8->guisection = _("Outputs");

    opt17 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt17->key = "tci";
    opt17->description =
        _("Name for output topographic index ln(a / tan(b)) map");
    opt17->required = NO;
    opt17->guisection = _("Outputs");

    opt18 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt18->key = "spi";
    opt18->label = _("Name for output stream power index a * tan(b)");
    opt18->required = NO;
    opt18->guisection = _("Outputs");

    opt9 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt9->key = "drainage";
    opt9->label = _("Name for output drainage direction raster map");
    opt9->description = _("Directions numbered from 1 to 8");
    opt9->required = NO;
    opt9->guisection = _("Outputs");

    opt10 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt10->key = "basin";
    opt10->description = _("Name for output basins raster map");
    opt10->required = NO;
    opt10->guisection = _("Outputs");

    opt11 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt11->key = "stream";
    opt11->description = _("Name for output stream segments raster map");
    opt11->required = NO;
    opt11->guisection = _("Outputs");

    opt12 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt12->key = "half_basin";
    opt12->label = _("Name for output half basins raster map");
    opt12->description = _("Each half-basin is given a unique value");
    opt12->required = NO;
    opt12->guisection = _("Outputs");

    opt13 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt13->key = "length_slope";
    opt13->label = _("Name for output slope length raster map");
    opt13->description = _("Slope length and steepness (LS) factor for USLE");
    opt13->required = NO;
    opt13->guisection = _("Outputs");

    opt14 = G_define_standard_option(G_OPT_R_OUTPUT);
    opt14->key = "slope_steepness";
    opt14->label = _("Name for output slope steepness raster map");
    opt14->description = _("Slope steepness (S) factor for USLE");
    opt14->required = NO;
    opt14->guisection = _("Outputs");

    opt15 = G_define_option();
    opt15->key = "convergence";
    opt15->type = TYPE_INTEGER;
    opt15->required = NO;
    opt15->answer = "5";
    opt15->label = _("Convergence factor for MFD (1-10)");
    opt15->description =
        _("1 = most diverging flow, 10 = most converging flow. Recommended: 5");

    opt16 = G_define_standard_option(G_OPT_MEMORYMB);

    flag_sfd = G_define_flag();
    flag_sfd->key = 's';
    flag_sfd->label = _("SFD (D8) flow (default is MFD)");
    flag_sfd->description =
        _("SFD: single flow direction, MFD: multiple flow direction");

    flag_flow = G_define_flag();
    flag_flow->key = '4';
    flag_flow->description =
        _("Allow only horizontal and vertical flow of water");

    flag_seg = G_define_flag();
    flag_seg->key = 'm';
    flag_seg->label = _("Enable disk swap memory option: Operation is slow");
    flag_seg->description =
        _("Only needed if memory requirements exceed available RAM; see manual "
          "on how to calculate memory requirements");

    flag_abs = G_define_flag();
    flag_abs->key = 'a';
    flag_abs->label =
        _("Use positive flow accumulation even for likely underestimates");
    flag_abs->description =
        _("See manual for a detailed description of flow accumulation output");

    flag_flat = G_define_flag();
    flag_flat->key = 'b';
    flag_flat->label = _("Beautify flat areas");
    flag_flat->description =
        _("Flow direction in flat areas is modified to look prettier");

    /* Some options requires threshold */
    G_option_requires(opt10, opt6, NULL);
    G_option_requires(opt11, opt6, NULL);
    G_option_requires(opt12, opt6, NULL);
    G_option_requires(opt13, opt6, NULL);
    G_option_requires(opt14, opt6, NULL);

    /* Check for some output map */
    G_option_required(opt8, opt17, opt18, opt9, opt10, opt11, opt12, opt13,
                      opt14, NULL);

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

    /* basin threshold */
    if (opt6->answer) {
        if (atoi(opt6->answer) <= 0)
            G_fatal_error(_("The basin threshold must be a positive number."));
    }

    /* Build command line */
    snprintf(command, GPATH_MAX, "%s/etc/r.watershed/%s", G_gisbase(),
             flag_seg->answer ? "seg" : "ram");
    new_argv[new_argc++] = command;

    if (flag_sfd->answer)
        new_argv[new_argc++] = "-s";

    if (flag_flow->answer)
        new_argv[new_argc++] = "-4";

    if (flag_abs->answer)
        new_argv[new_argc++] = "-a";

    if (flag_flat->answer && !flag_seg->answer)
        new_argv[new_argc++] = "-b";

    if (flag_flat->answer && flag_seg->answer)
        G_message(
            _("Beautify flat areas is not yet supported for disk swap mode"));

    do_opt(opt1);
    do_opt(opt2);
    do_opt(opt3);
    do_opt(opt4);
    do_opt(opt5);
    do_opt(opt19);
    do_opt(opt6);
    do_opt(opt7);
    do_opt(opt8);
    do_opt(opt17);
    do_opt(opt18);
    do_opt(opt9);
    do_opt(opt10);
    do_opt(opt11);
    do_opt(opt12);
    do_opt(opt13);
    do_opt(opt14);
    do_opt(opt15);
    if (flag_seg->answer)
        do_opt(opt16);
    new_argv[new_argc++] = NULL;

    G_debug(1, "Mode: %s", flag_seg->answer ? "Segmented" : "All in RAM");
    /*
       if (flag_seg->answer)
       G_message(_("Using memory cache size: %.1f MB"), atof(opt16->answer));
     */
    ret = G_vspawn_ex(new_argv[0], new_argv);

    if (ret != EXIT_SUCCESS)
        G_warning(_("Subprocess failed with exit code %d"), ret);

    /* record map metadata/history info */
    if (opt8->answer)
        write_hist(
            opt8->answer,
            "Watershed accumulation: overland flow that traverses each cell",
            opt1->answer, flag_seg->answer, flag_sfd->answer);
    if (opt17->answer)
        write_hist(opt17->answer,
                   "Watershed accumulation: topographic index ln(a / tan b)",
                   opt1->answer, flag_seg->answer, flag_sfd->answer);
    if (opt18->answer)
        write_hist(opt18->answer,
                   "Watershed accumulation: stream power index a * tan b",
                   opt1->answer, flag_seg->answer, flag_sfd->answer);
    if (opt9->answer)
        write_hist(
            opt9->answer,
            "Watershed drainage direction (CCW from East divided by 45deg)",
            opt1->answer, flag_seg->answer, flag_sfd->answer);
    if (opt10->answer)
        write_hist(opt10->answer, "Watershed basins", opt1->answer,
                   flag_seg->answer, flag_sfd->answer);
    if (opt11->answer)
        write_hist(opt11->answer, "Watershed stream segments", opt1->answer,
                   flag_seg->answer, flag_sfd->answer);
    if (opt12->answer)
        write_hist(opt12->answer, "Watershed half-basins", opt1->answer,
                   flag_seg->answer, flag_sfd->answer);
    if (opt13->answer)
        write_hist(opt13->answer,
                   "Watershed slope length and steepness (LS) factor",
                   opt1->answer, flag_seg->answer, flag_sfd->answer);
    if (opt14->answer)
        write_hist(opt14->answer, "Watershed slope steepness (S) factor",
                   opt1->answer, flag_seg->answer, flag_sfd->answer);

    exit(ret);
}

/* record map history info */
static void write_hist(char *map_name, char *title, char *source_name, int mode,
                       int sfd)
{
    struct History history;

    Rast_put_cell_title(map_name, title);

    Rast_short_history(map_name, "raster", &history);
    Rast_set_history(&history, HIST_DATSRC_1, source_name);
    Rast_append_format_history(&history, "Processing mode: %s",
                               sfd ? "SFD (D8)" : "MFD");
    Rast_append_format_history(&history, "Memory mode: %s",
                               mode ? "Segmented" : "All in RAM");
    Rast_command_history(&history);

    Rast_write_history(map_name, &history);
}
