#include "be_mapping.h"
#include <string.h>
#include <math.h>

/*********************************************************************************************\
 * Helper functions to create a map with single line calls
\*********************************************************************************************/
/* Insert an nil to a key */
void be_map_insert_nil(bvm *vm, const char *key)
{
  if (be_ismap(vm, -1)) {
    be_pushstring(vm, key);
    be_pushnil(vm);
    be_data_insert(vm, -3);
    be_pop(vm, 2);
  }
}
/* Insert an int to a key */
// On stack is either:
// Case 1;  (-2) map instance, (-1) map
// Case 2;  (-2) nil, (-1) string -> if key matches then update (-2)
void be_map_insert_int(bvm *vm, const char *key, bint value)
{
  if (be_ismap(vm, -1)) {
    be_pushstring(vm, key);
    be_pushint(vm, value);
    be_data_insert(vm, -3);
    be_pop(vm, 2);
  } else if (be_isstring(vm, -1)) {
    const char * needle = be_tostring(vm, -1);
    if (strcmp(key, needle) == 0) {
      be_pushint(vm, value);
      be_moveto(vm, -1, -3);
      be_pop(vm, 1);
    }
  }
}
/* Insert an bbool to a key */
void be_map_insert_bool(bvm *vm, const char *key, bbool value)
{
  if (be_ismap(vm, -1)) {
    be_pushstring(vm, key);
    be_pushbool(vm, value);
    be_data_insert(vm, -3);
    be_pop(vm, 2);
  } else if (be_isstring(vm, -1)) {
    const char * needle = be_tostring(vm, -1);
    if (strcmp(key, needle) == 0) {
      be_pushbool(vm, value);
      be_moveto(vm, -1, -3);
      be_pop(vm, 1);
    }
  }
}
/* Insert an real to a key */
/* if value == NAN, ignore */
void be_map_insert_real(bvm *vm, const char *key, breal value)
{
  if (be_ismap(vm, -1)) {
    if (!isnan(value)) {
      be_pushstring(vm, key);
      be_pushreal(vm, value);
      be_data_insert(vm, -3);
      be_pop(vm, 2);
    }
  } else if (be_isstring(vm, -1)) {
    const char * needle = be_tostring(vm, -1);
    if (strcmp(key, needle) == 0) {
      be_pushreal(vm, value);
      be_moveto(vm, -1, -3);
      be_pop(vm, 1);
    }
  }
}
/* Insert an C string to a key */
void be_map_insert_str(bvm *vm, const char *key, const char *value)
{
  if (be_ismap(vm, -1)) {
    be_pushstring(vm, key);
    be_pushstring(vm, value);
    be_data_insert(vm, -3);
    be_pop(vm, 2);
  } else if (be_isstring(vm, -1)) {
    const char * needle = be_tostring(vm, -1);
    if (strcmp(key, needle) == 0) {
      be_pushstring(vm, value);
      be_moveto(vm, -1, -3);
      be_pop(vm, 1);
    }
  }
}
/* Insert list of bytes as individual integers to a key */
void be_map_insert_list_uint8(bvm *vm, const char *key, const uint8_t *value, size_t size)
{
  if (be_ismap(vm, -1)) {
    be_pushstring(vm, key);

    be_newobject(vm, "list");
    for (uint32_t i=0; i < size; i++) {
      be_pushint(vm, value[i]);
      be_data_push(vm, -2);
      be_pop(vm, 1);
    }
    be_pop(vm, 1);                  // now list is on top

    be_data_insert(vm, -3);         // insert into map, key/value
    be_pop(vm, 2);                  // pop both key and value
  }
}

/*********************************************************************************************\
 * Binary search for dynamic attributes
 * 
 * Names need to be sorted
\*********************************************************************************************/
// binary search within an array of sorted strings
// the first 4 bytes are a pointer to a string
// returns 0..total_elements-1 or -1 if not found
//
// This version skips the first character of the string if it's not a letter,
// the first character is used to indicate the type of the value associated to the key
int be_map_bin_search(const char * needle, const void * table, size_t elt_size, size_t total_elements) {
  int low = 0;
  int high = total_elements - 1;
  int mid = (low + high) / 2;
  // start a dissect
  while (low <= high) {
    const char * elt = *(const char **) ( ((uint8_t*)table) + mid * elt_size );
    char first_char = elt[0];
    if ( !(first_char >= 'a' && first_char <='z') && !(first_char >= 'A' && first_char <='Z') ) {
      elt++;  // skip first char
    }
    int comp = strcmp(needle, elt);
    if (comp < 0) {
      high = mid - 1;
    } else if (comp > 0) {
      low = mid + 1;
    } else {
      break;
    }
    mid = (low + high) / 2;
  }
  if (low <= high) {
    return mid;
  } else {
    return -1;
  }
}