/*
* $Id: gpd.c,v 2.6 2006/03/27 16:37:20 markus Exp $
*/

/*  gpd.c
    Bill Brown, USACERL  
    December 1993
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <grass/gstypes.h>
#include "rowcol.h"

#define CHK_FREQ 50

/* BOB -- border allowed outside of viewport*/
#define v_border 50

/* ACS_MODIFY_BEGIN site_attr management ***************************************/
static float _cur_size_;

/* This substitutes gpd_obj */
int gpd_obj_site_attr(geosurf * gs, geosite * gp, geopoint *gpt, Point3 site)
{
    float size, z, y, x, z_scale, z_offset;
    int marker, color, i, ii, iii;
	int use_attr, has_drawn;
	int _put_aside_;

	_put_aside_ = 0;
	_cur_size_ = gp->size;

    z_scale = GS_global_exag();
	z_offset = 0.0;

	has_drawn = 0;

	for(i=0; i<GPT_MAX_ATTR; i++) {
		color = gp->color; marker = gp->marker; size = gp->size;
		use_attr = 0;

		if (gp->use_attr[i] & ST_ATT_COLOR) {
			use_attr = 1;
			color = gpt->color[i];
		}

		if (gp->use_attr[i] & ST_ATT_MARKER) {
			use_attr = 1;
			marker = gpt->marker[i];
		}

		if (gp->use_attr[i] & ST_ATT_SIZE) {
			use_attr = 1;
			size = gpt->size[i] * gp->size;
			if (gp->marker == ST_HISTOGRAM) _put_aside_ = 1;
		}

/* ACS_MODIFY_BEGIN site_highlight management **********************************/
		if (gpt->highlight_color) color = gpt->highlight_color_value;
		if (gpt->highlight_marker) marker = gpt->highlight_marker_value;
		if (gpt->highlight_size) size *= gpt->highlight_size_value;
/* ACS_MODIFY_END site_highlight management ************************************/

		if (_put_aside_) {
			if (use_attr == 1) {
				has_drawn = 1;

/*******************************************************************************
		fixed size = gp->size
		this is mailny intended for "histograms" that grow in z, but not in xy

        square filling to right and then up

         15 14 13 12
          8  7  6 11
          3  2  5 10
          0  1  4  9

*******************************************************************************/
				x = site[X];
				y = site[Y];

				ii = (int)(sqrt(i));
				iii = ii * ii + ii;

				if (i <= iii) {
					site[X] += ii * 2.2 * gp->size;
					site[Y] += (i-ii) * 2.2 * gp->size;
				} else {
					site[X] += (ii-(i-iii)) * 2.2 * gp->size;
					site[Y] += ii * 2.2 * gp->size;

				}

				gpd_obj(gs, color, size, marker, site);

				site[X] = x;
				site[Y] = y;
			}
		} else {
			if (i>0) z_offset += size;
			if (use_attr == 1) {
				has_drawn = 1;

				z = site[Z];
				site[Z] += z_offset / z_scale;

				gpd_obj(gs, color, size, marker, site);

				site[Z] = z;
			}

			z_offset += size;
		}
	}

	if (has_drawn == 0)
		gpd_obj(gs, color, size, marker, site);

	return(0);
}
/* ACS_MODIFY_END site_attr management *****************************************/


/* check for cancel every CHK_FREQ points */

int gs_point_in_region(geosurf * gs, float *pt, float *region)
{
    float top, bottom, left, right;

    if (!region) {
	top = gs->yrange;
	bottom = VROW2Y(gs, VROWS(gs));
	left = 0.0;
	right = VCOL2X(gs, VCOLS(gs));
    }
    else {
	top = region[0];
	bottom = region[1];
	left = region[2];
	right = region[3];
    }

    return (pt[X] >= left && pt[X] <= right &&
	    pt[Y] >= bottom && pt[Y] <= top);
}

/* TODO: add size1, size2 & dir1, dir2 (eg azimuth, elevation) variables
*/
/* do normal transforms before calling */
/* Note gs: NULL if 3d obj or const elev surface */
void gpd_obj(geosurf * gs, int color, float size, int marker, Point3 pt)
{
    float sz, lpt[3];
    float siz[3];

    gsd_color_func(color);
    sz = GS_global_exag();
    GS_v3eq(lpt, pt);		/* CHANGING Z OF POINT PASSED, so use copy */

    switch (marker) {
/* ACS_MODIFY_BEGIN site_attr management ***************************************/
    case ST_HISTOGRAM:
	gsd_colormode(CM_DIFFUSE);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	siz[0] = _cur_size_;
	siz[1] = _cur_size_;
	siz[2] = size;

	gsd_box(lpt, color, siz);

	gsd_popmatrix();
	gsd_colormode(CM_COLOR);

	break;
/* ACS_MODIFY_END   site_attr management ***************************************/
    case ST_DIAMOND:
	/*
	   gsd_colormode(CM_AD);
	 */
	gsd_colormode(CM_DIFFUSE);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_diamond(lpt, color, size);
	gsd_popmatrix();
	gsd_colormode(CM_COLOR);

	break;
    case ST_BOX:
	gsd_colormode(CM_COLOR);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_draw_box(lpt, color, size);
	gsd_popmatrix();

	break;
    case ST_SPHERE:
	/*
	   gsd_colormode(CM_AD);
	 */
	gsd_colormode(CM_DIFFUSE);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_sphere(lpt, size);
	gsd_popmatrix();
	gsd_colormode(CM_COLOR);

	break;
    case ST_GYRO:
	gsd_colormode(CM_COLOR);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_draw_gyro(lpt, color, size);
	gsd_popmatrix();

	break;
    case ST_ASTER:
	gsd_colormode(CM_COLOR);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_draw_asterisk(lpt, color, size);
	gsd_popmatrix();

	break;
    case ST_CUBE:
	gsd_colormode(CM_DIFFUSE);
	gsd_pushmatrix();

	if (sz) {
	    lpt[Z] *= sz;
	    gsd_scale(1.0, 1.0, 1. / sz);
	}

	gsd_cube(lpt, color, size);
	gsd_popmatrix();
	gsd_colormode(CM_COLOR);

	break;
    default:
    case ST_X:
	gsd_colormode(CM_COLOR);
	gsd_x(gs, lpt, color, size);

	break;
    }

    return;
}

/* need to think about translations - If user translates surface,
sites should automatically go with it, but translating sites should
translate it relative to surface on which it's displayed */
/*  TODO: prevent scaling by 0  */
/* handling mask checking here */
int gpd_2dsite(geosite * gp, geosurf * gs, int do_fast)
{
    float site[3], konst;
    float size;
    int src, check, marker, color;
    geopoint *gpt;
    typbuff *buf;
    GLdouble modelMatrix[16], projMatrix[16];
    GLint viewport[4];
    GLint window[4];


    if (GS_check_cancel()) {
	return (0);
    }

    if (gs) {
	gs_update_curmask(gs);

	src = gs_get_att_src(gs, ATT_TOPO);

	if (src == CONST_ATT) {
	    konst = gs->att[ATT_TOPO].constant;
	    site[Z] = konst;
	}
	else {
	    buf = gs_get_att_typbuff(gs, ATT_TOPO, 0);
	}

	/* Get viewport parameters for view check */
	gsd_getwindow(&window, &viewport, &modelMatrix, &projMatrix);

	gsd_pushmatrix();

	gsd_do_scale(1);

	gsd_translate(gs->x_trans, gs->y_trans, gs->z_trans);

	gsd_linewidth(gp->width);

	check = 0;
	color = gp->color;
	marker = gp->marker;
	size = gp->size;

	for (gpt = gp->points; gpt; gpt = gpt->next) {
	    if (!(++check % CHK_FREQ)) {
		if (GS_check_cancel()) {
		    gsd_linewidth(1);
		    gsd_popmatrix();

		    return (0);
		}
	    }

	    site[X] = gpt->p3[X] + gp->x_trans - gs->ox;
	    site[Y] = gpt->p3[Y] + gp->y_trans - gs->oy;

	    if (gs_point_is_masked(gs, site)) {
		continue;
	    }

	    /* TODO: set other dynamic attributes */
	    if (gp->attr_mode & ST_ATT_COLOR) {
		color = gpt->iattr;
	    }

	    if (src == MAP_ATT) {
		if (viewcell_tri_interp(gs, buf, site, 1)) {
		    /* returns 0 if outside or masked */
		    site[Z] += gp->z_trans;

		    if (gsd_checkpoint
			(site, window, viewport, modelMatrix, projMatrix))
			continue;
		    else
/* ACS_MODIFY_OneLine site_attr management - was: gpd_obj(gs, color, size, marker, site); */
			gpd_obj_site_attr(gs, gp, gpt, site);
		}
	    }
	    else if (src == CONST_ATT) {
		if (gs_point_in_region(gs, site, NULL)) {
		    site[Z] += gp->z_trans;
		    if (gsd_checkpoint
			(site, window, viewport, modelMatrix, projMatrix))
			continue;
		    else
/* ACS_MODIFY_OneLine site_attr management - was: gpd_obj(NULL, color, size, marker, site); */
			gpd_obj_site_attr(NULL, gp, gpt, site);
		}
	    }
	}

	gsd_linewidth(1);
	gsd_popmatrix();
    }

    return (1);
}

int gpd_3dsite(geosite * gp, float xo, float yo, int do_fast)
{
    float site[3], tz;
    float size;
    int check, color, marker;
    geopoint *gpt;
    GLdouble modelMatrix[16], projMatrix[16];
    GLint viewport[4];
    GLint window[4];

    if (GS_check_cancel()) {
	return (0);
    }

    gsd_getwindow(&window, &viewport, &modelMatrix, &projMatrix);

    gsd_pushmatrix();

    gsd_do_scale(1);

    tz = GS_global_exag();
    site[Z] = 0.0;

    check = 0;
    color = gp->color;
    marker = gp->marker;
    size = gp->size;

    gsd_linewidth(gp->width);

    for (gpt = gp->points; gpt; gpt = gpt->next) {
	if (!(++check % CHK_FREQ)) {
	    if (GS_check_cancel()) {
		gsd_linewidth(1);
		gsd_popmatrix();

		return (0);
	    }
	}

	site[X] = gpt->p3[X] + gp->x_trans - xo;
	site[Y] = gpt->p3[Y] + gp->y_trans - yo;

	if (tz) {
	    site[Z] = gpt->p3[Z] + gp->z_trans;
	}

	/* TODO: set other dynamic attributes */
	if (gp->attr_mode & ST_ATT_COLOR) {
	    color = gpt->iattr;
	}

	if (gsd_checkpoint(site, window, viewport, modelMatrix, projMatrix))
	    continue;
	else
	    /* clip points outside default region? */
/* ACS_MODIFY_OneLine site_attr management - was: gpd_obj(NULL, color, size, marker, site); */
	    gpd_obj_site_attr(NULL, gp, gpt, site);
    }

    gsd_linewidth(1);
    gsd_popmatrix();

    return (1);
}
