//	VirtualDub - Video processing and capture application
//	System library component
//	Copyright (C) 1998-2004 Avery Lee, All Rights Reserved.
//
//	Beginning with 1.6.0, the VirtualDub system library is licensed
//	differently than the remainder of VirtualDub.  This particular file is
//	thus licensed as follows (the "zlib" license):
//
//	This software is provided 'as-is', without any express or implied
//	warranty.  In no event will the authors be held liable for any
//	damages arising from the use of this software.
//
//	Permission is granted to anyone to use this software for any purpose,
//	including commercial applications, and to alter it and redistribute it
//	freely, subject to the following restrictions:
//
//	1.	The origin of this software must not be misrepresented; you must
//		not claim that you wrote the original software. If you use this
//		software in a product, an acknowledgment in the product
//		documentation would be appreciated but is not required.
//	2.	Altered source versions must be plainly marked as such, and must
//		not be misrepresented as being the original software.
//	3.	This notice may not be removed or altered from any source
//		distribution.

#include "stdafx.h"
#include <vd2/system/VDString.h>
#include <vd2/system/vdstl.h>
#include <vd2/system/vdstdc.h>

const VDStringSpanA::value_type VDStringSpanA::sNull[1] = {0};

void VDStringA::push_back_extend() {
	VDASSERT(mpEOS == mpEnd);
	size_type current_size = (size_type)(mpEnd - mpBegin);

	reserve_slow(current_size * 2 + 1, current_size);
}

void VDStringA::resize_slow(size_type n, size_type current_size) {
	resize_slow(n, current_size, 0);
}

void VDStringA::resize_slow(size_type n, size_type current_size, value_type c) {
	VDASSERT(n > current_size);

	size_type current_capacity = (size_type)(mpEOS - mpBegin);
	if (n > current_capacity)
		reserve_slow(n, current_capacity);

	memset(mpBegin + current_size, c, n - current_size);
	mpEnd = mpBegin + n;
	*mpEnd = 0;
}

void VDStringA::reserve_slow(size_type n, size_type current_capacity) {
	VDASSERT(n > current_capacity);

	size_type current_size = (size_type)(mpEnd - mpBegin);
	value_type *s = new value_type[n + 1];
	memcpy(s, mpBegin, (current_size + 1) * sizeof(value_type));
	if (mpBegin != sNull)
		delete[] mpBegin;

	mpBegin = s;
	mpEnd = s + current_size;
	mpEOS = s + n;
}

void VDStringA::reserve_amortized_slow(size_type n, size_type current_size, size_type current_capacity) {
	n += current_size;

	size_type doublesize = current_size * 2;
	if (n < doublesize)
		n = doublesize;

	reserve_slow(n, current_capacity);
}

VDStringA& VDStringA::sprintf(const value_type *format, ...) {
	clear();
	va_list val;
	va_start(val, format);
	append_vsprintf(format, val);
	va_end(val);
	return *this;
}

VDStringA& VDStringA::append_sprintf(const value_type *format, ...) {
	va_list val;
	va_start(val, format);
	append_vsprintf(format, val);
	va_end(val);
	return *this;
}

VDStringA& VDStringA::append_vsprintf(const value_type *format, va_list val) {
	char buf[2048];

	int len = vdvsnprintf(buf, 2048, format, val);
	if (len >= 0)
		append(buf, buf+len);
	else {
		int len;

		vdfastvector<char> tmp;
		for(int siz = 8192; siz <= 65536; siz += siz) {
			tmp.resize(siz);

			char *tmpp = tmp.data();
			len = vdvsnprintf(tmp.data(), siz, format, val);
			if (len >= 0) {
				append(tmpp, tmpp+len);
				break;
			}
		}
	}

	return *this;
}

void VDStringA::move_from(VDStringA& src) {
	if (mpBegin != sNull)
		delete[] mpBegin;

	mpBegin = src.mpBegin;
	mpEnd = src.mpEnd;
	mpEOS = src.mpEOS;

	src.mpBegin = const_cast<value_type *>(sNull);
	src.mpEnd = const_cast<value_type *>(sNull);
	src.mpEOS = const_cast<value_type *>(sNull);
}

///////////////////////////////////////////////////////////////////////////////

const VDStringSpanW::value_type VDStringSpanW::sNull[1] = {0};

void VDStringW::push_back_extend() {
	VDASSERT(mpEOS == mpEnd);
	size_type current_size = (size_type)(mpEnd - mpBegin);

	reserve_slow(current_size * 2 + 1, current_size);
}

void VDStringW::resize_slow(size_type n, size_type current_size) {
	VDASSERT(n > current_size);

	size_type current_capacity = (size_type)(mpEOS - mpBegin);
	if (n > current_capacity)
		reserve_slow(n, current_capacity);

	mpEnd = mpBegin + n;
	*mpEnd = 0;
}

void VDStringW::reserve_slow(size_type n, size_type current_capacity) {
	VDASSERT(current_capacity == (size_type)(mpEOS - mpBegin));
	VDASSERT(n > current_capacity);

	size_type current_size = (size_type)(mpEnd - mpBegin);
	value_type *s = new value_type[n + 1];
	memcpy(s, mpBegin, (current_size + 1) * sizeof(value_type));
	if (mpBegin != sNull)
		delete[] mpBegin;

	mpBegin = s;
	mpEnd = s + current_size;
	mpEOS = s + n;
}

void VDStringW::reserve_amortized_slow(size_type n, size_type current_size, size_type current_capacity) {
	n += current_size;

	size_type doublesize = current_size * 2;
	if (n < doublesize)
		n = doublesize;

	reserve_slow(n, current_capacity);
}

VDStringW& VDStringW::sprintf(const value_type *format, ...) {
	clear();
	va_list val;
	va_start(val, format);
	append_vsprintf(format, val);
	va_end(val);
	return *this;
}

VDStringW& VDStringW::append_sprintf(const value_type *format, ...) {
	va_list val;
	va_start(val, format);
	append_vsprintf(format, val);
	va_end(val);
	return *this;
}

VDStringW& VDStringW::append_vsprintf(const value_type *format, va_list val) {
	wchar_t buf[1024];

	int len = vdvswprintf(buf, 1024, format, val);
	if (len >= 0)
		append(buf, buf+len);
	else {
		int len;

		vdfastvector<wchar_t> tmp;
		for(int siz = 4096; siz <= 65536; siz += siz) {
			tmp.resize(siz);

			wchar_t *tmpp = tmp.data();
			len = vdvswprintf(tmpp, siz, format, val);
			if (len >= 0) {
				append(tmpp, tmpp+len);
				break;
			}
		}
	}

	va_end(val);
	return *this;
}

void VDStringW::move_from(VDStringW& src) {
	if (mpBegin != sNull)
		delete[] mpBegin;

	mpBegin = src.mpBegin;
	mpEnd = src.mpEnd;
	mpEOS = src.mpEOS;

	src.mpBegin = const_cast<value_type *>(sNull);
	src.mpEnd = const_cast<value_type *>(sNull);
	src.mpEOS = const_cast<value_type *>(sNull);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

template<>
VDStringA *vdmove_forward(VDStringA *src1, VDStringA *src2, VDStringA *dst) {
	VDStringA *p = src1;
	while(p != src2) {
		dst->move_from(*p);
		++dst;
		++p;
	}

	return dst;
}

template<>
VDStringW *vdmove_forward(VDStringW *src1, VDStringW *src2, VDStringW *dst) {
	VDStringW *p = src1;
	while(p != src2) {
		dst->move_from(*p);
		++dst;
		++p;
	}

	return dst;
}

template<>
VDStringA *vdmove_backward(VDStringA *src1, VDStringA *src2, VDStringA *dst) {
	VDStringA *p = src2;
	while(p != src1) {
		--dst;
		--p;
		dst->move_from(*p);
	}

	return dst;
}

template<>
VDStringW *vdmove_backward(VDStringW *src1, VDStringW *src2, VDStringW *dst) {
	VDStringW *p = src2;
	while(p != src1) {
		--dst;
		--p;
		dst->move_from(*p);
	}

	return dst;
}

template<>
void vdmove<VDStringA>(VDStringA& dst, VDStringA& src) {
	dst.move_from(src);
}

template<>
void vdmove<VDStringW>(VDStringW& dst, VDStringW& src) {
	dst.move_from(src);
}
