/*
****************************************************************************
*
* 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 <stdlib.h>
#include <grass/gis.h>
#include "dataoct.h" 
#include "externs.h" 
#include "user.h"


struct quadruple *point_new(double x, double y, double z, double w, double sm)
{
struct quadruple *point;

if(!(point = (struct quadruple *)G_malloc (sizeof(struct quadruple)))) {
 return NULL;
}

point->x = x;
point->y = y;
point->z = z;
point->w = w;
point->sm = sm;

return point;
}




struct octdata *data_new (double x_orig, double y_orig, double z_orig, int n_rows, int n_cols, int n_levs, int n_points)

{
struct octdata *data; 
int i;

if (!(data = (struct octdata*)G_malloc (sizeof(struct octdata)))) {
  return NULL;
}

data->x_orig=x_orig;
data->y_orig=y_orig;
data->z_orig=z_orig;
data->n_rows=n_rows;
data->n_cols=n_cols;
data->n_levs=n_levs;
data->n_points=n_points;
data->points = (struct quadruple *)G_malloc (sizeof(struct quadruple)*(KMAX+1));
for (i=0;i<=KMAX;i++) {
  data->points[i].x=0;
  data->points[i].y=0;
  data->points[i].z=0;
  data->points[i].w=0;
  data->points[i].sm=0;
}

return data;
}




int oct_compare(struct quadruple *point, struct octdata *data)
/* returns the quadrant the point should be inserted in */
/* called by divide() */
{int cond1,cond2,cond3,cond4,cond5,cond6,rows,cols,levs;
 if (data==NULL) return -1;
 if(data->n_rows % 2==0) { 
   rows=data->n_rows/2;
 }
 else {
   rows=(int)(data->n_rows/2)+1; 
 } 
 if(data->n_cols % 2==0) { 
   cols=data->n_cols/2;
 }
 else {
   cols=(int)(data->n_cols/2)+1; 
 } 
 if(data->n_levs % 2==0) { 
   levs=data->n_levs/2;
 }
 else {
   levs=(int)(data->n_levs/2)+1; 
 } 
 cond1=(point->x>=data->x_orig);
 cond2=(point->x>=data->x_orig+cols*ew_res);
 cond3=(point->y>=data->y_orig);
 cond4=(point->y>=data->y_orig+rows*ns_res);
 cond5=(point->z>=data->z_orig);
 cond6=(point->z>=data->z_orig+levs*tb_res);
 if(cond1&&cond3&&cond5) {
   if(cond6) {
     if(cond2&&cond4) return NET;
     if(cond2) return SET;
     if(cond4) return NWT;
     return SWT;
   }
   else {
     if(cond2&&cond4) return NEB;
     if(cond2) return SEB;
     if(cond4) return NWB;
     return SWB;
   }
 }
 else return 0;
} 


int oct_add_data(struct quadruple *point, struct octdata *data)
{ int n,i,cond;
  double xx,yy,zz,r;
 
     cond=1;    
     for(i=0;i<data->n_points;i++) {
       xx=data->points[i].x - point->x;
       yy=data->points[i].y - point->y;
       zz=data->points[i].z - point->z;
       r=xx*xx+yy*yy+zz*zz;
       if (r<=dmin) {
         cond=0;
       }
     }

     if (cond) {
       n=(data->n_points)++;
       data->points[n].x=point->x;
       data->points[n].y=point->y;
       data->points[n].z=point->z;
       data->points[n].w=point->w;
       data->points[n].sm=point->sm;
     }
  return cond;
}
 

int oct_division_check(struct octdata *data)
{
   if (data->points == NULL) return -1;
   if (data->n_points < KMAX) return 0;
   else return 1;
}


struct octdata ** oct_divide_data(struct octdata *data)
{
    struct octdata **datas;
    int             cols1, cols2, rows1, rows2, levs1, levs2;
    int             comp, i;
    double          dx, dy, dz, x_or, y_or, z_or;

    if ((data->n_cols <= 1) || (data->n_rows <= 1))
    {
      clean_fatal_error("Points are too concentrated -- please increase DMIN");
    }

    if (data->n_cols % 2 == 0)
    {
	cols1 = data->n_cols / 2;
	cols2 = cols1;
    }
    else
    {
	cols2 = (int) (data->n_cols / 2);
	cols1 = cols2 + 1;
    }
    if (data->n_rows % 2 == 0)
    {
	rows1 = data->n_rows / 2;
	rows2 = rows1;
    }
    else
    {
	rows2 = (int) (data->n_rows / 2);
	rows1 = rows2 + 1;
    }
    if(data->n_levs % 2==0) { 
      levs1=data->n_levs/2;
      levs2=levs1;
    }
    else {
      levs2=(int)(data->n_levs/2); 
      levs1=levs2+1;
    } 
    dx=cols1*ew_res;
    dy=rows1*ns_res;
    dz=levs1*tb_res;
    x_or=data->x_orig;
    y_or=data->y_orig;
    z_or=data->z_orig;
    if (!(datas=(struct octdata **) G_malloc (sizeof(struct octdata *) * 9)))
    {
        return NULL;
    }
    datas[SWB]=data_new(x_or,y_or,z_or,rows1,cols1,levs1,0);
    datas[SEB]=data_new(x_or+dx,y_or,z_or,rows1,cols2,levs1,0);
    datas[NWB]=data_new(x_or,y_or+dy,z_or,rows2,cols1,levs1,0);
    datas[NEB]=data_new(x_or+dx,y_or+dy,z_or,rows2,cols2,levs1,0);
    datas[SWT]=data_new(x_or,y_or,z_or+dz,rows1,cols1,levs2,0);
    datas[SET]=data_new(x_or+dx,y_or,z_or+dz,rows1,cols2,levs2,0);
    datas[NWT]=data_new(x_or,y_or+dy,z_or+dz,rows2,cols1,levs2,0);
    datas[NET]=data_new(x_or+dx,y_or+dy,z_or+dz,rows2,cols2,levs2,0);
    for (i=0;i<data->n_points;i++) {
      comp = oct_compare (data->points+i, data); 
      if ((comp<1)||(comp>NUMLEAFS))  {
        clean_fatal_error("Point out of range");
      }
      oct_add_data (data->points+i, datas[comp]);
    }
    data->n_points=0;
    data->points = NULL;
    return datas;
}






int oct_intersect(double xmin, double xmax, double ymin, double ymax, 
                  double zmin, double zmax, struct octdata *data)
{
 int ix, ix0, iy, iy0, iz, iz0, izor, iyzoror; 
 
 ix0 = ((data->x_orig>=xmin)&&(data->x_orig<=xmax)); 
 iy0 = ((data->y_orig>=ymin)&&(data->y_orig<=ymax));
 iz0 = ((data->z_orig>=zmin)&&(data->z_orig<=zmax));
 iz  = ((zmin>=data->z_orig)&&(zmin<=data->z_orig+data->n_levs*tb_res));
 iy  = ((ymin>=data->y_orig)&&(ymin<=data->y_orig+data->n_rows*ns_res));
 ix  = ((xmin>=data->x_orig)&&(xmin<=data->x_orig+data->n_cols*ew_res));

/*printf("\n%f %f %f",zmin,data->z_orig,data->z_orig+data->n_levs*tb_res);*/
 izor = (iz || iz0);
 iyzoror = ((iy0 && izor) || (iy && izor));

 if (  (ix0  && iyzoror) || (ix && iyzoror) ) 

 {
    return 1;
 }
  else return 0;
}






int oct_get_points(struct quadruple *points, struct octdata *data,
                   double xmin, double xmax, double ymin, double ymax, 
                   double zmin, double zmax, int MAX)
{
 int i;
 int n=0;
 int l=0;

 struct quadruple *point;
    for(i=0;i<data->n_points;i++) {
      point=data->points+i;
      if(l>=MAX) return MAX+1;
      if ((point->x>=xmin)&&(point->x<=xmax)
        &&(point->y>=ymin)&&(point->y<=ymax)
        &&(point->z>=zmin)&&(point->z<=zmax))
      {
	points[l].x = point->x;
	points[l].y = point->y;
        points[l].z = point->z;
        points[l].w = point->w;
	points[l].sm = point->sm;
	l++;
      }
    }
    n = l;
    return n;
}	
