#include "btree_gist.h"
#include "btree_utils_num.h"
#include "utils/date.h"

extern GISTENTRY *
gbt_num_compress( GISTENTRY  *retval , GISTENTRY  *entry , const gbtree_ninfo * tinfo )
{

    if (entry->leafkey)
    {

       union {
         int16      i2;
         int32      i4;
         TimeADT    ts;
         DateADT    dt;
       } v ;
       
       GBT_NUMKEY  *r  = ( GBT_NUMKEY * ) palloc(2 * tinfo->size );
       void  *leaf  = NULL;

       switch ( tinfo->t )
       {
         case gbt_t_int2 :
           v.i2     = DatumGetInt16(entry->key);
           leaf     = &v.i2;
           break;
         case gbt_t_int4 :
           v.i4     = DatumGetInt32(entry->key);
           leaf     = &v.i4;
           break;
         case gbt_t_oid  :
           v.i4     = DatumGetObjectId(entry->key);
           leaf     = &v.i4;
           break;
         case gbt_t_time  :
           v.ts     = DatumGetTimeADT(entry->key);
           leaf     = &v.ts;
           break;
         case gbt_t_date  :
           v.dt     = DatumGetDateADT(entry->key);
           leaf     = &v.dt;
           break;
         default :
           leaf = DatumGetPointer(entry->key);
       }

       memset ( (void*) &r[0]  , 0   , 2*tinfo->size );
       memcpy ( (void*) &r[0]  , leaf, tinfo->size ); 
       memcpy ( (void*) &r[tinfo->size] , leaf, tinfo->size );
       retval = palloc(sizeof(GISTENTRY));
       gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page,
                                 entry->offset,( 2 * tinfo->size ), FALSE);
    } else
       retval = entry;

    return retval;
}




/*
** The GiST union method for numerical values
*/

extern void *
gbt_num_union( GBT_NUMKEY * out, const GistEntryVector * entryvec, const gbtree_ninfo * tinfo )
{
        int                     i,
                                numranges;
        GBT_NUMKEY *   cur ;
        GBT_NUMKEY_R   o, c;

        numranges = entryvec->n;
        cur       = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[0].key));


        o.lower = &((GBT_NUMKEY *)out)[0];
        o.upper = &((GBT_NUMKEY *)out)[tinfo->size];

        memcpy( (void*)out, (void*) cur, 2*tinfo->size );

        for (i = 1; i < numranges; i++)
        {
                cur = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key));
                c.lower = &cur[0];
                c.upper = &cur[tinfo->size];
                if ( (*tinfo->f_gt)(o.lower, c.lower) ) /* out->lower > cur->lower */
                     memcpy( (void* ) o.lower, (void*) c.lower, tinfo->size );
                if ( (*tinfo->f_lt)(o.upper, c.upper) ) /* out->upper < cur->upper */
                     memcpy( (void*) o.upper, (void*) c.upper, tinfo->size );
        }

        return out;
}



/*
** The GiST same method for numerical values
*/

extern bool gbt_num_same ( const GBT_NUMKEY * a, const GBT_NUMKEY * b, const gbtree_ninfo * tinfo )
{

  GBT_NUMKEY_R b1, b2 ;

  b1.lower = &(((GBT_NUMKEY *)a)[0]);
  b1.upper = &(((GBT_NUMKEY *)a)[tinfo->size]);
  b2.lower = &(((GBT_NUMKEY *)b)[0]);
  b2.upper = &(((GBT_NUMKEY *)b)[tinfo->size]);

  if (
    (*tinfo->f_eq)( b1.lower, b2.lower) &&
    (*tinfo->f_eq)( b1.upper, b2.upper)
  )
    return TRUE;
  return FALSE;
 
}


extern void
gbt_num_bin_union(Datum * u , GBT_NUMKEY * e , const gbtree_ninfo * tinfo )
{

  GBT_NUMKEY_R rd;

  rd.lower = &e[0];
  rd.upper = &e[tinfo->size];

  if (!DatumGetPointer(*u))
  {
    *u  = PointerGetDatum(palloc(2 * tinfo->size));
    memcpy( (void* ) &( ( (GBT_NUMKEY *) DatumGetPointer(*u) )[0] ) , (void*)rd.lower , tinfo->size );
    memcpy( (void* ) &( ( (GBT_NUMKEY *) DatumGetPointer(*u) )[tinfo->size]) , (void*)rd.upper , tinfo->size );
  }
  else
  {
    GBT_NUMKEY_R  ur ;
    ur.lower   = &( ( (GBT_NUMKEY *) DatumGetPointer(*u) )[0] ) ;
    ur.upper   = &( ( (GBT_NUMKEY *) DatumGetPointer(*u) )[tinfo->size]) ;
    if ( (*tinfo->f_gt)((void*)ur.lower, (void*)rd.lower) )
       memcpy( (void*) ur.lower, (void*) rd.lower, tinfo->size );
    if ( (*tinfo->f_lt)((void*)ur.upper, (void*)rd.upper) )
       memcpy( (void*) ur.upper, (void*) rd.upper, tinfo->size );
  }
}



/*
** The GiST consistent method
*/

extern bool  
gbt_num_consistent(
  const GBT_NUMKEY_R * key,
  const void         * query,
  const StrategyNumber * strategy,
  bool is_leaf,
  const gbtree_ninfo * tinfo
)
{

        bool    retval = FALSE;

        switch (*strategy)
        {
                case BTLessEqualStrategyNumber:
                        retval    = (*tinfo->f_ge)(query, key->lower);
                        break;
                case BTLessStrategyNumber:
                        if ( is_leaf )
                          retval  = (*tinfo->f_gt)(query, key->lower);
                        else 
                          retval  = (*tinfo->f_ge)(query, key->lower);
                        break;
                case BTEqualStrategyNumber:
                        if ( is_leaf )
                           retval = (*tinfo->f_eq)(query, key->lower);
                        else
                           retval = (*tinfo->f_le)(key->lower, query) && (*tinfo->f_le)(query, key->upper );
                        break;
                case BTGreaterStrategyNumber:
                        if ( is_leaf )
                          retval  = (*tinfo->f_lt)(query, key->upper);
                        else
                          retval  = (*tinfo->f_le)(query, key->upper);
                        break;
                case BTGreaterEqualStrategyNumber:
                        retval    = (*tinfo->f_le)(query, key->upper);
                        break;
                default:
                        retval = FALSE;
        }

        return (retval);
}




GIST_SPLITVEC *
gbt_num_picksplit( const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtree_ninfo * tinfo )
{

    OffsetNumber  i   ,

    maxoff    = entryvec->n - 1;

    Nsrt     arr[maxoff+1]  ;
    int       nbytes  ;   

    nbytes        = (maxoff + 2) * sizeof(OffsetNumber);
    v->spl_left   = (OffsetNumber *) palloc(nbytes);
    v->spl_right  = (OffsetNumber *) palloc(nbytes);   
    v->spl_ldatum = PointerGetDatum(0);
    v->spl_rdatum = PointerGetDatum(0);
    v->spl_nleft  = 0;
    v->spl_nright = 0; 

    /* Sort entries */

    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
    {
      arr[i].t  = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key));
      arr[i].i = i;
    }
    qsort ( (void*) &arr[FirstOffsetNumber], maxoff-FirstOffsetNumber+1,sizeof(Nsrt), tinfo->f_cmp );

    /* We do simply create two parts */

    for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
    {
      if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
      {
        gbt_num_bin_union(&v->spl_ldatum, arr[i].t, tinfo);
        v->spl_left[v->spl_nleft]   = arr[i].i;
        v->spl_nleft++;
      }
      else
      {
        gbt_num_bin_union(&v->spl_rdatum, arr[i].t, tinfo);
        v->spl_right[v->spl_nright]   = arr[i].i;
        v->spl_nright++;
      }
    }  

    return v;

}