/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * Simple buffer management for LNXALL applications
 *
 * 2024/07/02
 */

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

#include "lnxall_buffer.h"

int lbuff_init(struct lnxall_buff * bufp, int init_len)
{
	if (bufp == NULL)
		return -1;

	if (init_len < LNAXLL_BUFFER_MIN)
		init_len = LNAXLL_BUFFER_MIN;
	bufp->curlen = 0;
	bufp->maxlen = init_len;
	bufp->bufptr = (char *) malloc((size_t) init_len);
	if (bufp->bufptr == NULL) {
		fputs("Error, system out of memory!\n", stderr);
		fflush(stderr);
		return -1;
	}
	bufp->bufptr[0] = '\0';
	return 0;
}

static int lbuff_expand(struct lnxall_buff * bufp, unsigned int more)
{
	char * newptr;
	unsigned int maxlen, curlen;

	maxlen = bufp->maxlen;
	curlen = bufp->curlen;
	for (;;) {
		unsigned int free_s;
		if (maxlen < curlen) {
			fprintf(stderr, "Error, Fatal error for lnxall-buffer: %#x, %#x\n",
				maxlen, curlen);
			fflush(stderr);
			return -1;
		}

		free_s = maxlen - curlen;
		if (free_s > (more + 1))
			break;
		maxlen <<= 0x1;
	}

	if (bufp->maxlen == maxlen)
		return 0;

	newptr = (char *) realloc(bufp->bufptr, maxlen);
	if (newptr == NULL) {
		fprintf(stderr, "Error, system out of memory: %#x\n", maxlen);
		fflush(stderr);
		return -1;
	}

	bufp->bufptr = newptr;
	bufp->maxlen = maxlen;
	return 0;
}

int lbuff_sprintf(struct lnxall_buff * bufp, const char * fmtstr, ...)
{
	int mlen;
	va_list pva;
	unsigned int left;
	char btmp[512], * endp;

	if (bufp == NULL || fmtstr == NULL)
		return -1;

	va_start(pva, fmtstr);
	mlen = vsnprintf(btmp, sizeof(btmp) - 1, fmtstr, pva);
	va_end(pva);

	if (mlen < 0) {
		fprintf(stderr, "Error, format string has returned: %d\n", mlen);
		fflush(stderr);
		return -1;
	}

	if (mlen == 0)
		return 0;
	if (mlen < sizeof(btmp))
		return lbuff_append(bufp, btmp, mlen);

	mlen += 0x10;
	// Need to format again, more than `sizeof(btmp)
	left = bufp->maxlen - bufp->curlen;
	if (left <= (unsigned int) mlen &&
		lbuff_expand(bufp, (unsigned int) mlen) < 0) {
		return -1;
	}

	left = bufp->maxlen - bufp->curlen;
	endp = bufp->bufptr + bufp->curlen;
	va_start(pva, fmtstr);
	mlen = vsnprintf(endp, left, fmtstr, pva);
	va_end(pva);

	if (mlen > 0) {
		bufp->curlen += (unsigned int) mlen;
		endp[mlen] = '\0';
	} else {
		endp[0] = '\0';
	}
	return mlen;
}

int lbuff_append(struct lnxall_buff * bufp,
	const char * p, int applen)
{
	int alen;
	char * endp;
	unsigned int left;

	if (bufp == NULL || p == NULL || applen <= 0)
		return -1;

	alen = applen + 0x1;
	left = bufp->maxlen - bufp->curlen;
	if (left <= (unsigned int) alen &&
		lbuff_expand(bufp, (unsigned int) alen) < 0) {
		return -1;
	}

	endp = bufp->bufptr + bufp->curlen;
	memcpy(endp, p, (size_t) applen);
	endp[applen] = '\0';
	bufp->curlen += (unsigned int) applen;
	return applen;
}

void lbuff_free(struct lnxall_buff * bufp)
{
	if (bufp == NULL)
		return;

	if (bufp->bufptr != NULL) {
		free(bufp->bufptr);
		bufp->bufptr = NULL;
	}
	bufp->curlen = 0;
	bufp->maxlen = 0;
}

int lbuff_setlen(struct lnxall_buff * bufp, int curlen)
{
	if (bufp == NULL || curlen < 0)
		return -1;

	if (bufp->maxlen > (unsigned int) curlen) {
		bufp->curlen = (unsigned int) curlen;
		bufp->bufptr[curlen] = '\0';
		return 0;
	}
	return -1;
}
