#include <math.h>
#include <grass/gis.h>
#define TINF_MAIN
#include "tinf.h"
#include <limits.h>
#include <float.h>

/* To add a new multitype function, use the function below to initialize
 * the function pointer to each of the three typed functions.  The function
 * pointers and the function prototypes are defined in a header file.   
 * The actual functions follow. */

void set_func_pointers(int in_type)
{
   switch(in_type)
   {
      case CELL_TYPE :
         is_null=is_null_c;
         bpe=bpe_c;
         get_max=get_max_c;
         get_min=get_min_c;
         get_row=get_row_c;
         get_buf=get_buf_c;
         put_row=put_row_c;
         slope=slope_c;
         set_min=set_min_c;
         set_max=set_max_c;
         diff=diff_c;
         sum=sum_c;
         quot=quot_c;
         prod=prod_c;

         break;

      case FCELL_TYPE :
         is_null=is_null_f;
         bpe=bpe_f;
         get_max=get_max_f;
         get_min=get_min_f;
         get_row=get_row_f;
         get_buf=get_buf_f;
         put_row=put_row_f;
         slope=slope_f;
         set_min=set_min_f;
         set_max=set_max_f;
         diff=diff_f;
         sum=sum_f;
         quot=quot_f;
         prod=prod_f;
         
         break;

      case DCELL_TYPE :
         is_null=is_null_d;
         bpe=bpe_d;
         get_max=get_max_d;
         get_min=get_min_d;
         get_row=get_row_d;
         get_buf=get_buf_d;
         put_row=put_row_d;
         slope=slope_d;
         set_min=set_min_d;
         set_max=set_max_d;
         diff=diff_d;
         sum=sum_d;
         quot=quot_d;
         prod=prod_d;
   }

   return;

}

/* check for null values */
int is_null_c(void *value){ return G_is_c_null_value((CELL *)value); }
int is_null_f(void *value){ return G_is_f_null_value((FCELL *)value); }
int is_null_d(void *value){ return G_is_d_null_value((DCELL *)value); }

/* return the size of the current type */
int bpe_c(){ return sizeof(CELL); }
int bpe_f(){ return sizeof(FCELL); }
int bpe_d(){ return sizeof(DCELL); }

/* return the pointer that points to the smaller of two value */
void *get_min_c(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(CELL *)v1 < *(CELL *)v2)rc=v1;
   return rc;
}

void *get_min_f(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(FCELL *)v1 < *(FCELL *)v2)rc=v1;
   return rc;
}

void *get_min_d(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(DCELL *)v1 < *(DCELL *)v2)rc=v1;
   return rc;
}

/* return the pointer that points to the larger value */
void *get_max_c(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(CELL *)v1 > *(CELL *)v2)rc=v1;
   return rc;
}

void *get_max_f(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(FCELL *)v1 > *(FCELL *)v2)rc=v1;
   return rc;
}

void *get_max_d(void *v1, void *v2)
{
   void *rc;

   rc=v2;
   if(*(DCELL *)v1 > *(DCELL *)v2)rc=v1;
   return rc;
}

/* Read one line from a raster file */
int get_row_c(int fd, void *row, int n)
{
   return G_get_c_raster_row(fd,(CELL *)row,n);
}

int get_row_f(int fd, void *row, int n)
{
   return G_get_f_raster_row(fd,(FCELL *)row,n);
}

int get_row_d(int fd, void *row, int n)
{
   return G_get_d_raster_row(fd,(DCELL *)row,n);
}

/* Write one row to a raster file */
int put_row_c(int fd, void *row)
{
   return G_put_c_raster_row(fd,(CELL *)row);
}

int put_row_f(int fd, void *row)
{
   return G_put_f_raster_row(fd,(FCELL *)row);
}

int put_row_d(int fd, void *row)
{
   return G_put_d_raster_row(fd,(DCELL *)row);
}

/* Allocate memory for one line of data */
void *get_buf_c()
{
   return (void *)G_allocate_c_raster_buf();
}

void *get_buf_f()
{
   return (void *)G_allocate_f_raster_buf();
}

void *get_buf_d()
{
   return (void *)G_allocate_d_raster_buf();
}

/* initialize memory to a minimum value */
void set_min_c(void *v){ *(CELL *)v=INT_MIN; }
void set_min_f(void *v){ *(FCELL *)v=FLT_MIN; }
void set_min_d(void *v){ *(DCELL *)v=DBL_MIN; }

/* initialize memory to a maximum value */
void set_max_c(void *v){ *(CELL *)v=INT_MAX; }
void set_max_f(void *v){ *(FCELL *)v=FLT_MAX; }
void set_max_d(void *v){ *(DCELL *)v=DBL_MAX; }

/* get the difference between two values, returned in the first pointer */
void diff_c(void *v1, void *v2){*(CELL *)v1-=*(CELL *)v2;}
void diff_f(void *v1, void *v2){*(FCELL *)v1-=*(FCELL *)v2;}
void diff_d(void *v1, void *v2){*(DCELL *)v1-=*(DCELL *)v2;}

/* get the sum of two values, returned in the first pointer */
void sum_c(void *v1, void *v2){*(CELL *)v1+=*(CELL *)v2;}
void sum_f(void *v1, void *v2){*(FCELL *)v1+=*(FCELL *)v2;}
void sum_d(void *v1, void *v2){*(DCELL *)v1+=*(DCELL *)v2;}

/* get the quotient of two values, returned in the first pointer */
void quot_c(void *v1, void *v2){*(CELL *)v1/=*(CELL *)v2;}
void quot_f(void *v1, void *v2){*(FCELL *)v1/=*(FCELL *)v2;}
void quot_d(void *v1, void *v2){*(DCELL *)v1/=*(DCELL *)v2;}

/* get the product of two values, returned in the first pointer */
void prod_c(void *v1, void *v2){*(CELL *)v1*=*(CELL *)v2;}
void prod_f(void *v1, void *v2){*(FCELL *)v1*=*(FCELL *)v2;}
void prod_d(void *v1, void *v2){*(DCELL *)v1*=*(DCELL *)v2;}

/* probably not a function of general interest */
/* calculate the slope between two cells, returned as a double  */
double slope_c(void *line1, void *line2, double cnst)
{
   double rc;
   CELL *pedge;

   rc=-HUGE_VAL;
   pedge=(CELL *)line2;
   if(!G_is_c_null_value(pedge))
   {
      rc=(*(CELL *)line1-*pedge)/cnst;
   }
   return rc;
}

double slope_f(void *line1, void *line2, double cnst)
{
   double rc;
   FCELL *pedge;

   rc=-HUGE_VAL;
   pedge=(FCELL *)line2;
   if(!G_is_f_null_value(pedge))
   {
      rc=(*(FCELL *)line1-*pedge)/cnst;
   }
   return rc;
}

double slope_d(void *line1, void *line2, double cnst)
{
   double rc;
   DCELL *pedge;

   rc=-HUGE_VAL;
   pedge=(DCELL *)line2;
   if(!G_is_d_null_value(pedge))
   {
      rc=(*(DCELL *)line1-*pedge)/cnst;
   }
   return rc;
}

/* read a line and update a three-line buffer */
/* moving forward through a file */
int advance_band3(int fh, struct band3 *bnd)
{
   int rc;
   void *hold;

   hold=bnd->b[0];
   bnd->b[0]=bnd->b[1];
   bnd->b[1]=bnd->b[2];
   bnd->b[2]=hold;
   if(fh==0)rc=0;
   else rc=read(fh,bnd->b[2],bnd->sz);
   return rc;
}

/* read a line and update a three-line buffer */
/* moving backward through a file */
int retreat_band3(int fh, struct band3 *bnd)
{
   int rc;
   void *hold;
   
   hold=bnd->b[2];
   bnd->b[2]=bnd->b[1];
   bnd->b[1]=bnd->b[0];
   bnd->b[0]=hold;
   if(fh==0)rc=0;
   else
   {
      rc=read(fh,bnd->b[0],bnd->sz);
      lseek(fh,-2*bnd->sz,SEEK_CUR);
   }
   return rc;
}
