//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       volclean.cpp
//
//  Authors;
//    Jeff Saathoff (jeffreys)
//
//  Notes;
//    CSC disk cleanup implementation (IEmptyVolumeCache)
//--------------------------------------------------------------------------
#include "pch.h"
#include "folder.h"


int
CoTaskLoadString(HINSTANCE hInstance, UINT idString, LPWSTR *ppwsz)
{
    int nResult = 0;

    *ppwsz = NULL;

    ULONG cchString = SizeofStringResource(hInstance, idString);
    if (cchString)
    {
        cchString++;    // for NULL
        *ppwsz = (LPWSTR)CoTaskMemAlloc(cchString * sizeof(WCHAR));
        if (*ppwsz)
            nResult = LoadStringW(hInstance, idString, *ppwsz, cchString);
    }

    return nResult;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// IClassFactory::CreateInstance support                                     //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

HRESULT WINAPI
CCscVolumeCleaner::CreateInstance(REFIID riid, LPVOID *ppv)
{
    return Create(FALSE, riid, ppv);
}

HRESULT WINAPI
CCscVolumeCleaner::CreateInstance2(REFIID riid, LPVOID *ppv)
{
    return Create(TRUE, riid, ppv);
}

HRESULT WINAPI
CCscVolumeCleaner::Create(BOOL fPinned, REFIID riid, LPVOID *ppv)
{
    HRESULT hr;

    CCscVolumeCleaner *pThis = new CCscVolumeCleaner(fPinned);
    if (pThis)
    {
        hr = pThis->QueryInterface(riid, ppv);
        pThis->Release();
    }
    else
        hr = E_OUTOFMEMORY;

    return hr;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// IUnknown implementation                                                   //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CCscVolumeCleaner::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] =
    {
        QITABENT(CCscVolumeCleaner, IEmptyVolumeCache),
        QITABENT(CCscVolumeCleaner, IEmptyVolumeCache2),
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CCscVolumeCleaner::AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CCscVolumeCleaner::Release()
{
    if (InterlockedDecrement(&m_cRef))
        return m_cRef;

    delete this;
    return 0;
}

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// IEmptyVolumeCache implementation                                          //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

STDMETHODIMP
CCscVolumeCleaner::Initialize(HKEY  /*hkRegKey*/,
                              LPCWSTR pcwszVolume,
                              LPWSTR *ppwszDisplayName,
                              LPWSTR *ppwszDescription,
                              LPDWORD pdwFlags)
{
    BOOL bSettingsMode;
    CSCSPACEUSAGEINFO sui = {0};

    USES_CONVERSION;

    TraceEnter(TRACE_SHELLEX, "IEmptyVolumeCache::Initialize");
    TraceAssert(pcwszVolume != NULL);
    TraceAssert(ppwszDisplayName != NULL);
    TraceAssert(ppwszDescription != NULL);
    TraceAssert(pdwFlags != NULL);
    TraceAssert(IsCSCEnabled());

    bSettingsMode = (BOOL)((*pdwFlags) & EVCF_SETTINGSMODE);

    *ppwszDisplayName = NULL;
    *ppwszDescription = NULL;
    *pdwFlags = 0;

    // If this isn't the volume containing the CSC database, then we have
    // nothing to free. Note that we don't use the space usage data
    // returned here.
    GetCscSpaceUsageInfo(&sui);
    if (!bSettingsMode && !PathIsSameRoot(sui.szVolume, W2CT(pcwszVolume)))
        TraceLeaveResult(S_FALSE);

    m_PurgerSel.SetFlags(m_fPinned ? PURGE_FLAG_PINNED : (PURGE_FLAG_UNPINNED | PURGE_IGNORE_ACCESS));
    m_pPurger = new CCachePurger(m_PurgerSel,
                                 CachePurgerCallback,
                                 this);
    if (!m_pPurger)
        TraceLeaveResult(E_FAIL);

    // If we're freeing auto-cached files, we want to be enabled by default,
    // but not if we're freeing pinned files.
    *pdwFlags = 0;
    if (!m_fPinned)
        *pdwFlags = EVCF_ENABLEBYDEFAULT | EVCF_ENABLEBYDEFAULT_AUTO;

    // If policy allows, turn on the "Details" button which launches the viewer
    if (!CConfig::GetSingleton().NoCacheViewer())
        *pdwFlags |= EVCF_HASSETTINGS;

    // Load the display name string
    CoTaskLoadString(g_hInstance,
                     m_fPinned ? IDS_APPLICATION : IDS_DISKCLEAN_DISPLAY,
                     ppwszDisplayName);

    // Load the description string
    CoTaskLoadString(g_hInstance,
                     m_fPinned ? IDS_DISKCLEAN_PIN_DESCRIPTION : IDS_DISKCLEAN_DESCRIPTION,
                     ppwszDescription);

    TraceLeaveResult(S_OK);
}

STDMETHODIMP
CCscVolumeCleaner::GetSpaceUsed(DWORDLONG *pdwlSpaceUsed,
                                LPEMPTYVOLUMECACHECALLBACK picb)
{
    m_pDiskCleaner = picb;
    m_pPurger->Scan();
    if (m_pDiskCleaner)
        m_pDiskCleaner->ScanProgress(m_dwlSpaceToFree,
                                     EVCCBF_LASTNOTIFICATION,
                                     NULL);
    *pdwlSpaceUsed = m_dwlSpaceToFree;
    return S_OK;
}

STDMETHODIMP
CCscVolumeCleaner::Purge(DWORDLONG /*dwlSpaceToFree*/,
                         LPEMPTYVOLUMECACHECALLBACK picb)
{
    m_pDiskCleaner = picb;
    m_pPurger->Delete();
    if (m_pDiskCleaner)
        m_pDiskCleaner->PurgeProgress(m_dwlSpaceFreed,
                                      0,
                                      EVCCBF_LASTNOTIFICATION,
                                      NULL);
    return S_OK;
}

STDMETHODIMP
CCscVolumeCleaner::ShowProperties(HWND /*hwnd*/)
{
    // Launch the viewer
    COfflineFilesFolder::Open();
    return S_FALSE;
}

STDMETHODIMP
CCscVolumeCleaner::Deactivate(LPDWORD /*pdwFlags*/)
{
    // nothing to do here
    return S_OK;
}

// IEmptyVolumeCache2 method
STDMETHODIMP
CCscVolumeCleaner::InitializeEx(HKEY hkRegKey,
                                LPCWSTR pcwszVolume,
                                LPCWSTR pcwszKeyName,
                                LPWSTR *ppwszDisplayName,
                                LPWSTR *ppwszDescription,
                                LPWSTR *ppwszBtnText,
                                LPDWORD pdwFlags)
{
    HRESULT hr = Initialize(hkRegKey,
                            pcwszVolume,
                            ppwszDisplayName,
                            ppwszDescription,
                            pdwFlags);
    if (S_OK == hr)
        CoTaskLoadString(g_hInstance, IDS_DISKCLEAN_BTN_TEXT, ppwszBtnText);
    return hr;
}

BOOL
CCscVolumeCleaner::ScanCallback(CCachePurger *pPurger)
{
    BOOL bContinue = TRUE;

    // If the pinned state matches what we're looking for, add the
    // size to the total.
    if (pPurger->WillDeleteThisFile())
        m_dwlSpaceToFree += pPurger->FileBytes();

    if (m_pDiskCleaner)
        bContinue = SUCCEEDED(m_pDiskCleaner->ScanProgress(m_dwlSpaceToFree,
                                                           0,
                                                           NULL));
    return bContinue;
}

BOOL
CCscVolumeCleaner::DeleteCallback(CCachePurger *pPurger)
{
    BOOL bContinue = TRUE;

    // Don't let this go below zero
    m_dwlSpaceToFree -= min(pPurger->FileBytes(), m_dwlSpaceToFree);
    m_dwlSpaceFreed += pPurger->FileBytes();

    if (m_pDiskCleaner)
        bContinue = SUCCEEDED(m_pDiskCleaner->PurgeProgress(m_dwlSpaceFreed,
                                                            m_dwlSpaceToFree,
                                                            0,
                                                            NULL));
    return bContinue;
}

BOOL CALLBACK
CCscVolumeCleaner::CachePurgerCallback(CCachePurger *pPurger)
{
    PCSCVOLCLEANER pThis = (PCSCVOLCLEANER)pPurger->CallbackData();
    switch (pPurger->Phase())
    {
    case PURGE_PHASE_SCAN:
        return pThis->ScanCallback(pPurger);
    case PURGE_PHASE_DELETE:
        return pThis->DeleteCallback(pPurger);
    }
    return FALSE;
}