/*
 *  Update a history file.  Some of the digit file information  is placed in
 *  the hist file.
 *  returns  0  -  successful creation of history file
 *          -1  -  error
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <grass/gis.h>
#include <grass/colors.h>
#include <grass/raster.h>
#include <grass/dbmi.h>
#include <grass/vector.h>
#include <grass/glocale.h>
#include "local.h"

struct My_labels_rule
{
    dbString label;
    double d;
    int i;
};


int update_hist(const char *raster_name, const char *vector_name, long scale)
{
    struct History hist;

    if (raster_name == NULL)
	return (-1);

    Rast_short_history(raster_name, "raster", &hist);

    /* store information from digit file into history */
    Rast_format_history(&hist, HIST_DATSRC_1,
			"Vector Map: %s", vector_name);
    Rast_format_history(&hist, HIST_DATSRC_2,
			"Original scale from vector map: 1:%ld", scale);	/* 4.0 */

    /* store command line options */
    Rast_command_history(&hist);

    Rast_write_history(raster_name, &hist);

    return 0;
}

int update_cats(const char *raster_name)
{
    /* TODO: maybe attribute transfer from vector map? 
       Use Rast_set_cat() somewhere */

    struct Categories cats;

    Rast_init_cats(raster_name, &cats);
    Rast_write_cats(raster_name, &cats);

    return 0;
}

int update_dbcolors(const char *rast_name, const char *vector_map, int field,
		    const char *rgb_column, int is_fp, const char *attr_column)
{
    int i;

    /* Map */
    struct Map_info Map;

    /* Attributes */
    int nrec;
    struct field_info *Fi;
    dbDriver *Driver;
    dbCatValArray cvarr;

    /* colors */
    int cat;
    struct Colors colors;

    struct My_color_rule
    {
	int red;
	int green;
	int blue;
	double d;
	int i;
    } *my_color_rules;

    int colors_n_values = 0;
    int red;
    int grn;
    int blu;

    /* init colors structure */
    Rast_init_colors(&colors);

    /* open vector map and database driver */
    if (Vect_open_old(&Map, vector_map, "") < 0)
	G_fatal_error(_("Unable to open vector map <%s>"), vector_map);

    db_CatValArray_init(&cvarr);
    if ((Fi = Vect_get_field(&Map, field)) == NULL)
	G_fatal_error(_("Database connection not defined for layer %d"),
		      field);

    if ((Driver =
	 db_start_driver_open_database(Fi->driver, Fi->database)) == NULL)
	G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
		      Fi->database, Fi->driver);

    if (!attr_column)
	attr_column = Fi->key;
    
    /* get number of records in attr_column */
    if ((nrec =
	 db_select_CatValArray(Driver, Fi->table, Fi->key, attr_column, NULL,
			       &cvarr)) == -1)
	G_fatal_error(_("Unknown column <%s> in table <%s>"), attr_column,
		      Fi->table);

    if (nrec < 0)
	G_fatal_error(_("No records selected from table <%s>"), Fi->table);

    G_debug(3, "nrec = %d", nrec);

    /* allocate space for color rules */
    my_color_rules =
	(struct My_color_rule *)G_malloc(sizeof(struct My_color_rule) * nrec);

    /* for each attribute */
    for (i = 0; i < cvarr.n_values; i++) {
	char colorstring[12];
	dbValue value;

	/* selecect color attribute and category */
	cat = cvarr.value[i].cat;
	if (db_select_value
	    (Driver, Fi->table, Fi->key, cat, rgb_column, &value) < 0) {
	    G_warning(_("No records selected"));
	    continue;
	}
	sprintf(colorstring, "%s", value.s.string);

	/* convert color string to three color integers */
	if (*colorstring != '\0') {
	    G_debug(3, "element colorstring: %s", colorstring);

	    if (G_str_to_color(colorstring, &red, &grn, &blu) == 1) {
		G_debug(3, "cat %d r:%d g:%d b:%d", cat, red, grn, blu);
	    }
	    else {
		G_warning(_("Error in color definition column (%s) "
			    "with cat %d: colorstring [%s]"), rgb_column, cat,
			  colorstring);
		G_warning(_("Color set to [200:200:200]"));
		red = grn = blu = 200;
	    }
	}
	else {
	    G_warning(_("Error in color definition column (%s), with cat %d"),
		      rgb_column, cat);
	}

	/* append color rules to my_color_rules array, they will be set
	 * later all togheter */
	colors_n_values++;
	my_color_rules[i].red = red;
	my_color_rules[i].green = grn;
	my_color_rules[i].blue = blu;
	if (is_fp) {
	    my_color_rules[i].d = cvarr.value[i].val.d;
	    G_debug(2, "val: %f rgb: %s", cvarr.value[i].val.d, colorstring);
	}
	else {
	    my_color_rules[i].i = cvarr.value[i].val.i;
	    G_debug(2, "val: %d rgb: %s", cvarr.value[i].val.i, colorstring);
	}
    }				/* /for each value in database */

    /* close the database driver */
    db_close_database_shutdown_driver(Driver);

    /* set the color rules: for each rule */
    for (i = 0; i < colors_n_values - 1; i++) {
	if (is_fp) {		/* add floating point color rule */
	    Rast_add_d_color_rule(&my_color_rules[i].d,
				      my_color_rules[i].red,
				      my_color_rules[i].green,
				      my_color_rules[i].blue,
				      &my_color_rules[i + 1].d,
				      my_color_rules[i + 1].red,
				      my_color_rules[i + 1].green,
				      my_color_rules[i + 1].blue, &colors);
	}
	else {			/* add CELL color rule */
	    Rast_add_c_color_rule(&my_color_rules[i].i,
				  my_color_rules[i].red, my_color_rules[i].green,
				  my_color_rules[i].blue,
				  &my_color_rules[i + 1].i,
				  my_color_rules[i + 1].red,
				  my_color_rules[i + 1].green,
				  my_color_rules[i + 1].blue, &colors);
	}
    }

    /* write the rules */
    Rast_write_colors(rast_name, G_mapset(), &colors);

    return 1;
}

static int cmp_labels_d(const void *a, const void *b)
{
    struct My_labels_rule *al = (struct My_labels_rule *) a;
    struct My_labels_rule *bl = (struct My_labels_rule *) b;

    if (al->d < bl->d)
	return -1;

    return (al->d > bl->d);
}

static int cmp_labels_i(const void *a, const void *b)
{
    struct My_labels_rule *al = (struct My_labels_rule *) a;
    struct My_labels_rule *bl = (struct My_labels_rule *) b;

    if (al->i < bl->i)
	return -1;

    return (al->i > bl->i);
}

/* add labels to raster cells */
int update_labels(const char *rast_name, const char *vector_map, int field,
		  const char *label_column, int use, int val,
		  const char *attr_column)
{
    int i, j;

    /* Map */
    struct Map_info Map;

    /* Attributes */
    int nrec;
    struct field_info *Fi;
    dbDriver *Driver;
    dbCatValArray cvarr;
    int col_type;

    /* labels */
    struct Categories rast_cats;
    int labels_n_values = 0;
    struct My_labels_rule *my_labels_rules;

    /* init raster categories */
    Rast_init_cats("Categories", &rast_cats);

    switch (use) {
    case USE_ATTR:
	{
	    int is_fp = Rast_map_is_fp(rast_name, G_mapset());

	    if (!label_column) {
		G_verbose_message(_("Label column was not specified, no labels will be written"));
		break;
	    }

	    Rast_set_cats_title("Rasterized vector map from labels", &rast_cats);

	    /* open vector map and database driver */
	    Vect_set_open_level(1);
	    if (Vect_open_old(&Map, vector_map, G_find_vector2(vector_map, "")) < 0)
		G_fatal_error(_("Unable to open vector map <%s>"), vector_map);

	    db_CatValArray_init(&cvarr);
	    if (!(Fi = Vect_get_field(&Map, field)))
		G_fatal_error(_("Database connection not defined for layer %d"),
			      field);

	    Vect_close(&Map);

	    if (!
		(Driver =
		 db_start_driver_open_database(Fi->driver, Fi->database)))
		G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
			      Fi->database, Fi->driver);

	    /* get number of records in label_column */
	    if ((nrec =
		 db_select_CatValArray(Driver, Fi->table, Fi->key,
				       attr_column, NULL, &cvarr)) == -1)
		G_fatal_error(_("Unknown column <%s> in table <%s>"),
			      attr_column, Fi->table);

	    if (nrec < 0)
		G_fatal_error(_("No records selected from table <%s>"),
			      Fi->table);

	    G_debug(3, "nrec = %d", nrec);

	    my_labels_rules =
		(struct My_labels_rule *)
		G_malloc(sizeof(struct My_labels_rule) * nrec);

	    /* get column type */
	    if ((col_type =
		 db_column_Ctype(Driver, Fi->table,
				 label_column)) == -1) {
		G_fatal_error(_("Column <%s> not found"), label_column);
	    }

	    /* for each attribute */
	    for (i = 0; i < cvarr.n_values; i++) {
		char tmp[64];
		dbValue value;
		int cat = cvarr.value[i].cat;

		if (db_select_value
		    (Driver, Fi->table, Fi->key, cat, label_column,
		     &value) < 0) {
		    G_warning(_("No records selected"));
		    continue;
		}

		labels_n_values++;

		db_init_string(&my_labels_rules[i].label);

		/* switch the column type */
		switch (col_type) {
		case DB_C_TYPE_DOUBLE:
		    sprintf(tmp, "%lf", db_get_value_double(&value));
		    db_set_string(&my_labels_rules[i].label, tmp);
		    break;
		case DB_C_TYPE_INT:
		    sprintf(tmp, "%d", db_get_value_int(&value));
		    db_set_string(&my_labels_rules[i].label, tmp);
		    break;
		case DB_C_TYPE_STRING:
		    db_set_string(&my_labels_rules[i].label,
				  db_get_value_string(&value));
		    break;
		default:
		    G_warning(_("Column type (%s) not supported"),
			      db_sqltype_name(col_type));
		}

		/* add the raster category to label */
		if (is_fp)
		    my_labels_rules[i].d = cvarr.value[i].val.d;
		else
		    my_labels_rules[i].i = cvarr.value[i].val.i;
	    }			/* for each value in database */

	    /* close the database driver */
	    db_close_database_shutdown_driver(Driver);

	    /* set the labels */
	    if (labels_n_values > 0) {
		char *lblstr;

		/* remove values without labels */
		j = 0;
		for (i = 0; i < labels_n_values; i++) {
		    lblstr = db_get_string(&my_labels_rules[i].label);
		    if (lblstr && *lblstr) {
			my_labels_rules[j] = my_labels_rules[i];
			j++;
		    }
		}
		labels_n_values = j;
	    }

	    if (labels_n_values > 0) {
		char *lblstr;

		if (is_fp) {
		    DCELL val1, val2;

		    qsort(my_labels_rules, labels_n_values, 
			  sizeof(struct My_labels_rule), cmp_labels_d);

		    /* remove duplicate values */
		    j = 1;
		    for (i = 1; i < labels_n_values; i++) {
			if (my_labels_rules[i - 1].d != my_labels_rules[i].d) {
			    my_labels_rules[j] = my_labels_rules[i];
			    j++;
			}
			else {
			    if (strcmp(db_get_string(&my_labels_rules[i].label),
			               db_get_string(&my_labels_rules[i - 1].label)) != 0) {
				G_warning(_("Different labels for the same value are not supported"));
			    }
			}
		    }
		    labels_n_values = j;

		    /* add labels */
		    i = 0;
		    val1 = val2 = my_labels_rules[i].d;
		    lblstr = db_get_string(&my_labels_rules[i].label);

		    for (i = 1; i < labels_n_values; i++) {
			if (strcmp(lblstr, db_get_string(&my_labels_rules[i].label)) == 0) {
			    val2 = my_labels_rules[i].d;
			}
			else {
			    Rast_set_d_cat(&val1, &val2, lblstr,
					 &rast_cats);

			    val1 = val2 = my_labels_rules[i].d;
			    lblstr = db_get_string(&my_labels_rules[i].label);
			}
		    }
		    Rast_set_d_cat(&val1, &val2, lblstr, &rast_cats);
		}
		else {
		    CELL val1, val2;

		    qsort(my_labels_rules, labels_n_values, 
			  sizeof(struct My_labels_rule), cmp_labels_i);

		    /* remove duplicate values */
		    j = 1;
		    for (i = 1; i < labels_n_values; i++) {
			if (my_labels_rules[i - 1].i != my_labels_rules[i].i) {
			    my_labels_rules[j] = my_labels_rules[i];
			    j++;
			}
			else {
			    if (strcmp(db_get_string(&my_labels_rules[i].label),
			               db_get_string(&my_labels_rules[i - 1].label)) != 0) {
				G_warning(_("Different labels for the same value are not supported"));
			    }
			}
		    }
		    labels_n_values = j;

		    /* add labels */
		    i = 0;
		    val1 = val2 = my_labels_rules[i].i;
		    lblstr = db_get_string(&my_labels_rules[i].label);

		    for (i = 1; i < labels_n_values; i++) {
			if (strcmp(lblstr, db_get_string(&my_labels_rules[i].label)) == 0) {
			    val2 = my_labels_rules[i].i;
			}
			else {
			    Rast_set_c_cat(&val1, &val2, lblstr,
					   &rast_cats);

			    val1 = val2 = my_labels_rules[i].i;
			    lblstr = db_get_string(&my_labels_rules[i].label);
			}
		    }
		    Rast_set_c_cat(&val1, &val2, lblstr, &rast_cats);
		}
	    }
	}
	break;
    case USE_VAL:
	{
	    char msg[64];
	    RASTER_MAP_TYPE map_type;
	    struct FPRange fprange;
	    struct Range range;

	    map_type = Rast_map_type(rast_name, G_mapset());
	    Rast_set_cats_title("Rasterized vector map from values", &rast_cats);

	    if (map_type == CELL_TYPE) {
		CELL min, max;

		Rast_read_range(rast_name, G_mapset(), &range);
		Rast_get_range_min_max(&range, &min, &max);

		sprintf(msg, "Value %d", val);
		Rast_set_cat(&min, &max, msg, &rast_cats, map_type);
	    }
	    else {
		DCELL fmin, fmax;

		Rast_read_fp_range(rast_name, G_mapset(), &fprange);
		Rast_get_fp_range_min_max(&fprange, &fmin, &fmax);

		sprintf(msg, "Value %.4f", (double)val);
		Rast_set_cat(&fmin, &fmax, msg, &rast_cats, map_type);
	    }

	}
	break;
    case USE_CAT:
	{
	    int row, rows, fd;
	    void *rowbuf;
	    struct Cell_stats stats;
	    CELL n;
	    RASTER_MAP_TYPE map_type;
	    long count;

	    map_type = Rast_map_type(rast_name, G_mapset());

	    if (label_column) {

		Rast_set_cats_title("Rasterized vector map from labels", &rast_cats);

		/* open vector map and database driver */
		Vect_set_open_level(1);
		if (Vect_open_old(&Map, vector_map, G_find_vector2(vector_map, "")) < 0)
		    G_fatal_error(_("Unable to open vector map <%s>"),
				    vector_map);

		db_CatValArray_init(&cvarr);
		if (!(Fi = Vect_get_field(&Map, field)))
		    G_fatal_error(_("Database connection not defined for layer %d"),
				  field);

		Vect_close(&Map);

		if (!
		    (Driver =
		     db_start_driver_open_database(Fi->driver, Fi->database)))
		    G_fatal_error(_("Unable to open database <%s> by driver <%s>"),
				  Fi->database, Fi->driver);

		/* get number of records in label_column */
		if ((nrec =
		     db_select_CatValArray(Driver, Fi->table, Fi->key,
					   label_column, NULL, &cvarr)) == -1)
		    G_fatal_error(_("Unknown column <%s> in table <%s>"),
				  label_column, Fi->table);

		if (nrec < 0)
		    G_fatal_error(_("No records selected from table <%s>"),
				  Fi->table);

		G_debug(3, "nrec = %d", nrec);

		my_labels_rules =
		    (struct My_labels_rule *)
		    G_malloc(sizeof(struct My_labels_rule) * nrec);

		/* get column type */
		if ((col_type =
		     db_column_Ctype(Driver, Fi->table,
				     label_column)) == -1) {
		    G_fatal_error(_("Column <%s> not found"), label_column);
		}

		/* close the database driver */
		db_close_database_shutdown_driver(Driver);

		/* for each attribute */
		for (i = 0; i < cvarr.n_values; i++) {
		    char tmp[2000];
		    int cat = cvarr.value[i].cat;
		    labels_n_values++;

		    db_init_string(&my_labels_rules[i].label);

		    /* switch the column type */
		    switch (col_type) {
		    case DB_C_TYPE_DOUBLE:
			sprintf(tmp, "%lf", cvarr.value[i].val.d);
			db_set_string(&my_labels_rules[i].label, tmp);
			break;
		    case DB_C_TYPE_INT:
			sprintf(tmp, "%d", cvarr.value[i].val.i);
			db_set_string(&my_labels_rules[i].label, tmp);
			break;
		    case DB_C_TYPE_STRING:
			db_set_string(&my_labels_rules[i].label,
				      db_get_string(cvarr.value[i].val.s));
			break;
		    default:
			G_warning(_("Column type (%s) not supported"),
				  db_sqltype_name(col_type));
		    }

		    /* add the raster category to label */
		    my_labels_rules[i].i = cat;

		    Rast_set_cat(&(my_labels_rules[i].i),
				 &(my_labels_rules[i].i),
				 db_get_string(&my_labels_rules[i].label),
				 &rast_cats, map_type);
		}			/* for each value in database */
	    }
	    else  {
		Rast_set_cats_title("Rasterized vector map from categories", &rast_cats);

		/* Rast_set_cat() is terribly slow for many categories,
		 * and the added labels are not really informative:
		 * 1:Category 1
		 * 2:Category 2
		 * ...
		 * 100000:Category 100000
		 * -> skip */

#if 0
		fd = Rast_open_old(rast_name, G_mapset());
		rowbuf = Rast_allocate_buf(map_type);
		Rast_init_cell_stats(&stats);
		rows = Rast_window_rows();

		for (row = 0; row < rows; row++) {
		    Rast_get_row(fd, rowbuf, row, map_type);
		    Rast_update_cell_stats(rowbuf, Rast_window_cols(), &stats);
		}

		Rast_rewind_cell_stats(&stats);

		while (Rast_next_cell_stat(&n, &count, &stats)) {
		    char msg[80];

		    sprintf(msg, "Category %d", n);
		    Rast_set_cat(&n, &n, msg, &rast_cats, map_type);
		}
		Rast_close(fd);
		G_free(rowbuf);
#endif
	    }
	}
	break;
    case USE_D:
	{
	    DCELL fmin, fmax;
	    RASTER_MAP_TYPE map_type;
	    char msg[64];

	    map_type = Rast_map_type(rast_name, G_mapset());
	    Rast_set_cats_title("Rasterized vector map from line direction", &rast_cats);
	    Rast_write_units(rast_name, "degrees CCW from +x");

	    for (i = 1; i <= 360; i++) {
		sprintf(msg, "%d degrees", i);

		if (i == 360) {
		    fmin = 359.5;
		    fmax = 360.0;
		    Rast_set_cat(&fmin, &fmax, msg, &rast_cats, map_type);
		    fmin = 0.0;
		    fmax = 0.5;
		}
		else {
		    fmin = i - 0.5;
		    fmax = i + 0.5;
		}

		Rast_set_cat(&fmin, &fmax, msg, &rast_cats, map_type);
	    }
	}
	break;
    case USE_Z:
	/* TODO or not TODO */
	break;
    default:
	G_fatal_error(_("Unknown use type: %d"), use);
	break;
    }

    Rast_write_cats(rast_name, &rast_cats);
    Rast_free_cats(&rast_cats);

    return 1;
}
