#include "Core.h"

#include "HeapImp.h"

static inline void *MAlloc_WS()
{
	MCache& m = mcache[2];
	FreeLink *l = m.list;
	if(l) {
		m.list = l->next;
		m.count--;
		return l;
	}
	else {
		void *ptr = MAlloc_Get(m, 2);
		return ptr;
	}
}

static inline void MFree_WS(void *ptr)
{
	MCache& m = mcache[2];
	((FreeLink *)ptr)->next = m.list;
	m.list = (FreeLink *)ptr;
	if(++m.count > CACHEMAX)
		MFree_Reduce(m, 2);
}

/*
void *MAlloc_WS()         { void *ptr = new byte[24 * sizeof(wchar)]; LOG("Alloc " << ptr); return ptr; }
void  MFree_WS(void *ptr) { LOG("Free " << ptr); delete[] (byte *)ptr; }
*/

wchar *WString::Alloc(int& count)
{
	if(count <= SMALL) {
		count = SMALL;
		wchar *p = (wchar *)MAlloc_WS();
		return p;
	}
	size_t sz = sizeof(Atomic) + (count + 1) * sizeof(wchar);
	Atomic *rc = (Atomic *)MAlloc(sz);
	count = ((sz - sizeof(Atomic)) >> 1) - 1;
	*rc = 1;
	return (wchar *)(rc + 1);
}

void WString::Free()
{
	if(alloc)
		if(IsRc()) {
			Atomic& rc = Rc();
			if(AtomicDec(rc) == 0)
				MFree(&rc);
		}
		else
			MFree_WS(ptr);
}

wchar *WString::Insert(int pos, int count, const wchar *s)
{
	ASSERT(pos >= 0 && count >= 0 && pos <= GetCount());
	int newlen = length + count;
	if(newlen < alloc && !IsShared()/* && !(s >= ptr && s <= ptr + length)*/) { //Optimize!
		if(s >= ptr + pos && s <= ptr + length)
			s += count;
		if(pos < length)
			memmove(ptr + pos + count, ptr + pos, (length - pos) * sizeof(wchar));
		length = newlen;
		ptr[newlen] = 0;
		if(s)
			memcpy(ptr + pos, s, count * sizeof(wchar));
		return ptr + pos;
	}
	int all = max(2 * length, newlen);
	wchar *p = Alloc(all);
	if(pos > 0)
		memcpy(p, ptr, pos * sizeof(wchar));
	if(pos < length)
		memcpy(p + pos + count, ptr + pos, (length - pos) * sizeof(wchar));
	if(s)
		memcpy(p + pos, s, count * sizeof(wchar));
	ASSERT(pos + count <= all);
	p[newlen] = 0;
	Free();
	ptr = p;
	length = newlen;
	alloc = all;
	return ptr + pos;
}

void WString::Set(const WString& src)
{
	if(src.length == 0) {
		Zero();
		return;
	}
	length = src.length;
	alloc = src.alloc;
	if(src.IsRc()) {
		ptr = src.ptr;
		AtomicInc(Rc());
	}
	else {
		ptr = (wchar *)MAlloc_WS();
		qword *t = (qword *)ptr;
		qword *s = (qword *)src.ptr;
		t[0] = s[0];
		t[1] = s[1];
		t[2] = s[2];
		t[3] = s[3];
		t[4] = s[4];
		t[5] = s[5];
	}
}

void WString::Cat(const wchar *s, int l)
{
	if(length + l >= alloc || IsShared())
		Insert(length, l, s);
	else {
		memcpy(ptr + length, s, l * sizeof(wchar));
		ptr[length += l] = 0;
	}
}

void WString::LCat(int c)
{
	*Insert(length, 1, NULL) = c;
}

void WString::Set(const wchar *s, int l)
{
	alloc = length = l;
	memcpy(ptr = Alloc(alloc), s, l * sizeof(wchar));
	ptr[l] = 0;
}

void WString::UnShare()
{
	if(!IsShared()) return;
	int al = length;
	wchar *p = Alloc(al);
	memcpy(p, ptr, (length + 1) * sizeof(wchar));
	Free();
	ptr = p;
	alloc = al;
}

void WString::Set(int pos, int ch)
{
	ASSERT(pos >= 0 && pos < GetCount());
	UnShare();
	ptr[pos] = ch;
}

void WString::Trim(int pos)
{
	ASSERT(pos >= 0 && pos <= GetCount());
	UnShare();
	length = pos;
	ptr[pos] = 0;
}

void WString::Remove(int pos, int count)
{
	ASSERT(pos >= 0 && count >= 0 && pos + count <= GetCount());
	UnShare();
	memmove(ptr + pos, ptr + pos + count, (GetCount() - pos - count + 1) * sizeof(wchar));
	length -= count;
}

void WString::Insert(int pos, const wchar *s, int count)
{
	Insert(pos, count, s);
}

WString::WString(WStringBuffer& b)
{
	length = b.GetLength();
	ptr = b.begin;
	ptr[length] = 0;
	alloc = b.GetAlloc();
	b.Zero();
}

void WStringBuffer::Zero()
{
	static wchar h[2];
	begin = end = limit = h;
}

wchar *WStringBuffer::Alloc(int count, int& alloc)
{
	if(count <= 23) {
		wchar *s = (wchar *)MAlloc_WS();
		alloc = WString::SMALL;
		return s;
	}
	else {
		size_t sz = sizeof(Atomic) + (count + 1) * sizeof(wchar);
		Atomic *rc = (Atomic *)MAlloc(sz);
		alloc = ((sz - sizeof(Atomic)) >> 1) - 1;
		*rc = 1;
		return (wchar *)(rc + 1);
	}
}

void WStringBuffer::Free()
{
	int all = limit - begin;
	if(all == WString::SMALL)
		MFree_WS(begin);
	if(all > WString::SMALL)
		MFree((Atomic *)begin - 1);
}

void WStringBuffer::Expand(int n, const wchar *cat, int l)
{
	int al;
	int ep = end - begin;
	wchar *p = Alloc(n, al);
	memcpy(p, begin, GetLength() * sizeof(wchar));
	if(cat) {
		memcpy(p + ep, cat, l * sizeof(wchar));
		ep += l;
	}
	Free();
	begin = p;
	end = begin + ep;
	limit = begin + al;
}

void WStringBuffer::Expand()
{
	Expand(GetLength() * 2);
}

void WStringBuffer::SetLength(int l)
{
	if(l > (limit - begin))
		Expand(l);
	end = begin + l;
}

void WStringBuffer::Cat(const wchar *s, int l)
{
	if(end + l > limit)
		Expand(max(GetLength(), l) + GetLength(), s, l);
	else {
		memcpy(end, s, l * sizeof(wchar));
		end += l;
	}
}

WStringBuffer::WStringBuffer(WString& s)
{
	s.UnShare();
	int l = s.GetLength();
	begin = s.ptr;
	end = begin + l;
	limit = begin + s.GetAlloc();
	s.Zero();
}
