/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1997 - 1998 **/
/**********************************************************************/

/*
	format.cpp
		
    FILE HISTORY:
        
*/

#include "stdafx.h"
#include "tfschar.h"
#include "mprapi.h"
#include "rtrstr.h"
#include "format.h"

#include "raserror.h"
#include "mprerror.h"

//----------------------------------------------------------------------------
// Function:    FormatSystemError
//
// Formats an error code using '::FormatMessage', '::MprAdminGetErrorString',
// or '::RasAdminGetErrorString', or all the above (the default).
// If 'psFormat' is specified, it is used to format the error-string
// into 'sError'.
//----------------------------------------------------------------------------

DWORD
FormatSystemError(
    IN  HRESULT     hrErr,
		LPTSTR		pszBuffer,
		UINT		cchBuffer,
    IN  UINT        idsFormat,
    IN  DWORD       dwFormatFlags
    ) {
	DWORD	dwErr = WIN32_FROM_HRESULT(hrErr);
    DWORD dwRet;
    TCHAR* pszErr = NULL;
    WCHAR* pwsErr = NULL;
	CString	sError;

    dwFormatFlags &= FSEFLAG_ANYMESSAGE;

    do {

        //
        // See if 'FSEFLAG_MPRMESSAGE' is specified
        //
        if (dwFormatFlags & FSEFLAG_MPRMESSAGE)
		{
            dwFormatFlags &= ~FSEFLAG_MPRMESSAGE;

			if (((dwErr >= ROUTEBASE) && (dwErr <= ROUTEBASEEND)) ||
				((dwErr >= RASBASE) && (dwErr <= RASBASEEND)))
			{
				//
				// Try retrieving a string from rasmsg.dll or mprmsg.dll
				//
                dwRet = ::MprAdminGetErrorString(dwErr, &pwsErr);

                if (dwRet == NO_ERROR)
				{
                    pszErr = StrDupTFromW(pwsErr);
					::MprAdminBufferFree(pwsErr);
                    break;
                }
                else if (!dwFormatFlags)
					break;

                dwRet = NO_ERROR;
			}
			else if (!dwFormatFlags)
				return ERROR_INVALID_PARAMETER;
        }


        //
        // See if 'FSEFLAG_SYSMESSAGE' is specified
        //
        if (dwFormatFlags & FSEFLAG_SYSMESSAGE)
		{
            dwFormatFlags &= ~FSEFLAG_SYSMESSAGE;

            //
            // Try retrieving a string from the system
            //
            dwRet = ::FormatMessageW(
                        FORMAT_MESSAGE_ALLOCATE_BUFFER|
                        FORMAT_MESSAGE_FROM_SYSTEM,
                        NULL,
                        hrErr,
                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                        (LPTSTR)&pwsErr,
                        1,
                        NULL
                        );

            if (dwRet)
			{
                pszErr = StrDupTFromW(pwsErr);
				LocalFree(pwsErr);
                break;
            }
            else if (!dwFormatFlags)
				break;

            dwRet = NO_ERROR;
        }


    } while (FALSE);

    //
    // If no string was found, format the error as a number.
    //

    if (!pszErr)
	{
        TCHAR szErr[12];

        wsprintf(szErr, TEXT("%d"), dwErr);

        pszErr = StrDup(szErr);
    }


    //
    // Format the string into the caller's argument
    //

    if (idsFormat)
        AfxFormatString1(sError, idsFormat, pszErr);
    else
        sError = pszErr;

	// Finally, copy the output
	StrnCpy(pszBuffer, (LPCTSTR) sError, cchBuffer);

    delete pszErr;

    return dwRet;
}


//
// Forward declaration of utility function used by 'FormatNumber'
//
TCHAR*
padultoa(
    UINT    val,
    TCHAR*  pszBuf,
    INT     width
    );


//----------------------------------------------------------------------------
// Function:    FormatNumber
//
// This function takes an integer and formats a string with the value
// represented by the number, grouping digits by powers of one-thousand
//----------------------------------------------------------------------------

VOID
FormatNumber(DWORD      dwNumber,
			 LPTSTR		pszBuffer,
			 UINT		cchBuffer,
			 BOOL		fSigned)
{
	Assert(cchBuffer > 14);
    static TCHAR szNegativeSign[4] = TEXT("");
    static TCHAR szThousandsSeparator[4] = TEXT("");

    DWORD i, dwLength;
    TCHAR szDigits[12] = {0}, pszTemp[20] = {0};


    //
    // Retrieve the thousands-separator for the user's locale
    //

    if (szThousandsSeparator[0] == TEXT('\0'))
	{
        ::GetLocaleInfo(
            LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szThousandsSeparator, 4
            );
    }


    //
    // If we are formatting a signed value, see if the value is negative
    //

    if (fSigned)
	{
        if ((INT)dwNumber >= 0)
            fSigned = FALSE;
        else
		{
            //
            // The value is negative; retrieve the locale's negative-sign
            //

            if (szNegativeSign[0] == TEXT('\0')) {

                ::GetLocaleInfo(
                    LOCALE_USER_DEFAULT, LOCALE_SNEGATIVESIGN, szNegativeSign, 4
                    );
            }

            dwNumber = abs((INT)dwNumber);
        }
    }


    //
    // Convert the number to a string without thousands-separators
    //

    padultoa(dwNumber, szDigits, 0);

    dwLength = lstrlen(szDigits);


    //
    // If the length of the string without separators is n,
    // then the length of the string with separators is n + (n - 1) / 3
    //

    i = dwLength;
    dwLength += (dwLength - 1) / 3;


    //
    // Write the number to the buffer in reverse
    //

    TCHAR* pszsrc, *pszdst;

    pszsrc = szDigits + i - 1; pszdst = pszTemp + dwLength;

    *pszdst-- = TEXT('\0');

    while (TRUE) {
        if (i--) { *pszdst-- = *pszsrc--; } else { break; }
        if (i--) { *pszdst-- = *pszsrc--; } else { break; }
        if (i--) { *pszdst-- = *pszsrc--; } else { break; }
        if (i) { *pszdst-- = *szThousandsSeparator; } else { break; }
    }

	pszBuffer[0] = 0;
	
    if (fSigned)
		lstrcat(pszBuffer, szNegativeSign);

	lstrcat(pszBuffer, pszTemp);
}


//----------------------------------------------------------------------------
// Function:    padultoa
//
// This functions formats the specified unsigned integer
// into the specified string buffer, padding the buffer
// so that it is at least the specified width.
//
// It is assumed that the buffer is at least wide enough
// to contain the output, so this function does not truncate
// the conversion result to the length of the 'width' parameter.
//----------------------------------------------------------------------------

TCHAR*
padultoa(
    UINT    val,
    TCHAR*  pszBuf,
    INT     width
    ) {

    TCHAR temp;
    PTSTR psz, zsp;

    psz = pszBuf;

    //
    // write the digits in reverse order
    //

    do {

        *psz++ = TEXT('0') + (val % 10);
        val /= 10;

    } while(val > 0);

    //
    // pad the string to the required width
    //

    zsp = pszBuf + width;
    while (psz < zsp) { *psz++ = TEXT('0'); }


    *psz-- = TEXT('\0');


    //
    // reverse the digits
    //

    for (zsp = pszBuf; zsp < psz; zsp++, psz--) {

        temp = *psz; *psz = *zsp; *zsp = temp;
    }

    //
    // return the result
    //

    return pszBuf;
}


//----------------------------------------------------------------------------
// Function:    FormatListString
//
// Formats a list of strings as a single string, using the list-separator
// for the current-user's locale.
//----------------------------------------------------------------------------

VOID
FormatListString(
    IN  CStringList&    strList,
    IN  CString&        sListString,
    IN  LPCTSTR         pszSeparator
    ) {

    static TCHAR szListSeparator[4] = TEXT("");
    POSITION pos;

    sListString.Empty();

    pos = strList.GetHeadPosition();

    while (pos) {

        //
        // Add the next string
        //

        sListString += strList.GetNext(pos);


        //
        // If any strings are left, append the list-separator
        //

        if (pos) {

            //
            // Load the list-separator if necessary
            //

            if (!pszSeparator && szListSeparator[0] == TEXT('\0')) {

                GetLocaleInfo(
                    LOCALE_USER_DEFAULT, LOCALE_SLIST, szListSeparator, 4
                    );

                lstrcat(szListSeparator, TEXT(" "));
            }


            //
            // Append the list-separator
            //

            sListString += (pszSeparator ? pszSeparator : szListSeparator);
        }
    }
}


//----------------------------------------------------------------------------
// Function:    FormatHexBytes
//
// Formats an array of bytes as a string.
//----------------------------------------------------------------------------

VOID
FormatHexBytes(
    IN  BYTE*       pBytes,
    IN  DWORD       dwCount,
    IN  CString&    sBytes,
    IN  TCHAR       chDelimiter
    ) {

    TCHAR* psz;

    sBytes.Empty();

    if (!dwCount) { return; }

    psz = sBytes.GetBufferSetLength(dwCount * 3 + 1);

    for ( ; dwCount > 1; pBytes++, dwCount--) {

        *psz++ = c_szHexCharacters[*pBytes / 16];
        *psz++ = c_szHexCharacters[*pBytes % 16];
        *psz++ = chDelimiter;
    }

    *psz++ = c_szHexCharacters[*pBytes / 16];
    *psz++ = c_szHexCharacters[*pBytes % 16];
    *psz++ = TEXT('\0');

    sBytes.ReleaseBuffer();
}


/*!--------------------------------------------------------------------------
	DisplayErrorMessage
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
void DisplayErrorMessage(HWND hWndParent, HRESULT hr)
{
	if (FHrSucceeded(hr))
		return;

	TCHAR	szErr[2048];

	FormatSystemError(hr,
					  szErr,
					  DimensionOf(szErr),
					  0,
					  FSEFLAG_SYSMESSAGE | FSEFLAG_MPRMESSAGE
					 );
	AfxMessageBox(szErr);
//	::MessageBox(hWndParent, szErr, NULL, MB_OK);
}


/*!--------------------------------------------------------------------------
	DisplayStringErrorMessage2
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
void DisplayStringErrorMessage2(HWND hWndParent, LPCTSTR pszTopLevelText, HRESULT hr)
{
	if (FHrSucceeded(hr))
		return;

	TCHAR	szText[4096];
	TCHAR	szErr[2048];

	FormatSystemError(hr,
					  szErr,
					  DimensionOf(szErr),
					  0,
					  FSEFLAG_SYSMESSAGE | FSEFLAG_MPRMESSAGE
					 );
	StrnCpy(szText, pszTopLevelText, DimensionOf(szText));
	StrCat(szText, szErr);
	AfxMessageBox(szText);
//	::MessageBox(hWndParent, szErr, NULL, MB_OK);
}


/*!--------------------------------------------------------------------------
	DisplayIdErrorMessage2
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
void DisplayIdErrorMessage2(HWND hWndParent, UINT idsError, HRESULT hr)
{
	if (FHrSucceeded(hr))
		return;

	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	CString	stError;
	TCHAR	szErr[2048];

	stError.LoadString(idsError);
	
	FormatSystemError(hr,
					  szErr,
					  DimensionOf(szErr),
					  0,
					  FSEFLAG_SYSMESSAGE | FSEFLAG_MPRMESSAGE
					 );

	stError += szErr;
	AfxMessageBox(stError);
//	::MessageBox(hWndParent, szErr, NULL, MB_OK);
}


//----------------------------------------------------------------------------
// Function:    FormatDuration
//
// Formats a number as a duration, using the time-separator
// for the current-user's locale. dwBase is the number of units that make
// up a second (if dwBase==1 then the input is seconds, if dwBase==1000 then
// the input expected is in milliseconds.
//----------------------------------------------------------------------------


VOID
FormatDuration(
    IN  DWORD       dwDuration,
    IN  CString&    sDuration,
	IN	DWORD		dwTimeBase,
    IN  DWORD       dwFormatFlags
    ) {

    static TCHAR szTimeSeparator[4] = TEXT("");
    TCHAR *psz, szOutput[64];

    sDuration.Empty();

    if ((dwFormatFlags & FDFLAG_ALL) == 0) { return; }


    //
    // Retrieve the time-separator if necessary
    //

    if (szTimeSeparator[0] == TEXT('\0')) {

        GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STIME, szTimeSeparator, 4);
    }


    //
    // Concatenate the strings for the duration-components together
    //

    psz = szOutput;
    szOutput[0] = TEXT('\0');
    dwFormatFlags &= FDFLAG_ALL;


    if (dwFormatFlags & FDFLAG_DAYS) {

        //
        // Format the number of days if requested
        //

        padultoa(dwDuration / (24 * 60 * 60 * dwTimeBase), psz, 0);
        dwDuration %= (24 * 60 * 60 * dwTimeBase);

        //
        // Append the time-separator
        //

        if (dwFormatFlags &= ~FDFLAG_DAYS) { lstrcat(psz, szTimeSeparator); }

        psz += lstrlen(psz);
    }

    if (dwFormatFlags & FDFLAG_HOURS) {

        //
        // Format the number of hours if requested
        //

        padultoa(dwDuration / (60 * 60 * dwTimeBase), psz, 2);
        dwDuration %= (60 * 60 * dwTimeBase);

        //
        // Append the time-separator
        //

        if (dwFormatFlags &= ~FDFLAG_HOURS) { lstrcat(psz, szTimeSeparator); }

        psz += lstrlen(psz);
    }

    if (dwFormatFlags & FDFLAG_MINUTES) {

        //
        // Format the number of minutes
        //

        padultoa(dwDuration / (60 * dwTimeBase), psz, 2);
        dwDuration %= (60 * dwTimeBase);

        //
        // Append the time separator
        //

        if (dwFormatFlags &= ~FDFLAG_MINUTES) { lstrcat(psz, szTimeSeparator); }

        psz += lstrlen(psz);
    }

    if (dwFormatFlags & FDFLAG_SECONDS) {

        //
        // Format the number of seconds
        //

        padultoa(dwDuration / dwTimeBase, psz, 2);
        dwDuration %= dwTimeBase;

        //
        // Append the time-separator
        //

        if (dwFormatFlags &= ~FDFLAG_SECONDS) { lstrcat(psz, szTimeSeparator); }

        psz += lstrlen(psz);
    }

    if (dwFormatFlags & FDFLAG_MSECONDS) {

        //
        // Format the number of milliseconds
        //

        padultoa(dwDuration % dwTimeBase, psz, 0); psz += lstrlen(psz);
    }

    sDuration = szOutput;
}


/*---------------------------------------------------------------------------
	IfIndexToNameMapping implementation
 ---------------------------------------------------------------------------*/

IfIndexToNameMapping::IfIndexToNameMapping()
{
}

IfIndexToNameMapping::~IfIndexToNameMapping()
{
    // Iterate through the map and delete all the pointers
    POSITION    pos;
    LPVOID      pvKey, pvValue;

    for (pos = m_map.GetStartPosition(); pos; )
    {
        m_map.GetNextAssoc(pos, pvKey, pvValue);
        
        delete (CString *) pvValue;
        pvValue = NULL;
        m_map.SetAt(pvKey, pvValue);
    }
    m_map.RemoveAll();
}

/*!--------------------------------------------------------------------------
	IfIndexToNameMapping::Add
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
HRESULT IfIndexToNameMapping::Add(ULONG ulIndex, LPCTSTR pszName)
{
    HRESULT     hr = hrOK;
    LPVOID      pvKey, pvValue;

    COM_PROTECT_TRY
    {
        pvKey = (LPVOID) ULongToPtr( ulIndex );
        pvValue = NULL;
        
        // If we can find the value, don't add it
        if (m_map.Lookup(pvKey, pvValue) == 0)
        {
            pvValue = (LPVOID) new CString(pszName);
            m_map.SetAt(pvKey, pvValue);
        }
        
        Assert(((CString *) pvValue)->CompareNoCase(pszName) == 0);
        
    }
    COM_PROTECT_CATCH;
    
    return hr;
}

/*!--------------------------------------------------------------------------
	IfIndexToNameMapping::Find
		-
	Author: KennT
 ---------------------------------------------------------------------------*/
LPCTSTR IfIndexToNameMapping::Find(ULONG ulIndex)
{
    LPVOID  pvValue = NULL;
    
    if (m_map.Lookup((LPVOID) ULongToPtr(ulIndex), pvValue))
        return (LPCTSTR) *((CString *)pvValue);
    else
        return NULL;
}