/*
 ****************************************************************************
 *
 * MODULE:       s.vol.rst: program for 3D(volume) interpolation and geometry
 *               analysis from scattered point data using regularized spline
 *               with tension
 *
 * AUTHOR(S):    Original program (1989) and various modifications:
 *               Lubos Mitas
 *
 *               GRASS 4.2, GRASS 5.0 version and modifications:
 *               H. Mitasova,  I. Kosinovsky, D. Gerdes, J. Hofierka
 *
 * PURPOSE:      s.vol.rst interpolates the values to 3-dimensional grid from
 *               point data (climatic stations, drill holes etc.) given in a
 *               sites file named input. Output grid3 file is elev. 
 *               Regularized spline with tension is used for the
 *               interpolation.
 *
 * COPYRIGHT:    (C) 1989, 1993, 2000 L. Mitas,  H. Mitasova,
 *               I. Kosinovsky, D. Gerdes, J. Hofierka
 *
 *               This program is free software under the GNU General Public
 *              License (>=v2). Read the file COPYING that comes with GRASS
 *              for details.
 *
 *****************************************************************************/

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <grass/gis.h>

#include "oct.h"
#include "surf.h"
#include "dataoct.h"
#include "userextern.h"
#include "userglobs.h"
#include "user.h"
#include "points.h"

/*
   fi - tension parameter
   az- interpolated values z for output grid
   adx,ady, ... - estimation of derivatives for output grid
   nsizr,nsizc - number of rows and columns for output grid

   interp_call() - divides region on segments
 */



int
translate_oct(struct octtree *tree, double numberx, double numbery,
	      double numberz, double numberw)
{
    int total = 0, i;

    if (tree == NULL)
	return 0;
    if (tree->data == NULL)
	return 0;
    if (tree->leafs != NULL) {
	((struct octdata *)(tree->data))->x_orig -= numberx;
	((struct octdata *)(tree->data))->y_orig -= numbery;
	((struct octdata *)(tree->data))->z_orig -= numberz;
	for (i = 0; i < NUMLEAFS; i++) {
	    total +=
		translate_oct(tree->leafs[i], numberx, numbery, numberz,
			      numberw);
	}
    }
    else {
	((struct octdata *)(tree->data))->x_orig -= numberx;
	((struct octdata *)(tree->data))->y_orig -= numbery;
	((struct octdata *)(tree->data))->z_orig -= numberz;
	for (i = 0; i < ((struct octdata *)(tree->data))->n_points; i++) {
	    ((struct octdata *)(tree->data))->points[i].x -= numberx;
	    ((struct octdata *)(tree->data))->points[i].y -= numbery;
	    ((struct octdata *)(tree->data))->points[i].z -= numberz;
	    ((struct octdata *)(tree->data))->points[i].w -= numberw;
	}
	return 1;
    }
    return total;
}





int interp_call(struct octtree *root, struct octtree *tree)
{
    /*    double          xmn,xmx,ymn,ymx,zmn,zmx; */
    double distx, disty, distz, distxp, distyp, distzp, temp1, temp2, temp3;
    int i, npt, nptprev, MAXENC, k, j;
    static struct quadruple *points = NULL;
    struct point_3d skip_point;
    struct point_3d *point = NULL;
    int skip_index, segtest;
    double xx, yy, zz, ww;

    if (tree == NULL)
	return -1;
    if (tree->data == NULL)
	return -1;
    if (((struct octdata *)(tree->data))->points == NULL) {
	for (k = 0; k < NUMLEAFS; k++)
	    if (!interp_call(root, tree->leafs[k]))
		return 0;
	return 1;
    }
    else {

	if (!points) {
	    if (!
		(points =
		 (struct quadruple *)G_malloc(sizeof(struct quadruple) *
					      (KMAX2 + 1))))
		clean_fatal_error("Not enough memory for points");
	}

	/*           if(!(point=(struct point_3d*)G_malloc(sizeof(struct point_3d)*(KMAX2+1))))
	   clean_fatal_error("Not enough memory for points"); */


	distx = (((struct octdata *)(tree->data))->n_cols * ew_res) * 0.1;
	disty = (((struct octdata *)(tree->data))->n_rows * ns_res) * 0.1;
	distz = (((struct octdata *)(tree->data))->n_levs * tb_res) * 0.1;
	distxp = 0;
	distyp = 0;
	distzp = 0;
	xmn = ((struct octdata *)(tree->data))->x_orig;
	xmx = ((struct octdata *)(tree->data))->x_orig +
	    ((struct octdata *)(tree->data))->n_cols * ew_res;
	ymn = ((struct octdata *)(tree->data))->y_orig;
	ymx = ((struct octdata *)(tree->data))->y_orig +
	    ((struct octdata *)(tree->data))->n_rows * ns_res;
	zmn = ((struct octdata *)(tree->data))->z_orig;
	zmx = ((struct octdata *)(tree->data))->z_orig +
	    ((struct octdata *)(tree->data))->n_levs * tb_res;
	i = 0;
	MAXENC = 0;
	npt = OT_region_data(root, xmn - distx, xmx + distx, ymn - disty,
			     ymx + disty, zmn - distz, zmx + distz, points,
			     KMAX2);
	while ((npt < KMIN) || (npt > KMAX2)) {
	    if (i >= 70) {
		fprintf(stderr,
			"Warning: taking too long to find points for interpolation--please change the region to area where your points are\n");
		break;
	    }
	    i++;
	    if (npt > KMAX2) {
		MAXENC = 1;
		nptprev = npt;
		temp1 = distxp;
		distxp = distx;
		distx = distxp - fabs(distx - temp1) * 0.5;
		temp2 = distyp;
		distyp = disty;
		disty = distyp - fabs(disty - temp2) * 0.5;
		temp3 = distzp;
		distzp = distz;
		distz = distzp - fabs(distz - temp3) * 0.5;
		/*              npt = OT_region_data (root,xmn-distx,xmx+distx,ymn-disty,
		   ymx+disty,zmn-distz,zmx+distz,points, KMAX2); */
	    }
	    else {
		nptprev = npt;
		temp1 = distyp;
		distyp = disty;
		temp2 = distxp;
		distxp = distx;
		temp3 = distzp;
		distzp = distz;
		if (MAXENC) {
		    disty = fabs(disty - temp1) * 0.5 + distyp;
		    distx = fabs(distx - temp2) * 0.5 + distxp;
		    distz = fabs(distz - temp3) * 0.5 + distzp;
		}
		else {
		    distx += distx;
		    disty += disty;
		    distz += distz;
		}

		/*`             npt = OT_region_data (root,xmn-distx,xmx+distx,ymn-disty,
		   ymx+disty,zmn-distz,zmx+distz,points, KMAX2);
		 */
	    }			/* end of npt > KMAX2 else */

	    npt = OT_region_data(root, xmn - distx, xmx + distx, ymn - disty,
				 ymx + disty, zmn - distz, zmx + distz,
				 points, KMAX2);

	}
	/*fprintf(stderr,"got %d points for interpolation\n",npt); */
	{
	    static int first = 1;

	    if (first) {
		first = 0;

		if (!
		    (A =
		     (double *)G_malloc(sizeof(double) *
					((KMAX2 + 1) * (KMAX2 + 2) + 1)))) {
		    clean_fatal_error("Cannot allocate A");
		}
		if (!(b = (double *)G_malloc(sizeof(double) * (KMAX2 + 2)))) {
		    clean_fatal_error("Cannot allocate b");
		}
		if (!(w = (double *)G_malloc(sizeof(double) * (KMAX2 + 1)))) {
		    clean_fatal_error("Cannot allocate w");
		}
	    }
	}

	/*      if(!(points=(struct quadruple*)G_malloc(sizeof(struct quadruple)*(npt))))
	   clean_fatal_error("Not enough memory for points"); */
	if (!
	    (point =
	     (struct point_3d *)G_malloc(sizeof(struct point_3d) * (npt))))
	    clean_fatal_error("Not enough memory for points");

	/* cv stuff */

	if (cv) {
	    for (i = 0; i < npt; i++) {
		point[i].x = points[i].x;
		point[i].y = points[i].y;
		point[i].z = points[i].z;
		point[i].w = points[i].w;
	    }

	    for (skip_index = 0; skip_index < npt; skip_index++) {
		segtest = 0;
		j = 0;
		xx = point[skip_index].x;
		yy = point[skip_index].y;
		zz = point[skip_index].z;
		ww = point[skip_index].w;
		if (xx >= xmn && xx <= xmx && yy >= ymn && yy <= ymx &&
		    zz >= zmn && zz <= zmx) {
		    segtest = 1;
		    skip_point.x = point[skip_index].x;
		    skip_point.y = point[skip_index].y;
		    skip_point.z = point[skip_index].z;
		    skip_point.w = point[skip_index].w;
		    for (k = 0; k < npt; k++) {
			if (k != skip_index) {
			    points[j].x = point[k].x;
			    points[j].y = point[k].y;
			    points[j].z = point[k].z;
			    points[j].w = point[k].w;
			    j++;
			}
		    }
		}		/* segment area test */

		if (segtest == 1)
		    if (!COGRR1
			(xmn, ymn, zmn,
			 ((struct octdata *)(tree->data))->n_rows,
			 ((struct octdata *)(tree->data))->n_cols,
			 ((struct octdata *)(tree->data))->n_levs, npt - 1,
			 points, skip_point)) {
			fprintf(stderr, "Error in COGRR!\n");
			return 0;
		    }
	    }
	}



	/* show before to catch 0% */

	/*fprintf(stderr,"total segments = %d, Current = %d, npoints = %d\n",totsegm,cursegm+1,npt); */

	{
	    G_percent(cursegm, totsegm, 1);
	}

	if (!cv)
	    if (!COGRR1
		(xmn, ymn, zmn, ((struct octdata *)(tree->data))->n_rows,
		 ((struct octdata *)(tree->data))->n_cols,
		 ((struct octdata *)(tree->data))->n_levs, npt, points,
		 skip_point)) {
		fprintf(stderr, "Error in COGRR!\n");
		return 0;
	    }

	/*      G_free (points); */

	/* G_free (A); */
	/* show after to catch 100% */
	cursegm++;
	if (totsegm != 0) {
	    G_percent(cursegm, totsegm, 1);
	}

	return 1;
    }

    return 1;
}
