/*
* $Id: gs_bm.c,v 2.2 2006/05/02 19:24:42 brad Exp $
*/

/*  gs.c
    Bill Brown, USACERL  
    September 1993
*/

#include <grass/gstypes.h>
#include "gsget.h"
#include <stdio.h>

/* do combining of bitmaps, make bitmaps from other data w/maskval */

struct BM *gsbm_make_mask(typbuff * frombuff, float maskval, int rows,
			  int cols)
{
    int i, j, ioff;
    struct BM *bm;
    float curval;

    bm = BM_create(cols, rows);

    if (frombuff) {
	if (frombuff->bm) {
	    for (i = 0; i < rows; i++) {
		ioff = i * cols;

		for (j = 0; j < cols; j++) {
		    BM_set(bm, j, i, BM_get(frombuff->bm, j, i));
		}
	    }
	}
	else {
	    for (i = 0; i < rows; i++) {
		ioff = i * cols;

		for (j = 0; j < cols; j++) {
		    if (GET_MAPATT(frombuff, (ioff + j), curval)) {
			BM_set(bm, j, i, (curval == maskval));
		    }
		    else {
			BM_set(bm, j, i, 0);	/* doesn't mask nulls */
		    }
		}
	    }
	}
    }

    return (bm);
}

void gsbm_zero_mask(struct BM *map)
{
    int numbytes;
    unsigned char *buf;

    numbytes = map->bytes * map->rows;
    buf = map->data;

    while (numbytes--) {
	*buf++ = 0;
    }

    return;
}

/* mask types */
#define MASK_OR		1
#define MASK_ORNOT	2
#define MASK_AND	3
#define MASK_XOR	4

/* must be same size, ORs bitmaps & stores in bmvar */
static int gsbm_masks(struct BM *bmvar, struct BM *bmcon, const int mask_type)
{
    int i;
    int varsize, consize, numbytes;

    varsize = bmvar->rows * bmvar->cols;
    consize = bmcon->rows * bmcon->cols;
    numbytes = bmvar->bytes * bmvar->rows;

    if (bmcon && bmvar) {
	if (varsize != consize) {
	    fprintf(stderr, "bitmap mismatch\n");

	    return (-1);
	}

	if (bmvar->sparse || bmcon->sparse)
	    return (-1);

        switch (mask_type) {
        case MASK_OR:
            for (i = 0; i < numbytes; i++)
                bmvar->data[i] |= bmcon->data[i];
        break;
        case MASK_ORNOT:
            for (i = 0; i < numbytes; i++)
                bmvar->data[i] |= ~bmcon->data[i];
        break;
        case MASK_AND:
            for (i = 0; i < numbytes; i++)
                bmvar->data[i] &= bmcon->data[i];
        break;
        case MASK_XOR:
            for (i = 0; i < numbytes; i++)
                bmvar->data[i] ^= bmcon->data[i];
        break;
        }

	return (0);
    }

    return (-1);
}

/* must be same size, ORs bitmaps & stores in bmvar */
int gsbm_or_masks(struct BM *bmvar, struct BM *bmcon)
{
    return gsbm_masks(bmvar, bmcon, MASK_OR);
}

/* must be same size, ORs bitmap with ~bmcon & stores in bmvar */
int gsbm_ornot_masks(struct BM *bmvar, struct BM *bmcon)
{
    return gsbm_masks(bmvar, bmcon, MASK_ORNOT);
}

/* must be same size, ANDs bitmaps & stores in bmvar */
int gsbm_and_masks(struct BM *bmvar, struct BM *bmcon)
{
    return gsbm_masks(bmvar, bmcon, MASK_AND);
}

/* must be same size, XORs bitmaps & stores in bmvar */
int gsbm_xor_masks(struct BM *bmvar, struct BM *bmcon)
{
    return gsbm_masks(bmvar, bmcon, MASK_XOR);
}

/***********************************************************************/
int gs_update_curmask(geosurf * surf)
{
    struct BM *b_mask, *b_topo, *b_color;
    typbuff *t_topo, *t_mask, *t_color;
    int row, col, offset, destroy_ok = 1;
    gsurf_att *coloratt;

    if (surf->mask_needupdate) {
	surf->mask_needupdate = 0;
	surf->norm_needupdate = 1;	/* edges will need to be recalculated */

	t_topo = gs_get_att_typbuff(surf, ATT_TOPO, 0);

	if (!t_topo) {
	    surf->mask_needupdate = 1;

	    return (0);
	}

	if (surf->nz_topo || surf->nz_color ||
	    gs_mask_defined(surf) || t_topo->nm) {
	    b_mask = b_topo = b_color = NULL;

	    if (!surf->curmask) {
		surf->curmask = BM_create(surf->cols, surf->rows);
	    }
	    else {
		gsbm_zero_mask(surf->curmask);
	    }

	    if (surf->nz_topo) {
		/* no_zero elevation */
		b_topo = gsbm_make_mask(t_topo, 0.0, surf->rows, surf->cols);
	    }

	    /* make_mask_from_color */
	    if (surf->nz_color && surf->att[ATT_COLOR].att_src == MAP_ATT) {
		t_color = gs_get_att_typbuff(surf, ATT_COLOR, 0);
		coloratt = &(surf->att[ATT_COLOR]);
		b_color = BM_create(surf->cols, surf->rows);

		for (row = 0; row < surf->rows; row++) {
		    for (col = 0; col < surf->cols; col++) {
			offset = row * surf->cols + col;
			BM_set(b_color, col, row,
			       (NULL_COLOR ==
				gs_mapcolor(t_color, coloratt, offset)));
		    }
		}
	    }

	    if (gs_mask_defined(surf)) {
		t_mask = gs_get_att_typbuff(surf, ATT_MASK, 0);

		if (t_mask->bm) {
		    b_mask = t_mask->bm;
		    destroy_ok = 0;
		}
		else {
		    b_mask = BM_create(surf->cols, surf->rows);
		    gs_set_maskmode((int) surf->att[ATT_MASK].constant);

		    for (row = 0; row < surf->rows; row++) {
			for (col = 0; col < surf->cols; col++) {
			    offset = row * surf->cols + col;
			    BM_set(b_mask, col, row,
				   gs_masked(t_mask, col, row, offset));
			}
		    }
		}
	    }

	    if (b_topo) {
		fprintf(stderr, "Update topo mask\n");
		gsbm_or_masks(surf->curmask, b_topo);
		BM_destroy(b_topo);
	    }

	    if (b_color) {
		fprintf(stderr, "Update color mask\n");
		gsbm_or_masks(surf->curmask, b_color);
		BM_destroy(b_color);
	    }

	    if (t_topo->nm) {
		fprintf(stderr, "Update elev null mask\n");
		gsbm_or_masks(surf->curmask, t_topo->nm);
	    }

	    if (b_mask) {
		fprintf(stderr, "Update mask mask\n");

		if (t_mask->bm) {
		    if (surf->att[ATT_MASK].constant) {
			/* invert */
			gsbm_or_masks(surf->curmask, t_mask->bm);
		    }
		    else {
			gsbm_ornot_masks(surf->curmask, t_mask->bm);
		    }
		}
		else {
		    gsbm_or_masks(surf->curmask, b_mask);
		}

		if (destroy_ok) {
		    BM_destroy(b_mask);
		}
	    }

	    /* TODO: update surf->zminmasked */
	    return (1);
	}
	else if (surf->curmask) {
	    BM_destroy(surf->curmask);
	    surf->curmask = NULL;
	    surf->zminmasked = surf->zmin;
	}

    }

    return (0);
}

/***********************************************************************/
void print_bm(struct BM *bm)
{
    int i, j;

    for (i = 0; i < bm->rows; i++) {
	for (j = 0; j < bm->cols; j++) {
	    fprintf(stderr, "%d ", BM_get(bm, j, i));
	}

	fprintf(stderr, "\n");
    }

    return;
}
