/*
 **  Original Algorithm:    H. Mitasova, L. Mitas, J. Hofierka, M. Zlocha
 **  GRASS Implementation:  J. Caplan, M. Ruesink  1995
 **
 **  US Army Construction Engineering Research Lab, University of Illinois
 **
 **  Copyright  J. Caplan, H. Mitasova, L. Mitas, J. Hofierka,
 **      M. Zlocha
 **
 ** 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include "r.flow.h"
#include "mem.h"

#define OLD  0 /* magic        */
#define NEW  1 /*              */
#define TEMP 2 /*      numbers */

/****************************** Annoyances ******************************/

static const char *tmp_name(const char *fullname)
{
    char element[1024];
    const char *mapset = G_mapset();
    const char *location = G_location_path();
    const char *el = element;

    G_temp_element(element);
    while (*fullname++ == *location++)
        ;
    while (*fullname++ == *mapset++)
        ;
    while (*fullname++ == *el++)
        ;

    return fullname;
}

/********************************* I/O **********************************/

static int open_existing_cell_file(char *fname, struct Cell_head *chd)
{
    const char *mapset = G_find_raster(fname, "");

    if (mapset == NULL)
        G_fatal_error(_("Raster map <%s> not found"), fname);

    if (chd)
        Rast_get_cellhd(fname, mapset, chd);

    return Rast_open_old(fname, mapset);
}

static int compare_regions(const struct Cell_head *a, const struct Cell_head *b)
{
    return (fabs(a->ew_res - b->ew_res) < 1e-6 * b->ew_res &&
            fabs(a->ns_res - b->ns_res) < 1e-6 * b->ns_res);
}

void read_input_files(void)
{
    DCELL *barc;
    int fd, row, col;
    struct Cell_head hd;

    fd = open_existing_cell_file(parm.elevin, &hd);
    if (!compare_regions(&region, &hd))
        G_fatal_error(_("Elevation raster map resolution differs from current "
                        "region resolution"));

    G_important_message(_("Reading input raster map <%s>..."), parm.elevin);
    for (row = 0; row < region.rows; row++) {
        G_percent(row, region.rows, 5);
        Rast_get_d_row(fd, el.buf[row], row);
        if (parm.seg)
            put_row_seg(el, row);
    }
    G_percent(1, 1, 1);
    if (parm.seg)
        Segment_flush(el.seg);
    Rast_close(fd);

    if (parm.aspin) {
        fd = open_existing_cell_file(parm.aspin, &hd);
        if (!compare_regions(&region, &hd))
            G_fatal_error(_("Resolution of aspect file differs from "
                            "current region resolution"));

        G_important_message(_("Reading input raster map <%s>..."), parm.aspin);
        for (row = 0; row < region.rows; row++) {
            G_percent(row, region.rows, 5);
            Rast_get_d_row(fd, as.buf[row], row);
            if (parm.seg)
                put_row_seg(as, row);
        }
        G_percent(1, 1, 1);
        if (parm.seg)
            Segment_flush(as.seg);
        Rast_close(fd);
    }

    if (parm.barin) {
        G_message(_("Reading input files: barrier"));
        barc = Rast_allocate_d_buf();
        fd = open_existing_cell_file(parm.barin, &hd);

        for (row = 0; row < region.rows; row++) {
            Rast_get_d_row(fd, barc, row);
            for (col = 0; col < region.cols; col++) {
                BM_set(bitbar, col, row, (barc[col] != 0));
                if (parm.dsout && barc[col] != 0)
                    put(ds, row, col, -1);
            }
        }
        Rast_close(fd);
    }
}

static int open_segment_file(const char *name, layer l, int new)
{
    int fd;
    const char *mapset;

    if (new == TEMP)
        G_temp_element(string);
    else
        sprintf(string, "cell_misc/%s", parm.elevin);

    if (new || !(mapset = G_find_file2(string, name, ""))) {
        if ((fd = G_open_new(string, name)) < 0)
            G_fatal_error(_("Cannot create segment file %s"), name);

        if (Segment_format(fd, region.rows + l.row_offset * 2,
                           region.cols + l.col_offset * 2, SEGROWS, SEGCOLS,
                           sizeof(DCELL)) < 1)
            G_fatal_error(_("Cannot format segment file %s"), name);
        close(fd);
        mapset = G_mapset();
    }

    if ((fd = G_open_update(string, name)) < 0)
        G_fatal_error(_("Cannot open segment file %s"), name);

    return fd;
}

void open_output_files(void)
{
    if (parm.seg) {
        el.sfd = open_segment_file("elevation.seg", el, OLD);
        as.sfd = open_segment_file("aspect.seg", as, OLD);
        if (parm.dsout)
            ds.sfd = open_segment_file(tmp_name(G_tempfile()), ds, TEMP);
    }

    if (parm.lgout)
        lgfd = Rast_open_new(parm.lgout, FCELL_TYPE);

    if (parm.flout && (Vect_open_new(&fl, parm.flout, 0) < 0))
        G_fatal_error(_("Unable to create vector map <%s>"), parm.flout);
}

void close_files(void)
{
    if (parm.seg) {
        close(el.sfd);
        close(as.sfd);
        if (parm.dsout)
            close(ds.sfd);
    }
    /*   if (parm.lgout)
       Rast_close(lgfd); */
    if (parm.flout) {
        Vect_build(&fl);
        Vect_close(&fl);
    }
}

void write_density_file(void)
{
    const char *mapset;
    int dsfd, row, col;
    double dsmax = 0.0;
    struct Colors colors;
    CELL val1, val2;

    Rast_set_output_window(&region);

    G_message(_("Writing output raster map <%s>..."), parm.dsout);
    dsfd = Rast_open_new(parm.dsout, DCELL_TYPE);

    for (row = 0; row < region.rows; row++) {
        G_percent(row, region.rows, 5);
        Rast_put_row(dsfd, get_row(ds, row), DCELL_TYPE);
        for (col = 0; col < region.cols; col++)
            if (ds.buf[row][col] > dsmax)
                dsmax = ds.buf[row][col];
    }
    G_percent(1, 1, 1);
    Rast_close(dsfd);

    Rast_init_colors(&colors);

    val1 = -1;
    val2 = -1;
    Rast_add_c_color_rule(&val1, 0, 0, 0, &val2, 0, 0, 0, &colors);
    val1 = 0;
    val2 = 5;
    Rast_add_c_color_rule(&val1, 255, 255, 255, &val2, 255, 255, 0, &colors);
    val1 = 5;
    val2 = 30;
    Rast_add_c_color_rule(&val1, 255, 255, 0, &val2, 0, 255, 255, &colors);
    val1 = 30;
    val2 = 100;
    Rast_add_c_color_rule(&val1, 0, 255, 255, &val2, 0, 127, 255, &colors);
    val1 = 100;
    val2 = 1000;
    Rast_add_c_color_rule(&val1, 0, 127, 255, &val2, 0, 0, 255, &colors);
    val1 = 1000;
    val2 = (CELL)dsmax;
    Rast_add_c_color_rule(&val1, 0, 0, 255, &val2, 0, 0, 0, &colors);

    if ((mapset = G_find_file("cell", parm.dsout, "")) == NULL)
        G_fatal_error(_("Raster map <%s> not found"), parm.dsout);

    Rast_write_colors(parm.dsout, mapset, &colors);
    Rast_free_colors(&colors);
}
