/** \file buff.c
 * implementation of the buff type, a buffer for arbitrary strings
 * and actually a superset of word_t
 *
 * \author David Relson <relson@osagesoftware.com>
 * \author Matthias Andree <matthias.andree@gmx.de> (buff_fgetsl)
 */

#include "common.h"

#include <stdlib.h>

#include "buff.h"
#include "fgetsl.h"
#include "xmalloc.h"

#define BOGO_ASSERT(expr, msg) if (!(expr)) { fprintf(stderr, "%s: %s:%d %s\n", progname, __FILE__, __LINE__, msg); abort(); }

/* Function Definitions */
buff_t *buff_init(buff_t *self, byte *buff, uint used, uint size)
{
    self->t.u.text = buff;
    self->t.leng = used;
    self->read = 0;
    self->size = size;
    return self;
}

buff_t *buff_new(byte *buff, uint used, uint size)
{
    buff_t *self = (buff_t *)xmalloc(sizeof(buff_t));
    buff_init(self, buff, used, size);
    return self;
}

void buff_free(buff_t *self)
{
    xfree(self);
}

int buff_fgetsln(buff_t *self, FILE *in, uint maxlen)
{
    uint readpos = self->t.leng;
    int readcnt = xfgetsl((char *)self->t.u.text + readpos,
	    min(self->size - readpos, maxlen), in, true);
    /* WARNING: do not add NUL termination, the size must be exact! */
    self->read = readpos;
    if (readcnt >= 0)
	self->t.leng += readcnt;
    return readcnt;
}

int buff_add(buff_t *self, word_t *in)
{
    uint readpos = self->t.leng;
    int readcnt = in->leng;
    uint new_size = self->t.leng + in->leng;
    if (new_size > self->size) {
	self->t.u.text = (byte *)xrealloc(self->t.u.text, new_size + D);
	self->size = new_size;
    }
    self->read = readpos;
    self->t.leng += readcnt;
    memcpy(self->t.u.text + readpos, in->u.text, readcnt);
    Z(self->t.u.text[self->t.leng]);		/* for easier debugging - removable */

    return readcnt;
}

void buff_puts(const buff_t *self, uint width, FILE *fp)
{
    word_t word;
    word.leng = self->t.leng - self->read;
    word.u.text = self->t.u.text + self->read;
    word_puts(&word, width, fp);
}

void buff_shift(buff_t *self, uint start, uint length)
{
    /* Shift buffer contents to delete the specified segment. */
    /* Implemented for deleting html comments.		      */

    BOGO_ASSERT(start + length <= self->t.leng,
		"Invalid buff_shift() parameters.");

    memmove(self->t.u.text + start, self->t.u.text + start + length, self->t.leng - length);
    self->t.leng -= length;
    Z(self->t.u.text[self->t.leng]);		/* for easier debugging - removable */
    return;
}
