/* PURPOSE:      Develop the image segments */

/* Currently only region growing is implemented */

#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include <time.h>
#include <grass/gis.h>
#include <grass/glocale.h>
#include <grass/raster.h>
#include <grass/segment.h>	/* segmentation library */
#include <grass/rbtree.h>	/* Red Black Tree library functions */
#include "iseg.h"

int create_isegs(struct globals *globals)
{
    int row, col;
    int successflag = 1;
    int have_bound, rid;
    CELL current_bound, bounds_val;

    if (globals->bounds_map == NULL) {
	/* just one time through loop */
	successflag = globals->method_fn(globals);
    }
    else {
	/* outer processing loop for polygon constraints */
	for (current_bound = globals->lower_bound;
	     current_bound <= globals->upper_bound; current_bound++) {

	    G_debug(1, "current_bound = %d", current_bound);

	    have_bound = 0;

	    /* get min/max row/col to narrow the processing window */
	    globals->row_min = globals->nrows;
	    globals->row_max = 0;
	    globals->col_min = globals->ncols;
	    globals->col_max = 0;
	    for (row = 0; row < globals->nrows; row++) {
		for (col = 0; col < globals->ncols; col++) {
		    FLAG_SET(globals->null_flag, row, col);
		    Segment_get(&globals->bounds_seg, &bounds_val,
				row, col);

		    if (!Rast_is_c_null_value(&bounds_val)
		        && bounds_val == current_bound) {

			Segment_get(&globals->rid_seg, &rid, row, col);
			if (!Rast_is_c_null_value(&rid)) {
			    have_bound = 1;

			    FLAG_UNSET(globals->null_flag, row, col);

			    if (globals->row_min > row)
				globals->row_min = row;
			    if (globals->row_max < row)
				globals->row_max = row;
			    if (globals->col_min > col)
				globals->col_min = col;
			    if (globals->col_max < col)
				globals->col_max = col;
			}
		    }
		}
	    }
	    globals->row_max++;
	    globals->col_max++;

	    if (have_bound)
		successflag = globals->method_fn(globals);
	}    /* end outer loop for processing polygons */

	/* restore NULL flag */
	flag_clear_all(globals->null_flag);
	for (row = 0; row < globals->nrows; row++) {
	    for (col = 0; col < globals->ncols; col++) {
		Segment_get(&globals->rid_seg, &rid, row, col);
		if (Rast_is_c_null_value(&rid))
		    FLAG_SET(globals->null_flag, row, col);
	    }
	}
    }

    if (globals->method == ORM_RG) {
	/* renumber */
	int i, *new_id, max_id;

	G_debug(1, "Largest assigned ID: %d", globals->max_rid);

	new_id = G_malloc((globals->max_rid + 1) * sizeof(int));
	
	for (i = 0; i <= globals->max_rid; i++)
	    new_id[i] = 0;

	for (row = 0; row < globals->nrows; row++) {
	    for (col = 0; col < globals->ncols; col++) {
		Segment_get(&globals->rid_seg, &rid, row, col);
		if (!Rast_is_c_null_value(&rid))
		    new_id[rid]++;
	    }
	}

	max_id = 0;
	for (i = 0; i <= globals->max_rid; i++) {
	    if (new_id[i] > 0) {
		max_id++;
		new_id[i] = max_id;
	    }
	}
	globals->max_rid = max_id;
	G_debug(1, "Largest renumbered ID: %d", globals->max_rid);
	
	for (row = 0; row < globals->nrows; row++) {
	    for (col = 0; col < globals->ncols; col++) {
		Segment_get(&globals->rid_seg, &rid, row, col);
		if (!Rast_is_c_null_value(&rid)) {
		    rid = new_id[rid];
		    Segment_put(&globals->rid_seg, &rid, row, col);
		}
	    }
	}

	G_free(new_id);
    }

    return successflag;
}

void find_four_neighbors(int p_row, int p_col,
			        int neighbors[8][2])
{
    /* north */
    neighbors[0][0] = p_row - 1;
    neighbors[0][1] = p_col;

    /* east */
    neighbors[1][0] = p_row;
    neighbors[1][1] = p_col + 1;

    /* south */
    neighbors[2][0] = p_row + 1;
    neighbors[2][1] = p_col;

    /* west */
    neighbors[3][0] = p_row;
    neighbors[3][1] = p_col - 1;

    return;
}

void find_eight_neighbors(int p_row, int p_col,
			         int neighbors[8][2])
{
    /* get the 4 orthogonal neighbors */
    find_four_neighbors(p_row, p_col, neighbors);

    /* get the 4 diagonal neighbors */
    /* north-west */
    neighbors[4][0] = p_row - 1;
    neighbors[4][1] = p_col - 1;

    /* north-east */
    neighbors[5][0] = p_row - 1;
    neighbors[5][1] = p_col + 1;

    /* south-west */
    neighbors[6][0] = p_row + 1;
    neighbors[6][1] = p_col - 1;

    /* south-east */
    neighbors[7][0] = p_row + 1;
    neighbors[7][1] = p_col + 1;

    return;
}

/* similarity / distance between two points based on their input raster values */
/* assumes first point values already saved in files->bands_seg - only run Segment_get once for that value... */
/* TODO: Segment_get already happened for a[] values in the main function.  Could remove a[] from these parameters */
double calculate_euclidean_similarity(struct ngbr_stats *Ri,
                                      struct ngbr_stats *Rk,
				      struct globals *globals)
{
    double val = 0., diff;
    int n = globals->nbands - 1;

    /* squared euclidean distance, sum the square differences for each dimension */
    do {
	diff = Ri->mean[n] - Rk->mean[n];
	    
	val += diff * diff;
    } while (n--);

    /* the return value should always be in the range 0 - 1 */
    if (val <= 0)
	return 0.;

    val /= globals->max_diff;

#ifdef _OR_SHAPE_
    if (globals->shape_weight < 1)
	val = val * globals->shape_weight + (1 - globals->shape_weight) *
	      calculate_shape(rsi, rsk, nshared, globals);
#endif

    return val;
}

double calculate_manhattan_similarity(struct ngbr_stats *Ri,
                                      struct ngbr_stats *Rk,
				      struct globals *globals)
{
    double val = 0.;
    int n = globals->nbands - 1;

    /* squared manhattan distance, sum the differences for each dimension */
    do {
	val += fabs(Ri->mean[n] - Rk->mean[n]);
    } while (n--);

    /* the return value should always be in the range 0 - 1 */
    if (val <= 0)
	return 0.;

    val /= globals->max_diff;

#ifdef _OR_SHAPE_
    if (globals->shape_weight < 1)
	val = val * globals->shape_weight + (1 - globals->shape_weight) *
	      calculate_shape(rsi, rsk, nshared, globals);
#endif

    return val;
}
