#include <stdlib.h>
#include <math.h>
#include <grass/raster.h>
#include <grass/glocale.h>
#include "local_proto.h"

/* get stream segment length */
int seg_length(CELL stream_id, CELL *next_stream_id)
{
    int r, c, r_nbr, c_nbr;
    int slength = 1;
    CELL curr_stream;
    int asp_r[9] = {0, -1, -1, -1, 0, 1, 1, 1, 0};
    int asp_c[9] = {0, 1, 0, -1, -1, -1, 0, 1, 1};
    ASP_FLAG af;

    r = stream_node[stream_id].r;
    c = stream_node[stream_id].c;
    if (next_stream_id)
        *next_stream_id = stream_id;

    /* get next downstream point */
    seg_get(&aspflag, (char *)&af, r, c);
    while (af.asp > 0) {
        r_nbr = r + asp_r[(int)af.asp];
        c_nbr = c + asp_c[(int)af.asp];

        /* user-defined depression */
        if (r_nbr == r && c_nbr == c)
            break;
        /* outside region */
        if (r_nbr < 0 || r_nbr >= nrows || c_nbr < 0 || c_nbr >= ncols)
            break;
        /* next stream */
        cseg_get(&stream, &curr_stream, r_nbr, c_nbr);
        if (next_stream_id)
            *next_stream_id = curr_stream;
        if (curr_stream != stream_id)
            break;
        slength++;
        r = r_nbr;
        c = c_nbr;
        seg_get(&aspflag, (char *)&af, r, c);
    }

    return slength;
}

/* change downstream id: update or remove */
int update_stream_id(CELL stream_id, CELL new_stream_id)
{
    int i, r, c, r_nbr, c_nbr;
    CELL new_stream = new_stream_id;
    CELL curr_stream;
    int asp_r[9] = {0, -1, -1, -1, 0, 1, 1, 1, 0};
    int asp_c[9] = {0, 1, 0, -1, -1, -1, 0, 1, 1};
    ASP_FLAG af;

    r = stream_node[stream_id].r;
    c = stream_node[stream_id].c;
    cseg_get(&stream, &curr_stream, r, c);
    if (curr_stream != stream_id)
        G_fatal_error("Update downstream id: curr_stream != stream_id");
    cseg_put(&stream, &new_stream, r, c);
    curr_stream = stream_id;

    /* get next downstream point */
    seg_get(&aspflag, (char *)&af, r, c);
    while (af.asp > 0) {
        r_nbr = r + asp_r[(int)af.asp];
        c_nbr = c + asp_c[(int)af.asp];

        /* user-defined depression */
        if (r_nbr == r && c_nbr == c)
            break;
        /* outside region */
        if (r_nbr < 0 || r_nbr >= nrows || c_nbr < 0 || c_nbr >= ncols)
            break;
        /* next stream */
        cseg_get(&stream, &curr_stream, r_nbr, c_nbr);
        if (curr_stream != stream_id)
            break;
        r = r_nbr;
        c = c_nbr;
        cseg_put(&stream, &new_stream, r, c);
        seg_get(&aspflag, (char *)&af, r, c);
    }

    if (curr_stream <= 0)
        return curr_stream;

    /* update tributaries */
    if (curr_stream != stream_id) {
        int last_i = -1;

        for (i = 0; i < stream_node[curr_stream].n_trib; i++) {
            if (stream_node[curr_stream].trib[i] == stream_id) {
                if (new_stream_id) {
                    stream_node[curr_stream].trib[i] = new_stream_id;
                }
                else {
                    stream_node[curr_stream].n_trib--;
                    stream_node[curr_stream].trib[i] =
                        stream_node[curr_stream]
                            .trib[stream_node[curr_stream].n_trib];
                    stream_node[curr_stream]
                        .trib[stream_node[curr_stream].n_trib] = 0;
                }
                last_i = i;
                break;
            }
        }
        for (i = 0; i < stream_node[curr_stream].n_trib; i++) {
            if (stream_node[curr_stream].trib[i] == stream_id) {
                G_warning("last_i %d, i %d", last_i, i);
                G_warning("failed updating stream %d at node %d", stream_id,
                          curr_stream);
            }
        }
    }

    return curr_stream;
}

/* delete stream segments shorter than threshold */
int del_streams(int min_length)
{
    int i;
    int n_deleted = 0;
    CELL curr_stream, stream_id;
    int other_trib, tmp_trib;
    int slength;

    G_message(n_("Deleting stream segments shorter than %d cell...",
                 "Deleting stream segments shorter than %d cells...",
                 min_length),
              min_length);

    /* TODO: proceed from stream heads to outlets
     *       -> use depth first post order traversal */

    /* go through all nodes */
    for (i = 1; i <= n_stream_nodes; i++) {
        G_percent(i, n_stream_nodes, 2);

        /* not a stream head */
        if (stream_node[i].n_trib > 0)
            continue;

        /* already deleted */
        cseg_get(&stream, &curr_stream, stream_node[i].r, stream_node[i].c);
        if (curr_stream == 0)
            continue;

        /* get length counted as n cells */
        if ((slength = seg_length(i, &curr_stream)) >= min_length)
            continue;

        stream_id = i;

        /* check n sibling tributaries */
        if (curr_stream != stream_id) {
            /* only one sibling tributary */
            if (stream_node[curr_stream].n_trib == 2) {
                if (stream_node[curr_stream].trib[0] != stream_id)
                    other_trib = stream_node[curr_stream].trib[0];
                else
                    other_trib = stream_node[curr_stream].trib[1];

                /* other trib is also stream head */
                if (stream_node[other_trib].n_trib == 0) {
                    /* use shorter one */
                    if (seg_length(other_trib, NULL) < slength) {
                        tmp_trib = stream_id;
                        stream_id = other_trib;
                        other_trib = tmp_trib;
                    }
                }
                update_stream_id(stream_id, 0);
                n_deleted++;

                /* update downstream IDs */
                update_stream_id(curr_stream, other_trib);
            }
            /* more than one other tributary */
            else {
                update_stream_id(stream_id, 0);
                n_deleted++;
            }
        }
        /* stream head is also outlet */
        else {
            update_stream_id(stream_id, 0);
            n_deleted++;
        }
    }

    G_verbose_message(n_("%d stream segment deleted",
                         "%d stream segments deleted", n_deleted),
                      n_deleted);

    return n_deleted;
}
