#include <stddef.h>	/* size_t, uintptr_t */
#include <stdint.h>	/* for uint8_t, uint32_t */

#include <string.h>
#include <stdlib.h>

#include "critbit.h"

typedef struct {
  void* child[2];
  uint32_t byte;
  uint8_t otherbits;
} critbit0_node;

#if 0
typedef struct{
  void* root;
} critbit0_tree;
#endif

int critbit0_contains(critbit0_tree* t,const char* u) {
  const uint8_t* ubytes= (void*)u;
  const size_t ulen= strlen(u);
  uint8_t* p= t->root;

  if (!p) return 0;

  while ((uintptr_t)p & 1) {
    critbit0_node* q = (void*)(p-1);

    uint8_t c = 0;
    if (q->byte<ulen)
      c = ubytes[q->byte];

    const int direction = (1+(q->otherbits|c))>>8;

    p = q->child[direction];
  }

  return 0==strcmp(u,(const char*)p);
}

int critbit0_insert(critbit0_tree* t,const char* u) {
  const uint8_t* const ubytes = (void*)u;
  const size_t ulen = strlen(u);
  uint8_t* p = t->root;

  if (!p) {
    char* x = malloc(ulen+1);
    if (!x) return 0;
    memcpy(x,u,ulen+1);
    t->root= x;
    return 2;
  }

  while (1&(intptr_t)p) {
    critbit0_node* q = (void*)(p-1);

    uint8_t c = 0;
    if (q->byte<ulen)
      c = ubytes[q->byte];
    const int direction = (1+(q->otherbits|c))>>8;

    p = q->child[direction];
  }

  uint32_t newbyte;
  uint32_t newotherbits;

  for (newbyte = 0; newbyte < ulen; ++newbyte) {
    if (p[newbyte] != ubytes[newbyte]) {
      newotherbits = p[newbyte]^ubytes[newbyte];
      goto different_byte_found;
    }
  }

  if (p[newbyte]!=0) {
    newotherbits = p[newbyte];
    goto different_byte_found;
  }
  return 1;

different_byte_found:

  newotherbits |= newotherbits>>1;
  newotherbits |= newotherbits>>2;
  newotherbits |= newotherbits>>4;
  newotherbits = (newotherbits&~(newotherbits>>1))^255;
  uint8_t c = p[newbyte];
  int newdirection = (1+(newotherbits|c))>>8;

  critbit0_node* newnode;
  if (!(newnode=malloc(sizeof(critbit0_node))))
    return 0;

  char* x;
  if (!(x = malloc(ulen+1))) {
    free(newnode);
    return 0;
  }
  memcpy(x,ubytes,ulen+1);

  newnode->byte= newbyte;
  newnode->otherbits= newotherbits;
  newnode->child[1-newdirection]= x;

  void** wherep= &t->root;
  for(;;) {
    uint8_t* p = *wherep;
    if (!((intptr_t)p&1))
      break;
    critbit0_node* q = (void*)(p-1);
    if (q->byte > newbyte)break;
    if (q->byte==newbyte && q->otherbits>newotherbits)break;
    uint8_t c = 0;
    if (q->byte<ulen)
      c = ubytes[q->byte];
    const int direction = (1+(q->otherbits|c))>>8;
    wherep = q->child+direction;
  }

  newnode->child[newdirection]= *wherep;
  *wherep= (void*)(1+(char*)newnode);	// gcc -fanalyze false positive

  return 2;
}

int critbit0_delete(critbit0_tree* t,const char* u) {
  const uint8_t* ubytes = (void*)u;
  const size_t ulen = strlen(u);
  uint8_t* p = t->root;
  void** wherep = &t->root;
  void** whereq = 0;
  critbit0_node* q = 0;
  int direction = 0;

  if (!p) return 0;

  while ((intptr_t)p&1) {
    whereq = wherep;
    q = (void*)(p-1);
    uint8_t c = 0;
    if (q->byte<ulen)
      c = ubytes[q->byte];
    direction = (1+(q->otherbits|c))>>8;
    wherep = q->child+direction;
    p = *wherep;
  }

  if (0!=strcmp(u,(const char*)p))
    return 0;
  free(p);

  if (!whereq) {
    t->root = 0;
    return 1;
  }

  *whereq = q->child[1-direction];
  free(q);

  return 1;
}

static void traverse(void* top) {
  uint8_t* p = top;

  if ((intptr_t)p&1) {
    critbit0_node* q = (void*)(p-1);
    traverse(q->child[0]);
    traverse(q->child[1]);
    free(q);
  } else {
    free(p);
  }
}

void critbit0_clear(critbit0_tree* t) {
  if (t->root)
    traverse(t->root);
  t->root = NULL;
}

static int allprefixed_traverse(uint8_t* top,int(*handle)(const char*,void*),void* arg) {
  if ((uintptr_t)top&1) {
    critbit0_node* q = (void*)(top-1);
    int direction;
    for (direction=0; direction<2; ++direction)
      switch (allprefixed_traverse(q->child[direction],handle,arg)) {
      case 1: break;
      case 0: return 0;
      default: return-1;
      }
    return 1;
  }

  return handle((const char*)top,arg);
}

int critbit0_allprefixed(critbit0_tree* t,const char* prefix,int(*handle)(const char*,void*),void* arg) {
  const uint8_t* ubytes = (void*)prefix;
  const size_t ulen = strlen(prefix);
  uint8_t* p = t->root;
  uint8_t* top = p;

  if (!p) return 1;

  while ((uintptr_t)p&1) {
    critbit0_node* q = (void*)(p-1);
    uint8_t c = 0;
    if (q->byte<ulen)
      c=ubytes[q->byte];
    const int direction = (1+(q->otherbits|c))>>8;
    p = q->child[direction];
    if (q->byte<ulen)
      top = p;
  }

  size_t i;
  for (i=0; i<ulen; ++i) {
    if (p[i]!=ubytes[i])
      return 1;
  }

  return allprefixed_traverse(top,handle,arg);
}

