#include "stdafx.h"

#define STRSAFE_NO_DEPRECATE
#include <Strsafe.h>

#define CurAlloc Reserved[0]


BOOL
MultiString_Init(MultiString *ms,
				 DWORD dwInitLen)
{
	HANDLE hHeap = GetProcessHeap();

	if (!ms) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	if (dwInitLen) {
		ms->CurAlloc = dwInitLen;
		ms->Values = HeapAlloc(hHeap,
			HEAP_ZERO_MEMORY,
			sizeof(LPTSTR) * dwInitLen);
		if (!ms->Values) return FALSE;
		ms->Count = 0;
	} else {
		// Otherwise, this amounts to a zero-out
		ZeroMemory(ms, sizeof(MultiString));
	}

	return TRUE;
}

__inline static LPTSTR
CreateStrCopy(LPCTSTR szStr)
{
	HRESULT hrRes;
	LPTSTR szNewCopy;
	// dwLen needs to include the NULL terminator
	size_t dwLen = _tcslen(szStr) + 1;
	HANDLE hHeap = GetProcessHeap();
	
	// Allocate the new string
	szNewCopy = HeapAlloc(hHeap,
		HEAP_ZERO_MEMORY,
		sizeof(TCHAR) * dwLen);
	if (!szNewCopy) return NULL;

	// Copy the actual data over
	hrRes = StringCchCopy(szNewCopy,
		dwLen,
		szStr);
	if (FAILED(hrRes)) {
		HeapFree(hHeap, 0, szNewCopy);
		return NULL;
	}

	return szNewCopy;
}

BOOL
MultiString_Append(MultiString *ms,
				   LPCTSTR szNew)
{
	LPTSTR szNewCopy = NULL;
	DWORD dwNewLen;
	HANDLE hHeap = GetProcessHeap();

	if (!ms || !szNew) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	dwNewLen  = (DWORD)_tcslen(szNew) + 1;

	// Do we need to allocate more room for pointers?
	if (ms->Values == NULL) {
		// Oops, we have nothing! Let's allocate a couple
		if (!MultiString_Init(ms, 3)) {
			return FALSE;
		}
	} else if (ms->CurAlloc == ms->Count) {
		DWORD dwNewAlloc = ms->CurAlloc + 3;
		LPTSTR *pszNew = HeapReAlloc(hHeap,
			0,
			ms->Values,
			sizeof(LPTSTR) * dwNewAlloc);
		if (!pszNew) {
			// ReAlloc failed :<
			return FALSE;
		}
		ms->CurAlloc = dwNewAlloc;
		ms->Values = pszNew;
	}

	szNewCopy = CreateStrCopy(szNew);
	if (!szNewCopy) return FALSE;

	// All set, append to the end and increment Count
	ms->Values[ms->Count++] = szNewCopy;
	ms->TotalLength += dwNewLen;

	return TRUE;
}

BOOL
MultiString_Remove(MultiString *ms,
				   DWORD dwIdx)
{
	DWORD dwLen = 0;
	HANDLE hHeap = GetProcessHeap();

	if (!ms) return FALSE;

	if (dwIdx >= ms->Count) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	} else if (ms->Values[dwIdx] != NULL) {
		dwLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;

		HeapFree(hHeap, 0, ms->Values[dwIdx]);
	}


	// Shift down the remaining strings
	if (dwIdx < ms->Count - 1) {
		size_t i;
		for (i = dwIdx; i < ms->Count - 1; i++) {
			ms->Values[i] = ms->Values[i + 1];
		}
	}

	ms->Count--;
	ms->TotalLength -= dwLen;

	return TRUE;
}

BOOL
MultiString_Replace(MultiString *ms,
					DWORD dwIdx,
					LPCTSTR szNew)
{
	DWORD dwOldLen = 0,
		dwNewLen = 0;
	LPTSTR szTmp;
	HANDLE hHeap = GetProcessHeap();

	if (!ms || !szNew) return FALSE;

	if (dwIdx >= ms->Count) {
		SetLastError(ERROR_INVALID_PARAMETER);
		return FALSE;
	}

	dwNewLen = (DWORD)_tcslen(szNew) + 1;

	// Create the copied new string
	szTmp = CreateStrCopy(szNew);
	if (!szTmp) return FALSE;

	// Perform the switcheroo
	if (ms->Values[dwIdx] != NULL) {
		dwOldLen = (DWORD)_tcslen(ms->Values[dwIdx]) + 1;

		HeapFree(hHeap, 0, ms->Values[dwIdx]);
	}

	ms->Values[dwIdx] = szTmp;
	ms->TotalLength = ms->TotalLength + dwNewLen - dwOldLen;

	return TRUE;
}

void
MultiString_Dispose(MultiString *ms)
{
	HANDLE hHeap = GetProcessHeap();
	DWORD i;

	if (!ms) return;

	if (ms->Values != NULL) {
		for (i = 0; i < ms->Count; i++) {
			HeapFree(hHeap, 0, ms->Values[i]);
		}

		HeapFree(hHeap, 0, ms->Values);
		ms->Values = NULL;
	}
	ms->Count = 0;
	ms->CurAlloc = 0;

	return;
}