#include "global.h"
#include "config.h"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif

#include "stralloc.h"
#include "interpret.h"

#include "whitefish.h"
#include "resultset.h"
#include "blob.h"
#include "buffer.h"

static INLINE int range( int n, int m )
{
  int o;
  int f;
  if( !m )
    m = 8;
  if( m >= 32*1024 )
  {
    if( n < (32*1024-1) )
      return 32*1024;
    return (n+1);
  }
  o = m;
  f = m+n;
  while( m<f ) m*=2;
  return m-o;
}

static void wf_buffer_make_space( struct buffer *b, unsigned int n )
{
#ifdef PIKE_DEBUG
  if( b->read_only )
    fatal("Oops\n");
#endif
  if( b->allocated_size-b->size < n )
  {
    b->allocated_size += range(n,b->allocated_size);
    b->data = realloc(b->data,b->allocated_size);
  }
}

void wf_buffer_wbyte( struct buffer *b,
		      unsigned char s )
{
  if( b->allocated_size == b->size )
    wf_buffer_make_space( b, 1 );
  b->data[b->size++] = s;
}

void wf_buffer_wshort( struct buffer *b,
		       unsigned short s )
{
  wf_buffer_make_space( b, 2 );
  b->data[b->size++] = (s>>8);
  b->data[b->size++] = (s)&255;
}

void wf_buffer_wint( struct buffer *b,
		      unsigned int s )
{
  s = htonl(s);
  wf_buffer_make_space( b, 4 );
  memcpy( b->data+b->size, &s, 4 );
  b->size += 4;
}

void wf_buffer_rewind_r( struct buffer *b, int n )
{
  if( n == -1 )
    b->rpos = 0;
  else if( b->rpos > (unsigned int)n )
    b->rpos -= n;
  else
    b->rpos = 0;
}

void wf_buffer_rewind_w( struct buffer *b, int n )
{
  if( n == -1 )
     b->size = 0;
  else if( b->size > (unsigned int)n )
    b->size -= n;
  else
    b->size = 0;
  if (b->size > b->rpos) b->rpos = b->size;
}

int wf_buffer_rbyte( struct buffer *b )
{
  if( b->rpos < b->size )
    return ((unsigned char *)b->data)[ b->rpos++ ];
  return 0;
}

unsigned int wf_buffer_rint( struct buffer *b )
{
  unsigned int res = wf_buffer_rbyte(b);
  res = res<<8 | wf_buffer_rbyte(b);
  res = res<<8 | wf_buffer_rbyte(b);
  return res<<8 | wf_buffer_rbyte(b);
}

int wf_buffer_memcpy( struct buffer *d,
		      struct buffer *s,
		      int nelems )
{
  if( (s->rpos+nelems) > s->size ) {
#ifdef PIKE_DEBUG
    Pike_fatal("wf_buffer_memcpy(%p, %p, %d): Attempt to copy more than source (%d).\n",
	       d, s, nelems, s->size-s->rpos);
#endif
    nelems = s->size-s->rpos;
  }
  if( nelems <= 0 )
    return 0;
  wf_buffer_make_space( d, nelems );
  memcpy( d->data+d->size, s->data+s->rpos, nelems );
  s->rpos += nelems;
  d->size += nelems;
  return nelems;
}

int wf_buffer_rshort( struct buffer *b )
{
  int res = wf_buffer_rbyte(b);
  return res<<8 | wf_buffer_rbyte(b);
}

int wf_buffer_eof( struct buffer *b )
{
  return b->rpos >= b->size;
}


void wf_buffer_seek( struct buffer *b, unsigned int pos )
{
  b->rpos = pos;
}

void wf_buffer_seek_w( struct buffer *b, unsigned int pos )
{
#ifdef PIKE_DEBUG
  if( b->read_only )
    fatal( "Oops, read_only\n");
#endif
  if( pos > b->size )
  {
    wf_buffer_make_space( b, (unsigned int)(pos-b->size) );
    memset( b->data+b->size, 0, (unsigned int)(pos-b->size) );
  }
  b->size = pos;
}
  
void wf_buffer_clear( struct buffer *b )
{
  if( !b->read_only && b->data )
    free( b->data );
  if( b->read_only && b->str )    free_string( b->str );
  memset(b, 0, sizeof(struct buffer));
}

void wf_buffer_free( struct buffer *b )
{
  wf_buffer_clear( b );
  free( b );
}


void wf_buffer_set_empty( struct buffer *b )
{
  wf_buffer_clear( b );
  b->data = xalloc( 16 );
  b->allocated_size = 16;
}

void wf_buffer_set_pike_string( struct buffer *b,
				struct pike_string *data,
				int read_only )
{
  wf_buffer_clear( b );
  if( read_only )
  {
    b->read_only = 1;
    b->str = data;
    add_ref(data);
    b->size = data->len;
    b->data = STR0(data);
  }
  else
  {
    b->size = data->len;
    b->data = malloc( b->size );
    b->allocated_size = b->size;
    memcpy( b->data, data->str, b->size );
  }
}

struct buffer *wf_buffer_new( )
{
  return xcalloc( 1, sizeof( struct buffer ) );
}

void wf_buffer_append( struct buffer *b,
		       unsigned char *data,
		       int size )
{
  wf_buffer_make_space( b, size );
  memcpy( b->data+b->size, data, size );
  b->size+=size;
}
