// Copyright (c) 2000 Microsoft Corporation. All rights reserved.
//
// Implementation of PackExceptionFileAndLine and UnpackExceptionFileAndLine.
//

#include "stdinc.h"
#include "packexception.h"
#include "oleaut.h"

const WCHAR g_wszDescriptionFileSeparator[] = L"»"; // magic character for separating the filename, line, and description
const WCHAR g_wchDescriptionFileSeparator = g_wszDescriptionFileSeparator[0];

void SeparateDescriptionFileAndLine(BSTR bstrDescription, const WCHAR **ppwszFilename, const WCHAR **ppwszLine, const WCHAR **ppwszDescription)
{
	assert(bstrDescription && ppwszFilename && ppwszLine && ppwszDescription);

	// if there aren't any packed fields, the whole thing is the description
	*ppwszDescription = bstrDescription;

	WCHAR *pwszLine = wcsstr(bstrDescription, g_wszDescriptionFileSeparator);
	if (!pwszLine)
		return;

	WCHAR *pwszDescription = wcsstr(pwszLine + 1, g_wszDescriptionFileSeparator);
	if (!pwszDescription)
		return;

	// String looks like this:
	//                             MyScript.spt»23»Description of the error
	// pExcepInfo->bstrDescription-^  pwszLine-^  ^-pwszDescription

	*ppwszFilename = bstrDescription;
	assert(*pwszLine == g_wchDescriptionFileSeparator);
	*ppwszLine = pwszLine + 1;
	assert(*pwszDescription == g_wchDescriptionFileSeparator);
	*ppwszDescription = pwszDescription + 1;
}

bool wcsIsBlankTillSeparator(const WCHAR *pwsz)
{
	return !pwsz[0] || pwsz[0] == g_wchDescriptionFileSeparator;
}

void wcscpyTillSeparator(WCHAR *pwszDestination, const WCHAR *pwszSource)
{
	assert(pwszDestination && pwszSource);
	while (!wcsIsBlankTillSeparator(pwszSource))
	{
		*pwszDestination++ = *pwszSource++;
	}
	*pwszDestination = L'\0';
}

void wcscatTillSeparator(WCHAR *pwszDestination, const WCHAR *pwszSource)
{
	assert(pwszDestination && pwszSource);
	while (*pwszDestination != L'\0')
		++pwszDestination;

	wcscpyTillSeparator(pwszDestination, pwszSource);
}

void PackExceptionFileAndLine(bool fUseOleAut, EXCEPINFO *pExcepInfo, const WCHAR *pwszFilename, const ULONG *pulLine)
{
	if (!pExcepInfo || !pExcepInfo->bstrDescription)
		return;

	const WCHAR *pwszDescrFilename = L"";
	const WCHAR *pwszDescrLine = L"";
	const WCHAR *pwszDescrDescription = L"";

	SeparateDescriptionFileAndLine(pExcepInfo->bstrDescription, &pwszDescrFilename, &pwszDescrLine, &pwszDescrDescription);

	if (wcsIsBlankTillSeparator(pwszDescrFilename) && pwszFilename)
	{
		// Filename is blank.  Use the specified filename.
		pwszDescrFilename = pwszFilename;
	}

    // MSDN documentation for _ultow says max is 33 characters, but PREFIX complains if the length
    // is less than 40.
	WCHAR wszLineBuffer[40] = L""; 
	if (wcsIsBlankTillSeparator(pwszDescrLine) && pulLine)
	{
		// Line is blank.  Use the specified line.
		_ultow(*pulLine, wszLineBuffer, 10);
		pwszDescrLine = wszLineBuffer;
	}

	WCHAR *pwszNewDescription = new WCHAR[wcslen(pwszDescrFilename) + wcslen(pwszDescrLine) + wcslen(pwszDescrDescription) + (wcslen(g_wszDescriptionFileSeparator) * 2 + 1)];
	if (pwszNewDescription)
	{
		wcscpyTillSeparator(pwszNewDescription, pwszDescrFilename);
		wcscat(pwszNewDescription, g_wszDescriptionFileSeparator);
		wcscatTillSeparator(pwszNewDescription, pwszDescrLine);
		wcscat(pwszNewDescription, g_wszDescriptionFileSeparator);
		wcscat(pwszNewDescription, pwszDescrDescription);
		DMS_SysFreeString(fUseOleAut, pExcepInfo->bstrDescription);
		pExcepInfo->bstrDescription = DMS_SysAllocString(fUseOleAut, pwszNewDescription);
		delete[] pwszNewDescription;
	}
}

void UnpackExceptionFileAndLine(BSTR bstrDescription, DMUS_SCRIPT_ERRORINFO *pErrorInfo)
{
	assert(pErrorInfo && bstrDescription);

    if (pErrorInfo && bstrDescription)
    {

	    const WCHAR *pwszDescrFilename = L"";
	    const WCHAR *pwszDescrLine = L"";
	    const WCHAR *pwszDescrDescription = L"";

	    SeparateDescriptionFileAndLine(bstrDescription, &pwszDescrFilename, &pwszDescrLine, &pwszDescrDescription);

	    // String looks like this:
	    //                             CoolScriptFile.spt»23»Description of the error
	    //           pwszDescrFilename-^    pwszDescrLine-^  ^-pwszDescrDescription
	    // Except that if these weren't found then they point to a separate empty string.

	    if (!wcsIsBlankTillSeparator(pwszDescrFilename))
	    {
		    // Filename is present.  Copy to pErrorInfo.
		    assert(*(pwszDescrLine - 1) == g_wchDescriptionFileSeparator);
		    wcsTruncatedCopy(pErrorInfo->wszSourceFile,
							    pwszDescrFilename,
							    std::_MIN<UINT>(DMUS_MAX_FILENAME, pwszDescrLine - pwszDescrFilename));
	    }

	    if (!wcsIsBlankTillSeparator(pwszDescrLine))
	    {
		    // Line is present.  Copy to pErrorInfo.
		    WCHAR *pwszLineSeparator = const_cast<WCHAR *>(pwszDescrDescription - 1);
		    assert(*pwszLineSeparator == g_wchDescriptionFileSeparator);
		    *pwszLineSeparator = L'\0'; // terminate the line for wcstoul
		    pErrorInfo->ulLineNumber = wcstoul(pwszDescrLine, NULL, 10);
		    *pwszLineSeparator = g_wchDescriptionFileSeparator; // restore the separator
	    }

	    // Always copy the description
	    wcsTruncatedCopy(pErrorInfo->wszDescription, pwszDescrDescription, DMUS_MAX_FILENAME);
    }
}