//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       filesize.cpp
//
//--------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop

#include "filesize.h"
#include "util.h"
#include <resource.h>

//-----------------------------------------------------------------------------
// class FileSize
//-----------------------------------------------------------------------------
//
// Static array of file size "order" string IDs.
// These IDs identify the "%1 KB", "%1 MB", "%1 GB" resource strings.
//
int FileSize::m_rgiOrders[IDS_ORDER_EB - IDS_ORDER_BYTES + 1];

FileSize::FileSize(
    ULONGLONG ullSize
    ) : m_ullSize(ullSize)
{
    TraceAssert(IDS_ORDER_BYTES != 0);
    if (0 == m_rgiOrders[0])
    {
        //
        // Initialize the static array of file size "order" string IDs.
        //
        for (int i = IDS_ORDER_BYTES; i <= IDS_ORDER_EB; i++)
        {
            m_rgiOrders[i - IDS_ORDER_BYTES] = i;
        }
    }
}


//
// FileSize assignment.
//
FileSize& 
FileSize::operator = (
    const FileSize& rhs
    )
{
    if (this != &rhs)
    {
        m_ullSize = rhs.m_ullSize;
    }
    return *this;
}


//
// The following code for converting a file size value to a text
// string (i.e. "10.5 MB") was taken from shell32.dll so that file size
// values would match those displayed in shell views.  The code isn't
// my normal style but I left it "as is" so I wouldn't break it. [brianau]
//
const int MAX_INT64_SIZE        = 30;
const int MAX_COMMA_NUMBER_SIZE = MAX_INT64_SIZE + 10;

//
// Convert a ULONGLONG file size value to a text string.
//
void 
FileSize::CvtSizeToText(
    ULONGLONG n, 
    LPTSTR pszBuffer
    ) const
{
    TCHAR     szTemp[MAX_INT64_SIZE];
    ULONGLONG iChr;

    iChr = 0;

    do {
        szTemp[iChr++] = (TCHAR)(TEXT('0') + (TCHAR)(n % 10));
        n = n / 10;
    } while (n != 0);

    do {
        iChr--;
        *pszBuffer++ = szTemp[iChr];
    } while (iChr != 0);

    *pszBuffer++ = '\0';
}


//
// Convert a string to an integer (taken from shlwapi.dll).
//
int
FileSize::StrToInt(
    LPCTSTR lpSrc
    ) const
{
    int n = 0;
    BOOL bNeg = FALSE;

    if (*lpSrc == TEXT('-')) {
        bNeg = TRUE;
        lpSrc++;
    }

    while (IsDigit(*lpSrc)) {
        n *= 10;
        n += *lpSrc - TEXT('0');
        lpSrc++;
    }
    return bNeg ? -n : n;
}


//
// Add commas where necessary to a number with more than 3 digits.
//
LPTSTR 
FileSize::AddCommas(
    ULONGLONG n, 
    LPTSTR pszResult,
    int cchResult
    ) const
{
    TCHAR  szTemp[MAX_COMMA_NUMBER_SIZE];
    TCHAR  szSep[5];
    NUMBERFMT nfmt;

    nfmt.NumDigits=0;
    nfmt.LeadingZero=0;
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szSep, ARRAYSIZE(szSep));
    nfmt.Grouping = StrToInt(szSep);
    GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYSIZE(szSep));
    nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
    nfmt.NegativeOrder= 0;

    CvtSizeToText(n, szTemp);

    if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, cchResult) == 0)
        lstrcpy(pszResult, szTemp);

    return pszResult;
}


//
// Format a file size value as a text string suitable for viewing.
//
void
FileSize::Format(
    ULONGLONG ullSize,
    LPTSTR pszFS,
    UINT cchFS
    ) const
{
    TraceAssert(NULL != pszFS);
    TraceAssert(0 < cchFS);

    int i;
    ULONGLONG wInt;
    ULONGLONG dw64 = ullSize;
    UINT wLen, wDec;
    TCHAR szTemp[MAX_COMMA_NUMBER_SIZE], szFormat[5];

    if (dw64 < 1000) 
    {
        wsprintf(szTemp, TEXT("%d"), (DWORD)(dw64));
        i = 0;
    }
    else
    {
        int cOrders = ARRAYSIZE(m_rgiOrders);
        for (i = 1; i < cOrders - 1 && dw64 >= 1000L * 1024L; dw64 >>= 10, i++);
            /* do nothing */

        wInt = dw64 >> 10;
        AddCommas(wInt, szTemp, ARRAYSIZE(szTemp));
        wLen = lstrlen(szTemp);
        if (wLen < 3)
        {
            wDec = ((DWORD)(dw64 - wInt * 1024L)) * 1000 / 1024;
            // At this point, wDec should be between 0 and 1000
            // we want get the top one (or two) digits.
            wDec /= 10;
            if (wLen == 2)
                wDec /= 10;

            // Note that we need to set the format before getting the
            // intl char.
            lstrcpy(szFormat, TEXT("%02d"));

            szFormat[2] = (TCHAR)(TEXT('0') + 3 - wLen);
            GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
                    szTemp+wLen, ARRAYSIZE(szTemp)-wLen);
            wLen = lstrlen(szTemp);
            wLen += wsprintf(szTemp+wLen, szFormat, wDec);
        }
    }
    *pszFS = TEXT('\0');
    LPTSTR pszText;
    if (0 < FormatStringID(&pszText, g_hInstance, m_rgiOrders[i], szTemp))
    {
        lstrcpyn(pszFS, pszText, cchFS);
        LocalFree(pszText);
    }
}

