#include <stdlib.h>
#include "tree.h"
#include "rect.h"

#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)

void calc_p(point *points,     /* Calculate p_max and p_min */
	    point *nextpoint,  /* for new point `nextpoint' */
	    double gamma1,     /* and existing points in `points' */
	    double gamma2,
	    double w1,
	    double w2,
	    double h1,
	    double h2,
	    double *p_max,
	    double *p_min
	    )
{
  int n_max=0;   /* Number of points in max process */
  int n_min=0;   /* Number of points in min process */
  int i=0;
  rect new;
  rect *old;
  double extra_max1; /* m((x\oplus G_1)
			\setminus X_{\max}\oplus G_1) */
  double extra_max2; /* m((x\oplus G_2)
			\setminus X_{\max}\oplus G_2) */
  double extra_min1; /* m((x\oplus G_1)
			\setminus X_{\min}\oplus G_1) */
  double extra_min2; /* m((x\oplus G_2)
			\setminus X_{\min}\oplus G_2) */
  double mG1;         /* w1*h1 */
  double mG2;         /* w2*h2 */

  mG1=w1*h1;
  mG2=w2*h2;

  applyinorder(points,count,&n_max);   /* Count number of points in */
				       /* the max process */

  applyinorder(points,count_min,&n_min);  /* Count number of points */
					  /* in the min process */

  old=(rect *)calloc((n_max+2),sizeof(rect));  /* Allocate space */
					       /* according to number */
					       /* of points in max */
					       /* process */

/* First deal with w1,h1 rectangle. */
  new.bl.x=nextpoint->pos.x-w1;
  new.bl.y=nextpoint->pos.y-h1;    /* Set new to be w1,h1 rectangle */
  new.tr.x=nextpoint->pos.x+w1;     /* around new position */
  new.tr.y=nextpoint->pos.y+h1;

/* Max process first */
  makerectinorder(points,&(old[1]),&i,w1,h1); /* Make an array of */
					      /* rectangles from the */
					      /* points in the max */
					      /* process for w1, h1 */

  extra_max1=mG1+extra(&new,&(old[1]),n_max,-1); /* Find extra\_max1 */

/* Now min process */
  i=0;
  makerectinorder_min(points,&(old[1]),&i,w1,h1); /* Make an array of */
						  /* rectangles from */
						  /* the points in */
						  /* the min process */
						  /* for w1, h1 */

  extra_min1=mG1+extra(&new,&(old[1]),n_min,-1);

/* Now deal with w2,h2 rectangle.  Algorithm same as for w1,h1 rectangle */
  new.bl.x=nextpoint->pos.x-w2;
  new.bl.y=nextpoint->pos.y-h2;
  new.tr.x=nextpoint->pos.x+w2;
  new.tr.y=nextpoint->pos.y+h2;

  i=0;
  makerectinorder(points,&(old[1]),&i,w2,h2);
  extra_max2=mG2+extra(&new,&(old[1]),n_max,-1);

  i=0;
  makerectinorder_min(points,&(old[1]),&i,w2,h2);
  extra_min2=mG2+extra(&new,&(old[1]),n_min,-1);

  if((*old).bl.x != 0.0)
    exit(2);
  if((*old).bl.y != 0.0)
    exit(2);
  if((*old).tr.x != 0.0)
    exit(2);
  if((*old).tr.y != 0.0)
    exit(2);
  if(old[n_max+1].bl.x != 0.0)
    exit(2);
  if(old[n_max+1].bl.y != 0.0)
    exit(2);
  if(old[n_max+1].tr.x != 0.0)
    exit(2);
  if(old[n_max+1].tr.y != 0.0)
    exit(2);
  free(old);     /* Don't forget to free allocated memory! */

  *p_max=pow(gamma1,-extra_max1*100)*pow(gamma2,(mG2-extra_min2)*100);
  *p_min=pow(gamma1,-extra_min1*100)*pow(gamma2,(mG2-extra_max2)*100);
}

void count(point *p,void *arg) /* Use with applyinorder to count points */
{
  int *num;

  num=(int *) arg;
  (*num)++;
}

void count_min(point *p,void *arg) /* Use with applyinorder to count */
				   /* points in the minimum process */
{
  int *num;

  num=(int *) arg;
  if(p->min_life)
    (*num)++;
}

void makerectinorder(point *tree,  /* Traverses `tree' in order and */
		     rect *rects,  /* generates an array of `rect's */
		     int *counter, /* corresponding to the */
		     double width, /* rectangles centred at the */
		     double height /* points in `tree' with */
		     )             /* dimensions `width' by `height' */
{
  if(tree == NULL)
    return;
  makerectinorder(tree->left,rects,counter,width,height);
  rects[*counter].bl.x=tree->pos.x-width;
  rects[*counter].bl.y=tree->pos.y-height;
  rects[*counter].tr.x=tree->pos.x+width;
  rects[*counter].tr.y=tree->pos.y+height;
  ++(*counter);
  makerectinorder(tree->right,rects,counter,width,height);
}

void makerectinorder_min(point *tree,  /* Traverses `tree' in order */
			 rect *rects,  /* and generates an array of */
			 int *counter, /* `rect's corresponding to */
			 double width, /* the rectangles centred at */
			 double height /* the points in `tree' with */
			 )             /* dimensions `width' by */
				       /* `height' for those points */
				       /* which are in the min */
				       /* process */
{
  if(tree == NULL)
    return;
  makerectinorder_min(tree->left,rects,counter,width,height);
  if(tree->min_life){
    rects[*counter].bl.x=tree->pos.x-width;
    rects[*counter].bl.y=tree->pos.y-height;
    rects[*counter].tr.x=tree->pos.x+width;
    rects[*counter].tr.y=tree->pos.y+height;
    ++(*counter);
  }
  makerectinorder_min(tree->right,rects,counter,width,height);
}

double extra(rect *new,
	     rect old[],
	     int n_old,
	     int sign)
{
  int i;
  int n_olap=1;         /* Number of rectangles from old which */
			/* overlap with new */
  rect *over;           /* Array of rectangles overlapping with new */
  double area=0.0;      /* Area of overlap */

  if(n_old == 0)
    return 0.0;
  over=(rect *)calloc((n_old+2),sizeof(rect));

  for(i=1;i<=n_old;++i){
    /* If new and old[i] overlap store overlap-rectangle in 
       over[n_olap-1] */
    if(overlap(new,&(old[i]),&(over[n_olap])) > 0){
      area+=sign*(over[n_olap]).area;/* Add area of overlap-rectangle */
				     /* to `area', taking care to */
				     /* give it the correct sign. */
      ++n_olap;
    }
  }

/* For those rectangles which overlap with new */
  for(i=1; i<=n_olap-2; ++i)
/* Subtract the overlap between them from area so as not to count 
   regions more than once. */
    area+=extra(&(over[i]),&(over[i+1]),n_olap-i,-1*sign);

  if(over->bl.x != 0.0)
    exit(2);
  if(over->bl.y != 0.0)
    exit(2);
  if(over->tr.x != 0.0)
    exit(2);
  if(over->tr.y != 0.0)
    exit(2);
  if(over[n_old+1].bl.x != 0.0)
    exit(2);
  if(over[n_old+1].bl.y != 0.0)
    exit(2);
  if(over[n_old+1].tr.x != 0.0)
    exit(2);
  if(over[n_old+1].tr.y != 0.0)
    exit(2);
  free(over);
  return area;
}

/* Return value of 0 means that the rectangles passed were 
   non-overlapping */
double overlap(rect *A,
	       rect *B,
	       rect *OL)
{
  if(((A->bl.x <= B->tr.x) && (A->tr.x >= B->bl.x)) && /* If A and B */
						       /* overlap in */
						       /* the x */
						       /* direction */
     ((A->bl.y <= B->tr.y) && (A->tr.y >= B->bl.y))) { /* And A and B */
						       /* overlap */
						       /* in the y */
						       /* direction */
    OL->bl.x=max(A->bl.x,B->bl.x);
    OL->bl.y=max(A->bl.y,B->bl.y);
    OL->tr.x=min(A->tr.x,B->tr.x);
    OL->tr.y=min(A->tr.y,B->tr.y);
    OL->area=(OL->tr.x-OL->bl.x)*(OL->tr.y-OL->bl.y);
    return OL->area;
/* If the whole thing executed successfully return the area 
   of the overlap and also store this value in OL->area. */
  }
  else
    return(0.0);
}
