#include "bytestring.h"
#include <assert.h>
#include <string.h>

int bsvector_init(bsvector_t *sv, long int len) {
  long int i;
  sv->data=igraph_Calloc(len, bytestring_t*);
  if (sv->data==0) {
    IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM);
  }
  for (i=0; i<len; i++) {
    sv->data[i]=new_bytestring(0);
    if (sv->data[i]==0) {
      bsvector_destroy(sv);
      IGRAPH_ERROR("bsvector init failed", IGRAPH_ENOMEM);
    }
  }
  sv->len=len;

  return 0;
}

void bsvector_destroy(bsvector_t *sv) {
  long int i;
  assert(sv != 0);
  if (sv->data != 0) {
    for (i=0; i<sv->len; i++) {
      if (sv->data[i] != 0) {
        destroy_bytestring(sv->data[i]);
      }
    }
    igraph_Free(sv->data);
  }
}

void bsvector_get(const bsvector_t *sv, long int idx, bytestring_t **value) {
  assert(sv != 0);
  assert(sv->data != 0);
  assert(sv->data[idx] != 0);
  *value = sv->data[idx];
}

int bsvector_set(bsvector_t *sv, long int idx, const bytestring_t *value) {
  assert(sv != 0);
  assert(sv->data != 0);

  if (sv->data[idx] != 0) {
    destroy_bytestring(sv->data[idx]);
  }
  sv->data[idx] = new_bytestring(value->len);

  memcpy(sv->data[idx]->value, value->value, value->len * sizeof(char));

  return 0;
}

void bsvector_remove_section(bsvector_t *v, long int from, long int to) {
  long int i;

  assert(v != 0);
  assert(v->data != 0);

  for (i=from; i<to; i++) {
    if (v->data[i] != 0) {
      destroy_bytestring(v->data[i]);
    }
  }
  for (i=0; i<v->len-to; i++) {
    v->data[from+i]=v->data[to+i];
  }

  v->len -= (to-from);
}

void bsvector_remove(bsvector_t *v, long int elem) {
  assert(v != 0);
  assert(v->data != 0);
  bsvector_remove_section(v, elem, elem+1);
}

/*
void bsvector_move_interval(bsvector_t *v, long int begin,
				   long int end, long int to) {
  long int i;
  assert(v != 0);
  assert(v->data != 0);
  for (i=to; i<to+end-begin; i++) {
    if (v->data[i] != 0) {
      destroy_bytestring(v->data[i]);
    }
  }
  for (i=0; i<end-begin; i++) {
    if (v->data[begin+i] != 0) {
      size_t len=strlen(v->data[begin+i])+1;
      v->data[to+i]=igraph_Calloc(len, char);
      memcpy(v->data[to+i], v->data[begin+i], sizeof(char)*len);
    }
  }
}
*/

int bsvector_copy(bsvector_t *to, const bsvector_t *from) {
  long int i;
  bytestring_t *str;
  assert(from != 0);
/*   assert(from->data != 0); */
  to->data=igraph_Calloc(from->len, bytestring_t*);
  if (to->data==0) {
    IGRAPH_ERROR("Cannot copy string vector", IGRAPH_ENOMEM);
  }
  to->len=from->len;

  for (i=0; i<from->len; i++) {
    int ret;
    bsvector_get(from, i, &str);
    ret=bsvector_set(to, i, str);
    if (ret != 0) {
      bsvector_destroy(to);
      IGRAPH_ERROR("cannot copy string vector", ret);
    }
  }

  return 0;
}

int bsvector_append(bsvector_t *to, const bsvector_t *from) {
  long int len1=bsvector_size(to), len2=bsvector_size(from);
  long int i;
  igraph_bool_t error=0;
  IGRAPH_CHECK(bsvector_resize(to, len1+len2));
  for (i=0; i<len2; i++) {
    if (from->data[i]->len > 0) {
      bsvector_set(to, len1+i, from->data[i]);
      if (!to->data[len1+i]) {
        error=1;
        break;
      }
    }
  }
  if (error) {
    bsvector_resize(to, len1);
    IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM);
  }
  return 0;
}

void bsvector_clear(bsvector_t *sv) {
  long int i, n=bsvector_size(sv);
  bytestring_t **tmp;

  for (i=0; i<n; i++) {
    destroy_bytestring(sv->data[i]);
  }
  sv->len=0;
  /* try to give back some memory */
  tmp=igraph_Realloc(sv->data, 1, bytestring_t*);
  if (tmp != 0) {
    sv->data=tmp;
  }
}

int bsvector_resize(bsvector_t* v, long int newsize) {
  long int toadd = newsize - v->len, i, j;
  bytestring_t **tmp;
  long int reallocsize=newsize;
  if (reallocsize==0) { reallocsize=1; }

  assert(v != 0);
  assert(v->data != 0);
  if (newsize < v->len) {
    for (i=newsize; i<v->len; i++) {
      destroy_bytestring(v->data[i]);
    }
    /* try to give back some space */
    tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*);
    if (tmp != 0) {
      v->data=tmp;
    }
  } else if (newsize > v->len) {
    igraph_bool_t error=0;
    tmp=igraph_Realloc(v->data, (size_t) reallocsize, bytestring_t*);
    if (tmp==0) {
      IGRAPH_ERROR("cannot resize string vector", IGRAPH_ENOMEM);
    }
    v->data = tmp;

    for (i=0; i<toadd; i++) {
      v->data[v->len+i] = new_bytestring(0);
      if (v->data[v->len+i] == 0) {
	       error=1;
	       break;
      }
    }
    if (error) {
      /* There was an error, free everything we've allocated so far */
      for (j=0; j<i; j++) {
	       if (v->data[v->len+i] != 0) {
	          destroy_bytestring(v->data[v->len+i]);
	       }
      }
      /* Try to give back space */
      tmp=igraph_Realloc(v->data, (size_t) (v->len), bytestring_t*);
      if (tmp != 0) {
	       v->data=tmp;
      }
      IGRAPH_ERROR("Cannot resize string vector", IGRAPH_ENOMEM);
    }
  }
  v->len = newsize;

  return 0;
}

int bsvector_add(bsvector_t *v, const bytestring_t *value) {
  long int s=bsvector_size(v);
  bytestring_t **tmp;
  assert(v != 0);
  assert(v->data != 0);
  tmp=igraph_Realloc(v->data, (size_t) s+1, bytestring_t*);
  if (tmp == 0) {
    IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
  }
  v->data=tmp;
  v->data[s]=new_bytestring(value->len);
  if (v->data[s]==0) {
    IGRAPH_ERROR("cannot add string to string vector", IGRAPH_ENOMEM);
  }
  bsvector_set(v, s, value);
  v->len += 1;

  return 0;
}

/**
 * \ingroup strvector
 * \function igraph_strvector_permdelete
 * \brief Removes elements from a string vector (for internal use)
 */

void bsvector_permdelete(bsvector_t *v, const igraph_vector_t *index,
				long int nremove) {
  long int i;
  bytestring_t **tmp;
  assert(v != 0);
  assert(v->data != 0);

  for (i=0; i<bsvector_size(v); i++) {
    if (VECTOR(*index)[i] != 0) {
      v->data[ (long int) VECTOR(*index)[i]-1 ] = v->data[i];
    } else {
      destroy_bytestring(v->data[i]);
    }
  }
  /* Try to make it shorter */
  tmp=igraph_Realloc(v->data, v->len-nremove ?
		     (size_t) (v->len-nremove) : 1, bytestring_t*);
  if (tmp != 0) {
    v->data=tmp;
  }
  v->len -= nremove;
}

/**
 * \ingroup strvector
 * \function igraph_strvector_remove_negidx
 * \brief Removes elements from a string vector (for internal use)
 */

void bsvector_remove_negidx(bsvector_t *v, const igraph_vector_t *neg,
				   long int nremove) {
  long int i, idx=0;
  bytestring_t **tmp;
  assert(v != 0);
  assert(v->data != 0);
  for (i=0; i<bsvector_size(v); i++) {
    if (VECTOR(*neg)[i] >= 0) {
      v->data[idx++] = v->data[i];
    } else {
      destroy_bytestring(v->data[i]);
    }
  }
  /* Try to give back some memory */
  tmp=igraph_Realloc(v->data, v->len-nremove ?
		     (size_t) (v->len-nremove) : 1, bytestring_t*);
  if (tmp != 0) {
    v->data=tmp;
  }
  v->len -= nremove;
}

int bsvector_index(const bsvector_t *v, bsvector_t *newv,
                   const igraph_vector_t *idx) {
  long int i, newlen=igraph_vector_size(idx);
  IGRAPH_CHECK(bsvector_resize(newv, newlen));

  for (i=0; i<newlen; i++) {
    long int j=(long int) VECTOR(*idx)[i];
    bytestring_t *str;
    bsvector_get(v, j, &str);
    bsvector_set(newv, i, str);
  }

  return 0;
}

long int bsvector_size(const bsvector_t *sv) {
  assert(sv != 0);
  return sv->len;
}

bytestring_t* new_bytestring(int n) {
    bytestring_t *newstr = igraph_Calloc(1, bytestring_t);
    char *str = igraph_Calloc(n, char);
    newstr->len = n;
    newstr->value = str;
    return newstr;
}

void destroy_bytestring(bytestring_t* str) {
  if (str != NULL) {
    free(str->value);
    free(str);
  }
}

char* bytestring_to_char(bytestring_t* from) {
  char *str = igraph_Calloc(from->len + sizeof(long int), char);
  memcpy(str, &from->len, sizeof(long int));
  memcpy(str + sizeof(long int), from->value, from->len);
  return str;
}

bytestring_t* char_to_bytestring(char* from) {
  unsigned long int *n;
  memcpy(n, from, sizeof(long int));
  bytestring_t *str = new_bytestring(*n);
  memcpy(str->value, from+sizeof(long int), *n);
  return str;
}

igraph_strvector_t* bsvector_to_strvector(bsvector_t* from) {
  igraph_strvector_t *str;
  size_t i;
  igraph_strvector_init(str, from->len);
  for (i = 0; i < from->len; i++) {
    igraph_strvector_set(str, i, bytestring_to_char(from->data[i]));
  }
  return str;
}

bsvector_t* strvector_to_bsvector(igraph_strvector_t* from) {
  bsvector_t *str;
  size_t i;
  bsvector_init(str, from->len);
  for (i = 0; i < from->len; i++) {
    bsvector_set(str, i, char_to_bytestring(from->data[i]));
  }
  return str;
}