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

#ifndef _UTIL_H_
#define _UTIL_H_

#ifndef _INC_CSCVIEW_CONFIG_H
#   include "config.h"
#endif

#include "debug.h"

HRESULT GetRemotePath(LPCTSTR szInName, LPTSTR *pszOutName);
LPTSTR ULongToString(ULONG i, LPTSTR psz, ULONG cchMax);
VOID LocalFreeString(LPTSTR *ppsz);
BOOL LocalAllocString(LPTSTR *ppszDest, LPCTSTR pszSrc);
UINT SizeofStringResource(HINSTANCE hInstance, UINT idStr);
int LoadStringAlloc(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr);
void ShellChangeNotify(LPCTSTR pszPath, WIN32_FIND_DATA *pfd, BOOL bFlush, LONG nEvent = 0);
inline void ShellChangeNotify(LPCTSTR pszPath, BOOL bFlush = FALSE, LONG nEvent = 0) {ShellChangeNotify(pszPath, NULL, bFlush, nEvent);}
HRESULT GetLinkTarget(LPCTSTR pszShortcut, LPTSTR *ppszTarget, DWORD *pdwAttr = NULL);
void CenterWindow(HWND hwnd, HWND hwndParent);
DWORD CSCUIRebootSystem(void);
HRESULT SHSimpleIDListFromFindData(LPCTSTR pszPath, const WIN32_FIND_DATA *pfd, LPITEMIDLIST *ppidl);
DWORD CscDelete(LPCTSTR pszPath);
HRESULT IsOpenConnectionShare(LPCTSTR pszShare);
HRESULT IsOpenConnectionPathUNC(LPCTSTR pszPathUNC);
BOOL IsCSCEnabled(void);
BOOL IsCacheEncrypted(BOOL *pbPartial);
BOOL IsSyncInProgress(void);
BOOL IsPurgeInProgress(void);
BOOL IsEncryptionInProgress(void);
HANDLE RequestPermissionToEncryptCache(void);
bool CscVolumeSupportsEncryption(LPCTSTR pszPath = NULL);
BOOL IsWindowsTerminalServer(void);
HRESULT SHCreateFileSysBindCtx(const WIN32_FIND_DATA *pfd, IBindCtx **ppbc);
BOOL DeleteOfflineFilesFolderLink(HWND hwndParent = NULL);
BOOL DeleteOfflineFilesFolderLink_PerfSensitive(HWND hwndParent = NULL);
BOOL ShowHidden(void);
BOOL ShowSuperHidden(void);
BOOL IsSyncMgrInitialized(void);
void SetSyncMgrInitialized(void);
HWND GetProgressDialogWindow(IProgressDialog *ppd);
HRESULT ExpandStringInPlace(LPTSTR psz, DWORD cch);


//
// Info returned through CSCFindFirst[Next]File APIs.
//
struct CscFindData
{
    WIN32_FIND_DATA fd;
    DWORD           dwStatus;
    DWORD           dwPinCount;
    DWORD           dwHintFlags;
    FILETIME        ft;        
};

HANDLE CacheFindFirst(LPCTSTR pszPath, PSID psid, WIN32_FIND_DATA *pfd, DWORD *pdwStatus, DWORD *pdwPinCount, DWORD *pdwHintFlags, FILETIME *pft);

inline 
HANDLE CacheFindFirst(LPCTSTR pszPath, WIN32_FIND_DATA *pfd, DWORD *pdwStatus, DWORD *pdwPinCount, DWORD *pdwHintFlags, FILETIME *pft)
    { return CacheFindFirst(pszPath, (PSID)NULL, pfd, pdwStatus, pdwPinCount, pdwHintFlags, pft); }

inline 
HANDLE CacheFindFirst(LPCTSTR pszPath, CscFindData *p)
    { return CacheFindFirst(pszPath, &p->fd, &p->dwStatus, &p->dwPinCount, &p->dwHintFlags, &p->ft); }

inline 
HANDLE CacheFindFirst(LPCTSTR pszPath, PSID psid, CscFindData *p)
    { return CacheFindFirst(pszPath, psid, &p->fd, &p->dwStatus, &p->dwPinCount, &p->dwHintFlags, &p->ft); }

BOOL CacheFindNext(HANDLE hFind, WIN32_FIND_DATA *pfd, DWORD *pdwStatus, DWORD *pdwPinCount, DWORD *pdwHintFlags, FILETIME *pft);

inline
BOOL CacheFindNext(HANDLE hFind, CscFindData *p)
    { return CacheFindNext(hFind, &p->fd, &p->dwStatus, &p->dwPinCount, &p->dwHintFlags, &p->ft); }


inline bool IsHiddenSystem(DWORD dwAttr)
{
    return ((dwAttr & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM));
}


inline BOOL _PathIsSlow(DWORD dwSpeed) { return (dwSpeed && dwSpeed <= DWORD(CConfig::GetSingleton().SlowLinkSpeed())); }


typedef struct
{
    TCHAR    szVolume[80];         // Volume where CSC cache is stored.
    LONGLONG llBytesOnVolume;      // Disk size (bytes)
    LONGLONG llBytesTotalInCache;  // Size of cache (bytes)
    LONGLONG llBytesUsedInCache;   // Amount of cache used (bytes)
    DWORD    dwNumFilesInCache;    // Files in cache
    DWORD    dwNumDirsInCache;     // Directories in cache

} CSCSPACEUSAGEINFO;

void GetCscSpaceUsageInfo(CSCSPACEUSAGEINFO *psui);

typedef enum _enum_reason
{
    ENUM_REASON_FILE = 0,
    ENUM_REASON_FOLDER_BEGIN,
    ENUM_REASON_FOLDER_END
} ENUM_REASON;

typedef DWORD (WINAPI *PFN_CSCENUMPROC)(LPCTSTR, ENUM_REASON, DWORD, DWORD, DWORD, PWIN32_FIND_DATA, LPARAM);
DWORD _CSCEnumDatabase(LPCTSTR pszFolder, BOOL bRecurse, PFN_CSCENUMPROC pfnCB, LPARAM lpContext);

typedef DWORD (WINAPI *PFN_WIN32ENUMPROC)(LPCTSTR, ENUM_REASON, PWIN32_FIND_DATA, LPARAM);
DWORD _Win32EnumFolder(LPCTSTR pszFolder, BOOL bRecurse, PFN_WIN32ENUMPROC pfnCB, LPARAM lpContext);


//
// Statistical information about a particular network share in the CSC database.
//
typedef struct _CSCSHARESTATS
{
    int cTotal;
    int cPinned;
    int cModified;
    int cSparse;
    int cDirs;
    int cAccessUser;
    int cAccessGuest;
    int cAccessOther;
    bool bOffline;
    bool bOpenFiles;
} CSCSHARESTATS, *PCSCSHARESTATS;

typedef struct
{
    int cShares;
    int cTotal;
    int cPinned;
    int cModified;
    int cSparse;
    int cDirs;
    int cAccessUser;
    int cAccessGuest;
    int cAccessOther;
    int cSharesOffline;
    int cSharesWithOpenFiles;
} CSCCACHESTATS, *PCSCCACHESTATS;


//
// These flags indicate if the enumeration should stop when one or more associated 
// value's exceed 1.  This is useful when you're interested in 0 vs. !0 as opposed
// to an actual count.
// If multiple flags are set, the statistics enumeration continues until the
// values corresponding to ALL set unity flags are non-zero.
//
enum SHARE_STATS_UNITY_FLAGS { SSUF_NONE     = 0x00000000,   // This is the default.
                               SSUF_TOTAL    = 0x00000001,
                               SSUF_PINNED   = 0x00000002,
                               SSUF_MODIFIED = 0x00000004,
                               SSUF_SPARSE   = 0x00000008,
                               SSUF_DIRS     = 0x00000010,
                               SSUF_ACCUSER  = 0x00000020,
                               SSUF_ACCGUEST = 0x00000040,
                               SSUF_ACCOTHER = 0x00000080,
                               SSUF_ACCAND   = 0x00000100, // Must match all set access mask flags.
                               SSUF_ACCOR    = 0x00000200, // Match at least one access mask flag.
                               SSUF_ALL      = 0x000000FF };
//
// These flags indicate if any cache items should be excluded from the enumeration.
// By default, the value is 0 (everything included).  For perf reasons, we use the
// same flags defined in cscapi.h.
//
enum SHARE_STATS_EXCLUDE_FLAGS {
        SSEF_NONE               = 0x00000000,  // Default.  Include everything.
        SSEF_LOCAL_MOD_DATA     = FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED,
        SSEF_LOCAL_MOD_ATTRIB   = FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED,
        SSEF_LOCAL_MOD_TIME     = FLAG_CSC_COPY_STATUS_TIME_LOCALLY_MODIFIED,
        SSEF_LOCAL_DELETED      = FLAG_CSC_COPY_STATUS_LOCALLY_DELETED,
        SSEF_LOCAL_CREATED      = FLAG_CSC_COPY_STATUS_LOCALLY_CREATED,
        SSEF_STALE              = FLAG_CSC_COPY_STATUS_STALE,
        SSEF_SPARSE             = FLAG_CSC_COPY_STATUS_SPARSE,
        SSEF_ORPHAN             = FLAG_CSC_COPY_STATUS_ORPHAN,
        SSEF_SUSPECT            = FLAG_CSC_COPY_STATUS_SUSPECT,
        SSEF_CSCMASK            = FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED |
                                  FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED |
                                  FLAG_CSC_COPY_STATUS_TIME_LOCALLY_MODIFIED |
                                  FLAG_CSC_COPY_STATUS_LOCALLY_DELETED |
                                  FLAG_CSC_COPY_STATUS_LOCALLY_CREATED |
                                  FLAG_CSC_COPY_STATUS_STALE |
                                  FLAG_CSC_COPY_STATUS_SPARSE |
                                  FLAG_CSC_COPY_STATUS_ORPHAN |
                                  FLAG_CSC_COPY_STATUS_SUSPECT,
        SSEF_DIRECTORY          = 0x01000000,
        SSEF_FILE               = 0x02000000,
        SSEF_NOACCUSER          = 0x04000000,  // Exclude if no USER access.
        SSEF_NOACCGUEST         = 0x08000000,  // Exclude if no GUEST access.
        SSEF_NOACCOTHER         = 0x10000000,  // Exclude if no OTHER access.
        SSEF_NOACCAND           = 0x20000000   // Treat previous 3 flags as single mask.
        };

typedef struct
{
    DWORD dwExcludeFlags;  // [in] SSEF_XXXXX flags.
    DWORD dwUnityFlags;    // [in] SSUF_XXXXX flags.
    bool bAccessInfo;      // [in] Implied 'T' if unity or exclude access bits are set.
    bool bEnumAborted;     // [out]

} CSCGETSTATSINFO, *PCSCGETSTATSINFO;

BOOL _GetShareStatistics(LPCTSTR pszShare, PCSCGETSTATSINFO pi, PCSCSHARESTATS pss);
BOOL _GetCacheStatistics(PCSCGETSTATSINFO pi, PCSCCACHESTATS pcs);
BOOL _GetShareStatisticsForUser(LPCTSTR pszShare, PCSCGETSTATSINFO pi, PCSCSHARESTATS pss);
BOOL _GetCacheStatisticsForUser(PCSCGETSTATSINFO pi, PCSCCACHESTATS pcs);

// Higher level wrapper for IDA stuff
class CIDArray
{
private:
    STGMEDIUM       m_Medium;
    LPIDA           m_pIDA;
    IShellFolder   *m_psf;

public:
    CIDArray() : m_pIDA(NULL), m_psf(NULL)
    {
        ZeroMemory(&m_Medium, sizeof(m_Medium));
    }

    ~CIDArray();

    HRESULT Initialize(IDataObject *pdobj);

    HRESULT GetFolderPath(LPTSTR pszPath, UINT cch);
    HRESULT GetItemPath(UINT iItem, LPTSTR pszPath, UINT cch, DWORD *pdwAttribs);
    UINT Count() { return m_pIDA ? m_pIDA->cidl : 0; }
};


//
// Trivial class to ensure cleanup of FindFirst/FindNext handles.
// Perf should be as close as possible to a simple handle so most
// operations are defined inline.
// Implementation is in enum.cpp
//
class CCscFindHandle
{
    public:
        CCscFindHandle(HANDLE handle = INVALID_HANDLE_VALUE)
            : m_handle(handle), m_bOwns(INVALID_HANDLE_VALUE != handle) { }

        CCscFindHandle(const CCscFindHandle& rhs)
            : m_handle(INVALID_HANDLE_VALUE), m_bOwns(false)
            { *this = rhs; }

        ~CCscFindHandle(void)
            { Close(); }

        void Close(void);

        HANDLE Detach(void) const
            { m_bOwns = false; return m_handle; }

        void Attach(HANDLE handle)
            { Close(); m_handle = handle; m_bOwns = true; }

        operator HANDLE() const
            { return m_handle; }

        bool IsValid(void) const
            { return INVALID_HANDLE_VALUE != m_handle; }

        CCscFindHandle& operator = (HANDLE handle)
            { Attach(handle); return *this; }

        CCscFindHandle& operator = (const CCscFindHandle& rhs);

    private:
        mutable HANDLE m_handle;
        mutable bool   m_bOwns;
};


class CMutexAutoRelease
{
    public:
        explicit CMutexAutoRelease(HANDLE hmutex)
            : m_hmutex(hmutex) { }

        ~CMutexAutoRelease(void)
        { 
            if (m_hmutex)
            { 
                ReleaseMutex(m_hmutex);
                CloseHandle(m_hmutex);
            }
        }

    private:
        HANDLE m_hmutex;

        CMutexAutoRelease(const CMutexAutoRelease& rhs);
        CMutexAutoRelease& operator = (const CMutexAutoRelease& rhs);
};



//
// Ensures CoInitialize/CoUninitialize is exception-safe.
//
class CCoInit
{
    public:
        CCoInit(void)
            : m_hr(CoInitialize(NULL)) { }

        ~CCoInit(void)
            { if (SUCCEEDED(m_hr)) CoUninitialize(); }

        HRESULT Result(void) const
            { return m_hr; }
    private:
        HRESULT m_hr;
};


// String formatting functions - *ppszResult must be LocalFree'd
DWORD FormatStringID(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr, ...);
DWORD FormatString(LPTSTR *ppszResult, LPCTSTR pszFormat, ...);
DWORD FormatSystemError(LPTSTR *ppszResult, DWORD dwSysError);
DWORD vFormatStringID(LPTSTR *ppszResult, HINSTANCE hInstance, UINT idStr, va_list *pargs);
DWORD vFormatString(LPTSTR *ppszResult, LPCTSTR pszFormat, va_list *pargs);


void EnableDlgItems(HWND hwndDlg, const UINT* pCtlIds, int cCtls, bool bEnable);
void ShowDlgItems(HWND hwndDlg, const UINT* pCtlIds, int cCtls, bool bShow);


//
// We commonly use groups of CSC status flags together.
// Define them here so we're consistent throughout the project.
//
#define FLAG_CSCUI_COPY_STATUS_LOCALLY_DIRTY        (FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED | \
                                                     FLAG_CSC_COPY_STATUS_LOCALLY_DELETED | \
                                                     FLAG_CSC_COPY_STATUS_LOCALLY_CREATED)

#define FLAG_CSCUI_COPY_STATUS_ALL_DIRTY            (FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED | \
                                                     FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED | \
                                                     FLAG_CSC_COPY_STATUS_TIME_LOCALLY_MODIFIED | \
                                                     FLAG_CSC_COPY_STATUS_LOCALLY_DELETED | \
                                                     FLAG_CSC_COPY_STATUS_LOCALLY_CREATED)

//
// Some helper inlines for querying cache item access information.
//
inline bool CscCheckAccess(DWORD dwShareStatus, DWORD dwShift, DWORD dwAccessType)
{
    return 0 != ((dwShareStatus >> dwShift) & dwAccessType);
}

inline bool CscAccessUserRead(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_USER_ACCESS_SHIFT_COUNT, FLAG_CSC_READ_ACCESS);
}

inline bool CscAccessUserWrite(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_USER_ACCESS_SHIFT_COUNT, FLAG_CSC_WRITE_ACCESS);
}

inline bool CscAccessUser(DWORD dwShareStatus)
{
    return 0 != (dwShareStatus & FLAG_CSC_USER_ACCESS_MASK);
}

inline bool CscAccessGuestRead(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_GUEST_ACCESS_SHIFT_COUNT, FLAG_CSC_READ_ACCESS);
}

inline bool CscAccessGuestWrite(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_GUEST_ACCESS_SHIFT_COUNT, FLAG_CSC_WRITE_ACCESS);
}

inline bool CscAccessGuest(DWORD dwShareStatus)
{
    return 0 != (dwShareStatus & FLAG_CSC_GUEST_ACCESS_MASK);
}

inline bool CscAccessOtherRead(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_OTHER_ACCESS_SHIFT_COUNT, FLAG_CSC_READ_ACCESS);
}

inline bool CscAccessOtherWrite(DWORD dwShareStatus)
{
    return CscCheckAccess(dwShareStatus, FLAG_CSC_OTHER_ACCESS_SHIFT_COUNT, FLAG_CSC_WRITE_ACCESS);
}

inline bool CscAccessOther(DWORD dwShareStatus)
{
    return 0 != (dwShareStatus & FLAG_CSC_OTHER_ACCESS_MASK);
}

inline bool CscCanUserMergeFile(DWORD dwStatus)
{
    return (CscAccessUserWrite(dwStatus) || CscAccessGuestWrite(dwStatus));
}

//
// template inlines avoid the side-effects of min/max macros.
//
template <class T>
inline const T&
MAX(const T& a, const T& b)
{
    return a > b ? a : b;
}

template <class T>
inline const T&
MIN(const T& a, const T& b)
{
    return a < b ? a : b;
}

class CWin32Handle
{
    public:
        CWin32Handle(HANDLE handle)
            : m_handle(handle) { }

        CWin32Handle(void)
            : m_handle(NULL) { }

        ~CWin32Handle(void)
            { Close(); }

        void Close(void)
            { if (m_handle) CloseHandle(m_handle); m_handle = NULL; }

        operator HANDLE() const
            { return m_handle; }

        HANDLE *HandlePtr(void)
            { TraceAssert((NULL == m_handle)); return &m_handle; }

    private:
        HANDLE m_handle;

        //
        // Prevent copy.
        // This class is only intended for automatic handle cleanup.
        //
        CWin32Handle(const CWin32Handle& rhs);
        CWin32Handle& operator = (const CWin32Handle& rhs);
};



HRESULT DataObject_SetGlobal(IDataObject *pdtobj, CLIPFORMAT cf, HGLOBAL hGlobal);
HRESULT DataObject_SetDWORD(IDataObject *pdtobj, CLIPFORMAT cf, DWORD dw);
HRESULT DataObject_GetDWORD(IDataObject *pdtobj, CLIPFORMAT cf, DWORD *pdwOut);
HRESULT SetPreferredDropEffect(IDataObject *pdtobj, DWORD dwEffect);
DWORD   GetPreferredDropEffect(IDataObject *pdtobj);
HRESULT SetLogicalPerformedDropEffect(IDataObject *pdtobj, DWORD dwEffect);
DWORD   GetLogicalPerformedDropEffect(IDataObject *pdtobj);



//
// Simple class for automating the display and resetting of a wait cursor.
//
class CAutoWaitCursor
{
    public:
        CAutoWaitCursor(void)
            : m_hCursor(SetCursor(LoadCursor(NULL, IDC_WAIT))) 
            { ShowCursor(TRUE); }

        ~CAutoWaitCursor(void)
            { Reset(); }

        void Reset(void);

    private:
        HCURSOR m_hCursor;
};



class CAutoSetRedraw
{
    public:
        CAutoSetRedraw(HWND hwnd)
            : m_hwnd(hwnd) { }

        CAutoSetRedraw(HWND hwnd, bool bSet)
            : m_hwnd(hwnd) { Set(bSet); }

        ~CAutoSetRedraw(void)
            { Set(true); }

        void Set(bool bSet)
            { SendMessage(m_hwnd, WM_SETREDRAW, (WPARAM)bSet, 0); }

    private:
        HWND m_hwnd;
};




#endif  // _UTIL_H_

