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

#include "tboxlibs/flex.h"

#include "pinboard.h"
#include "indirected.h"
#include "msgtrans.h"
#include "debug.h"

char *indirect_store=NULL;
static int indirect_size=0;

#define align(a) ((a)+3 &~3)

void indirected_init(void)
{
  if (!flex_alloc((flex_ptr)&indirect_store,indirect_size)) {
    // fatal
    msgtrans_report_error("MemFatal");
    exit(1);
  }
}

// return new offset or -1 if error (as 0 is a valid offset)
int indirected_add_fromflex(flex_ptr block,int length)
{
  int size=sizeof(_indirected)+align(length);
  _indirected *newblock;
  
  if (!flex_extend((flex_ptr)&indirect_store,indirect_size+size)) {
    msgtrans_report_error("NoMem");
    return -1;
  }
  
  newblock=((_indirected *)((char *)indirect_store+indirect_size));
  newblock->length=length;
  memcpy(newblock->data,(char *)*block,length);
  
  indirect_size+=size;
  
  return indirect_size-size;
}

int indirected_add(void *data,int length)
{
  int size=sizeof(_indirected)+align(length);
  _indirected *newblock;
  
  if (!flex_extend((flex_ptr)&indirect_store,indirect_size+size)) {
    msgtrans_report_error("NoMem");
    return -1;
  }
  
  newblock=((_indirected *)((char *)indirect_store+indirect_size));
  newblock->length=length;
  if ((int *)data!=NULL) memcpy(newblock->data,data,length);
  
  indirect_size+=size;
  
  return indirect_size-size;
}

/* return 0 if error, or 1 if OK */
int indirected_remove(int offset)
{
  _indirected *ptr=(_indirected *)((char *)indirect_store+offset);
  int oldlength=align(ptr->length);
  
  if (offset>indirect_size || offset<0) return 0;
  
  // remove ptr->length bytes
  if (!flex_midextend((flex_ptr)&indirect_store,offset+oldlength,-oldlength)) {
    msgtrans_report_error("MemErr");
    return 0;
  }
  
  // done :)
  pin_update_offsets(offset,-oldlength);
  return 1;
}

/* return 0 if error, or 1 if ok */
int indirected_update(int offset,void *data,int length)
{
  _indirected *ptr=(_indirected *)((char *)indirect_store+offset);
  int delta=align(length)-align(ptr->length);
  if (delta==0) {
    if (data) memcpy(ptr->data,data,length);
    return 1; // nothing to do re: movement, just copy
  }
  

  // if shrinking, shrink from top of block as deletes backwards
  if (!flex_midextend((flex_ptr)&indirect_store,offset+(delta<0?-delta:0),delta)) {
    msgtrans_report_error("NoMem");
    return 0;
  }

  ptr->length=length;
  if (data) memcpy(ptr->data,data,length);
  
  pin_update_offsets(offset,delta);
  
  return 1;
}
