#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <float.h>

#include "b+tree_udi.h"

static struct udi_control_block BtreeCB;

void udi_btree_init(void) {
	UdiControlBlock cb = &BtreeCB;

	memset((void *) cb,0, sizeof(*cb));

	cb->decl=YAP_LookupAtom(SPEC);

	cb->init=BtreeUdiInit;
	cb->insert=BtreeUdiInsert;
	cb->search=BtreeUdiSearch;
	cb->destroy=BtreeUdiDestroy;

	Yap_UdiRegister(cb);
}
void *BtreeUdiInit (YAP_Term spec, int arg, int arity)
{
	return (void *) BTreeNew();
}

void *BtreeUdiInsert (void *control,
		YAP_Term term, int arg, void *data)
{
  btree_t btree = (btree_t) control;
  assert(control);

  BTreeInsert(&btree,
              YAP_FloatOfTerm(YAP_ArgOfTerm(arg,term)),
              data);

  return (void *) btree;
}

/*ARGS ARE AVAILABLE*/
int BtreeUdiSearch (void *control,
		int arg, Yap_UdiCallback callback, void *args)
{
  int j;
  size_t n;
  YAP_Term Constraints;
  const char * att;

  YAP_Term t = YAP_A(arg);
  if (YAP_IsAttVar(t))
      {
        Constraints = YAP_AttsOfVar(t);
        /* Yap_DebugPlWrite(Constraints); */
        att = YAP_AtomName(YAP_NameOfFunctor(YAP_FunctorOfTerm(Constraints)));
 
        n = sizeof (att_func) / sizeof (struct Att);
        for (j = 0; j < n; j ++)
          if (strcmp(att_func[j].att,att) == 0) /*TODO: Improve this do not need strcmp*/
            return att_func[j].proc_att(control, Constraints, callback, args);
      }
  return -1; /*YAP FALLBACK*/
}

/*Needs to test if tree is not null*/
int BTreeMinAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  void *d;
  d = BTreeMin(tree,NULL,NULL);
  callback(d,d,args);
  /*TODO: test empty tree*/
  return 1;
}

int BTreeMaxAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
	void *d;
	d = BTreeMax(tree,NULL,NULL);
	callback(d,d,args);
	/*TODO: test empty tree*/
	return 1;
}

int BTreeEqAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double r;
  void * d;

  r = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));

  d = BTreeSearch(tree,r,EQ,&n,&i);
  if (d) {
	callback(d,d,args);
	return 1;
  }
  return 0;
}

int BTreeLtAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double max;
  void * d;
  int count = 0;

  max = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));

  d = BTreeMin(tree,&n,&i);
  if (d)
	  do {
		  callback(d,d,args);
		  count ++;
	  } while ((d = BTreeSearchNext(max,LT,&n,&i)));

  return count;
}

int BTreeLeAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double max;
  void * d;
  int count = 0;

  max = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));

  d = BTreeMin(tree,&n,&i);
  if (d)
	  do {
		  callback(d,d,args);
		  count ++;
	  } while ((d = BTreeSearchNext(max,LE,&n,&i)));

  return count;
}

int BTreeGtAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double min;
  void * d;
  int count = 0;

  min = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));

  d = BTreeSearch(tree,min,GT,&n,&i);
  if (d)
	  do {
		  callback(d,d,args);
		  count ++;
	  }  while ((d = BTreeSearchNext(DBL_MAX,LT,&n,&i)));

  return count;
}

int BTreeGeAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double min;
  void * d;
  int count = 0;

  min = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));

  d = BTreeSearch(tree,min,GE,&n,&i);
  if (d)
	  do {
	  		  callback(d,d,args);
	  		  count ++;
	  	  }  while ((d = BTreeSearchNext(DBL_MAX,LT,&n,&i)));

  return count;
}

int BTreeRangeAtt (btree_t tree, YAP_Term constraint, Yap_UdiCallback callback, void *args)
{
  node_t n;
  int i;
  double min,max;
  int minc,maxc;
  void * d;
  int count = 0;

  min = YAP_FloatOfTerm(YAP_ArgOfTerm(2,constraint));
  minc = strcmp(YAP_AtomName(YAP_AtomOfTerm(YAP_ArgOfTerm(3,constraint))),
                "true") == 0 ? GE: GT;
  max = YAP_FloatOfTerm(YAP_ArgOfTerm(4,constraint));
  maxc = strcmp(YAP_AtomName(YAP_AtomOfTerm(YAP_ArgOfTerm(5,constraint))),
                "true") == 0 ? LE: LT;

  d = BTreeSearch(tree,min,minc,&n,&i);
  if (d)
	  do {
		  callback(d,d,args);
		  count ++;
	  } while ((d = BTreeSearchNext(max,maxc,&n,&i)));

  return count;
}

int BtreeUdiDestroy(void *control)
{
	btree_t btree = (btree_t) control;

	assert(btree);

	BTreeDestroy(btree);

	return TRUE;
}