/*
 *	Variable length array of strings
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "types.h"

aELEMENT *vamk(ptrdiff_t len)
{
	ptrdiff_t *newa = (ptrdiff_t *) joe_malloc((1 + len) * SIZEOF(aELEMENT) + 2 * SIZEOF(ptrdiff_t));

	newa[0] = len;
	newa[1] = 0;
	((aELEMENT *)(newa + 2))[0] = aterm;
	return (aELEMENT *)(newa + 2);
}

void varm(aELEMENT *vary)
{
	if (vary) {
		vazap(vary, 0, aLen(vary));
		joe_free((ptrdiff_t *) vary - 2);
	}
}

ptrdiff_t alen(aELEMENT *ary)
{
	if (ary) {
		aELEMENT *beg = ary;
		while (acmp(*ary, aterm))
			++ary;
		return ary - beg;
	} else
		return 0;
}

aELEMENT *vaensure(aELEMENT *vary, ptrdiff_t len)
{
	if (!vary)
		vary = vamk(len);
	else if (len > aSiz(vary)) {
		len += (len >> 2);
		vary = (aELEMENT *)(2 + (ptrdiff_t *) joe_realloc((ptrdiff_t *) vary - 2, (len + 1) * SIZEOF(aELEMENT) + 2 * SIZEOF(ptrdiff_t)));

		aSiz(vary) = len;
	}
	return vary;
}

aELEMENT *vazap(aELEMENT *vary, ptrdiff_t pos, ptrdiff_t n)
{
	if (vary) {
		ptrdiff_t x;

		if (pos < aLen(vary)) {
			if (pos + n <= aLen(vary)) {
				for (x = pos; x != pos + n; ++x)
					adel(vary[x]);
			} else {
				for (x = pos; x != aLen(vary); ++x)
					adel(vary[x]);
			}
		}
	}
	return vary;
}

aELEMENT *vatrunc(aELEMENT *vary, ptrdiff_t len)
{
	if (!vary || len > aLEN(vary))
		vary = vaensure(vary, len);
	if (len < aLen(vary)) {
		vary = vazap(vary, len, aLen(vary) - len);
		vary[len] = vary[aLen(vary)];
		aLen(vary) = len;
	} else if (len > aLen(vary)) {
		vary = vafill(vary, aLen(vary), ablank, len - aLen(vary));
	}
	return vary;
}

aELEMENT *vafill(aELEMENT *vary, ptrdiff_t pos, aELEMENT el, ptrdiff_t len)
{
	ptrdiff_t olen = aLEN(vary), x;

	if (!vary || pos + len > aSIZ(vary))
		vary = vaensure(vary, pos + len);
	if (pos + len > olen) {
		vary[pos + len] = vary[olen];
		aLen(vary) = pos + len;
	}
	for (x = pos; x != pos + len; ++x)
		vary[x] = adup(el);
	if (pos > olen)
		vary = vafill(vary, pos, ablank, pos - olen);
	return vary;
}

#ifdef junk
aELEMENT *vancpy(aELEMENT *vary, ptrdiff_t pos, aELEMENT *array, ptrdiff_t len)
{
	ptrdiff_t olen = aLEN(vary);

	if (!vary || pos + len > aSIZ(vary))
		vary = vaensure(vary, pos + len);
	if (pos + len > olen) {
		vary[pos + len] = vary[olen];
		aLen(vary) = pos + len;
	}
	if (pos > olen)
		vary = vafill(vary, olen, ablank, pos - olen);
	mfwrd(vary + pos, array, len * SIZEOF(aELEMENT));
	return vary;
}
#endif

aELEMENT *vandup(aELEMENT *vary, ptrdiff_t pos, aELEMENT *array, ptrdiff_t len)
{
	ptrdiff_t olen = aLEN(vary), x;

	if (!vary || pos + len > aSIZ(vary))
		vary = vaensure(vary, pos + len);
	if (pos + len > olen) {
		vary[pos + len] = vary[olen];
		aLen(vary) = pos + len;
	}
	if (pos > olen)
		vary = vafill(vary, olen, ablank, pos - olen);
	for (x = 0; x != len; ++x)
		vary[x + pos] = adup(array[x]);
	return vary;
}

aELEMENT *vadup(aELEMENT *vary)
{
	return vandup(NULL, 0, vary, aLEN(vary));
}

aELEMENT *_vaset(aELEMENT *vary, ptrdiff_t pos, aELEMENT el)
{
	if (!vary || pos + 1 > aSIZ(vary))
		vary = vaensure(vary, pos + 1);
	if (pos > aLen(vary)) {
		vary = vafill(vary, aLen(vary), ablank, pos - aLen(vary));
		vary[pos + 1] = vary[pos];
		vary[pos] = el;
		aLen(vary) = pos + 1;
	} else if (pos == aLen(vary)) {
		vary[pos + 1] = vary[pos];
		vary[pos] = el;
		aLen(vary) = pos + 1;
	} else {
		adel(vary[pos]);
		vary[pos] = el;
	}
	return vary;
}

static int _acmp(aELEMENT *a, aELEMENT *b)
{
	return acmp(*a, *b);
}

aELEMENT *vasort(aELEMENT *ary, ptrdiff_t len)
{
	if (!ary || !len)
		return ary;
	jsort(ary, len, SIZEOF(aELEMENT), (int (*)(const void *, const void *))_acmp);
	return ary;
}

void vadel(aELEMENT *ary, ptrdiff_t ofst, ptrdiff_t len)
{
	if (ary && ofst < aLen(ary)) {
		ptrdiff_t x;
		if (ofst + len > aLen(ary))
			len = aLen(ary) - ofst;
		for (x = ofst; x < ofst + len; ++x)
			adel(ary[x]);
		if (aLen(ary) - (ofst + len))
			mmove(ary + ofst, ary + ofst + len, (aLen(ary) - (ofst + len)) * SIZEOF(aELEMENT));
		aLen(ary) -= len;
		ary[aLen(ary)] = 0;
	}
}

void vauniq(aELEMENT *ary)
{
	if (ary) {
		ptrdiff_t x;
		ptrdiff_t len = aLen(ary);
		for (x = 0; x < len - 1; ++x) {
			ptrdiff_t y;
			for (y = x + 1;y < len; ++y)
				if (acmp(ary[x], ary[y]))
					break;
			vadel(ary, x + 1, y - (x + 1));
			len -= y - (x + 1);
		}
	}
}

aELEMENT *vawords(aELEMENT *a, const char *s, ptrdiff_t len, const char *sep, ptrdiff_t seplen)
{
	ptrdiff_t x;

	if (!a)
		a = vamk(10);
	else
		a = vatrunc(a, 0);
      loop:
	x = vsspan(s, len, sep, seplen);
	s += x;
	len -= x;
	if (len) {
		x = vsscan(s, len, sep, seplen);
		if (x != ~0) {
			a = vaadd(a, vsncpy(vsmk(x), 0, s, x));
			s += x;
			len -= x;
			if (len)
				goto loop;
		} else
			a = vaadd(a, vsncpy(vsmk(len), 0, s, len));
	}
	return a;
}
