#if USE_MALLOC
#include <malloc.h>
#endif

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

#define NEED_newRV_noinc
#include "ppport.h"

#include "TreeNode.h"

/*

   Since an IV is large enough to hold a pointer (see <perlguts>), we
   use that to store the new node information.

*/

MODULE = Tree::Node PACKAGE = Tree::Node

PROTOTYPES: ENABLE

SV*
new(package, child_count)
    char *package
    int  child_count
  PROTOTYPE: $$
  CODE:
    Node* self = new(child_count);
    SV*   n    = newSViv((IV) self);
    RETVAL     = newRV_noinc(n);
    sv_bless(RETVAL, gv_stashpv(package, 0));
    SvREADONLY_on(n);
    while (child_count--)
      self->next[child_count] = &PL_sv_undef;
  OUTPUT:
    RETVAL

IV
to_p_node(n)
    SV* n
  PROTOTYPE: $
  CODE:
    RETVAL = (IV) SV2NODE(n);
  OUTPUT:
    RETVAL

IV
p_new(child_count)
    int  child_count
  PROTOTYPE: $$
  CODE:
    Node* self = new(child_count);
    while (child_count--)
      self->next[child_count] = NULL;
    RETVAL = (IV) self;
  OUTPUT:
    RETVAL

void
DESTROY(n)
    SV* n
  PROTOTYPE: $
  CODE:
    Node* self = SV2NODE(n);
    int child_count = self->child_count;
    while (child_count--)
      SvREFCNT_dec(self->next[child_count]);
    DESTROY(self);

void
p_destroy(self)
    IV self
  PROTOTYPE: $
  CODE:
    if (self) DESTROY(IV2NODE(self));

int
MAX_LEVEL()
  PROTOTYPE:
  CODE:
    RETVAL = MAX_LEVEL;
  OUTPUT:
    RETVAL

int
_allocated_by_child_count(count)
    int count
  PROTOTYPE: $
  CODE:
    RETVAL = NODESIZE(count);
  OUTPUT:
    RETVAL

int
_allocated(n)
    SV* n
  PROTOTYPE: $
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = _allocated(self);
  OUTPUT:
    RETVAL

int
p_allocated(n)
    IV n
  PROTOTYPE: $
  CODE:
    RETVAL = _allocated(IV2NODE(n));
  OUTPUT:
    RETVAL


void
add_children(n, ...)
    SV* n
  ALIAS:
    add_children_left = 1
  PROTOTYPE: $;@
  PREINIT:
    int num = 1;
  CODE:
    Node* back;
    Node* self = SV2NODE(n);
    int   count = self->child_count;
    int   i;

    num = items-1;

    if (num<1)
      croak("number of children to add must be >= 1");
    if ((count+num) > MAX_LEVEL)
      croak("cannot %d children: we already have %d children", num, count);

    back = self;
#if USE_MALLOC
    self = realloc(self, (size_t) NODESIZE(count+num));
    if (self==NULL)
      croak("unable to allocate additional memory");
#else
    Renewc(self, NODESIZE(count+num), char, Node);
#endif

    if (self != back) {
      SvREADONLY_off((SV*)SvRV(n));
      sv_setiv((SV*) SvRV(n), (IV) self);
      SvREADONLY_on((SV*)SvRV(n));
    }

    self->child_count += num;

    if (ix==0) {
      for(i=0; i<num; i++)
        self->next[count+i] = newSVsv(ST(i+1));
    }
    else if (ix==1) {
      for(i=(count-1); i>=0; i--)
        self->next[i+num] = self->next[i];
      for(i=0; i<num; i++)
        self->next[i] = newSVsv(ST(i+1));
    }

int
child_count(n)
    SV* n
  PROTOTYPE: $
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = child_count(self);
  OUTPUT:
    RETVAL

int
p_child_count(self)
    IV self;
  PROTOTYPE: $
  CODE:
    RETVAL = child_count(IV2NODE(self));
  OUTPUT:
    RETVAL

void
get_children(n)
    SV* n
  PROTOTYPE: $
  PREINIT:
    int i;
  PPCODE:
    Node* self = SV2NODE(n);
    EXTEND(SP, self->child_count);
    for (i = 0; i < self->child_count; i++)
      PUSHs(get_child(self, i));

SV*
get_child(n, index)
    SV* n
    int index
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = get_child(self, index);
  OUTPUT:
    RETVAL

IV
p_get_child(n, index);
    IV n;
    int index
  PROTOTYPE: $$
  CODE:
    Node* self = IV2NODE(n);
    if ((index >= self->child_count) || (index < 0))
      croak("index out of bounds: must be between [0..%d]", self->child_count-1);
    RETVAL = (IV) self->next[index];
  OUTPUT:
    RETVAL

IV
p_get_child_or_null(n, index);
    IV n;
    int index
  PROTOTYPE: $$
  CODE:
    Node* self = IV2NODE(n);
    if ((index >= self->child_count) || (index < 0))
      RETVAL = (IV) NULL;
    else
      RETVAL = (IV) self->next[index];
  OUTPUT:
    RETVAL


SV*
get_child_or_undef(n, index)
    SV* n
    int index
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = get_child_or_undef(self, index);
  OUTPUT:
    RETVAL

void
set_child(n, index, t)
    SV* n
    int index
    SV* t
  PROTOTYPE: $$$
  CODE:
    Node* self = SV2NODE(n);
    set_child(self, index, t);

void
p_set_child(n, index, t)
    IV n
    int index
    IV t
  PROTOTYPE: $$$
  CODE:
    Node* self = IV2NODE(n);
    if ((index >= self->child_count) || (index < 0))
      croak("index out of bounds: must be between [0..%d]", self->child_count-1);
    self->next[index] = (SV*) t;

void
set_key(n, k)
    SV* n
    SV* k
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    set_key(self, k);

void
force_set_key(n, k)
    SV* n
    SV* k
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    force_set_key(self, k);

void
p_set_key(n, k)
    IV n
    SV* k
  PROTOTYPE: $$
  CODE:
    set_key(IV2NODE(n), k);

void
p_force_set_key(n, k)
    IV n
    SV* k
  PROTOTYPE: $$
  CODE:
    force_set_key(IV2NODE(n), k);

SV*
key(n)
    SV* n
  PROTOTYPE: $
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = get_key(self);
  OUTPUT:
    RETVAL

SV*
p_get_key(n)
    IV n
  PROTOTYPE: $
  CODE:
    RETVAL = get_key(IV2NODE(n));
  OUTPUT:
    RETVAL

I32
p_key_cmp(n, k)
    IV n
    SV* k
  PROTOTYPE: $$
  CODE:
    RETVAL = key_cmp(IV2NODE(n), k);
  OUTPUT:
    RETVAL

I32
key_cmp(n, k)
    SV* n
    SV* k
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = key_cmp(self, k);
  OUTPUT:
    RETVAL

void
set_value(n, v)
    SV* n
    SV* v
  PROTOTYPE: $$
  CODE:
    Node* self = SV2NODE(n);
    set_value(self, v);

void
p_set_value(n, v)
    IV n
    SV* v
  PROTOTYPE: $$
  CODE:
    set_value(IV2NODE(n), v);

SV*
value(n)
    SV* n
  PROTOTYPE: $
  CODE:
    Node* self = SV2NODE(n);
    RETVAL = get_value(self);
  OUTPUT:
    RETVAL

SV*
p_get_value(n)
    IV n
  PROTOTYPE: $
  CODE:
    RETVAL = get_value(IV2NODE(n));
  OUTPUT:
    RETVAL