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

/*----------------------------------------------------------------------------
/ Title;
/   debug.cpp
/
/ Authors;
/   David De Vorchik (daviddv)
/
/ Notes;
/   Provides printf style debug output
/----------------------------------------------------------------------------*/
#include "precomp.hxx"
#include "stdio.h"
#pragma hdrstop


#ifdef DEBUG


/*-----------------------------------------------------------------------------
/ Locals & helper functions
/----------------------------------------------------------------------------*/

#define GETDEPTH(x)  (x)=reinterpret_cast<UINT_PTR>(TlsGetValue (g_dwMargin));
#define SETDEPTH(x)  TlsSetValue (g_dwMargin, reinterpret_cast<LPVOID>((x)));


DWORD g_dwMargin=0;
DWORD g_dwTraceMask = 0;

#define MAX_CALL_DEPTH  64


#define BUFFER_SIZE 4096




/*-----------------------------------------------------------------------------
/ _indent
/ -------
/   Output to the debug stream indented by n columns.
/
/ In:
/   i = column to indent to.
/   pString -> string to be indented
/
/ Out:
/   -
/----------------------------------------------------------------------------*/

void _indent(UINT_PTR i, LPCTSTR pString)
{
    TCHAR szIndentBuffer[BUFFER_SIZE];
    szIndentBuffer[0] = TEXT('\0');

    wsprintf(szIndentBuffer, TEXT("%08x "), GetCurrentThreadId());

    for ( ; i > 0 ; i-- )
        lstrcat(szIndentBuffer, TEXT(" "));

    lstrcat(szIndentBuffer, pString);
    lstrcat(szIndentBuffer, TEXT("\n"));

    OutputDebugString(szIndentBuffer);
}





/*-----------------------------------------------------------------------------
/ DoTraceSetMask
/ --------------
/   Adjust the trace mask to reflect the state given.
/
/ In:
/   dwMask = mask for enabling / disable trace output
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
void DoTraceSetMask(DWORD dwMask)
{

    g_dwTraceMask = dwMask;
}


/*-----------------------------------------------------------------------------
/ DoTraceEnter
/ ------------
/   Display the name of the function we are in.
/
/ In:
/   pName -> function name to be displayed in subsequent trace output.
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
void DoTraceEnter(DWORD dwMask, LPCTSTR pName)
{
    UINT_PTR uDepth=0;
    TCHAR szStr[300];
    GETDEPTH(uDepth);
    uDepth++;
    SETDEPTH(uDepth);

    if ( !pName )
           pName = TEXT("<no name>");         // no function name given

    wsprintf (szStr, TEXT("ENTER: %s"), pName);
    _indent (uDepth, szStr);
}


/*-----------------------------------------------------------------------------
/ DoTraceLeave
/ ------------
/   On exit from a function, decrement the margin
/
/ In:
/    -
/ Out:
/   -
/----------------------------------------------------------------------------*/
void DoTraceLeave(void)
{
    UINT_PTR uDepth;
        GETDEPTH (uDepth);
        uDepth--;
        SETDEPTH(uDepth);

}


/*-----------------------------------------------------------------------------
/ DoTrace
/ -------
/   Perform printf formatting to the debugging stream.  We indent the output
/   and stream the function name as required to give some indication of
/   call stack depth.
/
/ In:
/   pFormat -> printf style formatting string
/   ... = arguments as required for the formatting
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
void DoTrace(LPCTSTR pFormat, ...)
{
    va_list va;
    TCHAR szTraceBuffer[BUFFER_SIZE];
    UINT_PTR uDepth;
    GETDEPTH(uDepth);
    if ( uDepth < MAX_CALL_DEPTH  )
    {
        va_start(va, pFormat);
        wvsprintf(szTraceBuffer, pFormat, va);
        va_end(va);

        _indent(uDepth+1, szTraceBuffer);
    }
}


/*-----------------------------------------------------------------------------
/ DoTraceGuid
/ -----------
/   Given a GUID output it into the debug string, first we try and map it
/   to a name (ie. IShellFolder), if that didn't work then we convert it
/   to its human readable form.
/
/ In:
/   pszPrefix -> prefix string
/   lpGuid -> guid to be streamed
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
#ifdef UNICODE
#define MAP_GUID(x)     &x, TEXT(""L#x)
#else
#define MAP_GUID(x)     &x, TEXT(""#x)
#endif

#define MAP_GUID2(x,y)  MAP_GUID(x), MAP_GUID(y)

const struct
{
    const GUID* m_pGUID;
    LPCTSTR     m_pName;
}
_guid_map[] =
{
    MAP_GUID(IID_IUnknown),
    MAP_GUID(IID_IClassFactory),
    MAP_GUID(IID_IDropTarget),
    MAP_GUID(IID_IDataObject),
    MAP_GUID(IID_IPersist),
    MAP_GUID(IID_IPersistStream),
    MAP_GUID(IID_IPersistFolder),
    MAP_GUID(IID_IPersistFolder2),
    MAP_GUID(IID_IPersistFile),
    MAP_GUID(IID_IOleWindow),
    MAP_GUID2(IID_INewShortcutHookA, IID_INewShortcutHookW),
    MAP_GUID(IID_IShellBrowser),
    MAP_GUID(IID_IShellView),
    MAP_GUID(IID_IContextMenu),
    MAP_GUID(IID_IShellIcon),
    MAP_GUID(IID_IShellFolder),
    MAP_GUID(IID_IShellExtInit),
    MAP_GUID(IID_IShellPropSheetExt),
    MAP_GUID2(IID_IExtractIconA, IID_IExtractIconW),
    MAP_GUID2(IID_IShellLinkA, IID_IShellLinkW),
    MAP_GUID2(IID_IShellCopyHookA, IID_IShellCopyHookW),
    MAP_GUID2(IID_IFileViewerA, IID_IFileViewerW),
    MAP_GUID(IID_ICommDlgBrowser),
    MAP_GUID(IID_IEnumIDList),
    MAP_GUID(IID_IFileViewerSite),
    MAP_GUID(IID_IContextMenu2),
    MAP_GUID2(IID_IShellExecuteHookA, IID_IShellExecuteHookW),
    MAP_GUID(IID_IPropSheetPage),
    MAP_GUID(IID_IShellView2),
    MAP_GUID(IID_IUniformResourceLocator),
    MAP_GUID(IID_IShellDetails),
    MAP_GUID(IID_IShellExtInit),
    MAP_GUID(IID_IShellPropSheetExt),
    MAP_GUID(IID_IShellIconOverlay),
    MAP_GUID(IID_IExtractImage),
    MAP_GUID(IID_IExtractImage2),
    MAP_GUID(IID_IQueryInfo),
    MAP_GUID(IID_IShellDetails3),
    MAP_GUID(IID_IShellView2),
    MAP_GUID(IID_IShellFolder2),
    MAP_GUID(IID_IShellIconOverlay),
    MAP_GUID(IID_IMoniker),
    MAP_GUID(IID_IStream),
    MAP_GUID(IID_ISequentialStream),
    MAP_GUID(IID_IPersistFreeThreadedObject),
};

void DoTraceGUID(LPCTSTR pPrefix, REFGUID rGUID)
{
    TCHAR szGUID[GUIDSTR_MAX];
    TCHAR szBuffer[1024];
    LPCTSTR pName = NULL;
    size_t i;
    UINT_PTR uDepth;
    GETDEPTH(uDepth);
    if (  uDepth < MAX_CALL_DEPTH  )
    {
        for ( i = 0 ; i < ARRAYSIZE(_guid_map); i++ )
        {
            if ( IsEqualGUID(rGUID, *_guid_map[i].m_pGUID) )
            {
                pName = _guid_map[i].m_pName;
                break;
            }
        }

        if ( !pName )
        {
            SHStringFromGUID(rGUID, szGUID, ARRAYSIZE(szGUID));
            pName = szGUID;
        }

        wsprintf(szBuffer, TEXT("%s %s"), pPrefix, pName);
        _indent(uDepth+1, szBuffer);
    }
}

/*-----------------------------------------------------------------------------
/ DoTraceViewMsg
/ --------------
/   Given a view msg (SFVM_ && DVM_), print out the corresponding text...
/
/ In:
/   uMsg -> msg to be streamed
/   wParam -> wParam value for message
/   lParam -> lParam value for message
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
#ifdef UNICODE
#define MAP_MSG(x)     x, TEXT(""L#x)
#else
#define MAP_MSG(x)     x, TEXT(""#x)
#endif

const struct
{
    UINT       m_uMsg;
    LPCTSTR    m_pName;
}
_view_msg_map[] =
{
    MAP_MSG(SFVM_MERGEMENU),
    MAP_MSG(SFVM_INVOKECOMMAND),
    MAP_MSG(SFVM_GETHELPTEXT),
    MAP_MSG(SFVM_GETTOOLTIPTEXT),
    MAP_MSG(SFVM_GETBUTTONINFO),
    MAP_MSG(SFVM_GETBUTTONS),
    MAP_MSG(SFVM_INITMENUPOPUP),
    MAP_MSG(SFVM_SELCHANGE),
    MAP_MSG(SFVM_DRAWITEM),
    MAP_MSG(SFVM_MEASUREITEM),
    MAP_MSG(SFVM_EXITMENULOOP),
    MAP_MSG(SFVM_PRERELEASE),
    MAP_MSG(SFVM_GETCCHMAX),
    MAP_MSG(SFVM_FSNOTIFY),
    MAP_MSG(SFVM_WINDOWCREATED),
    MAP_MSG(SFVM_WINDOWDESTROY),
    MAP_MSG(SFVM_REFRESH),
    MAP_MSG(SFVM_SETFOCUS),
    MAP_MSG(SFVM_QUERYCOPYHOOK),
    MAP_MSG(SFVM_NOTIFYCOPYHOOK),
    MAP_MSG(SFVM_GETDETAILSOF),
    MAP_MSG(SFVM_COLUMNCLICK),
    MAP_MSG(SFVM_QUERYFSNOTIFY),
    MAP_MSG(SFVM_DEFITEMCOUNT),
    MAP_MSG(SFVM_DEFVIEWMODE),
    MAP_MSG(SFVM_UNMERGEMENU),
    MAP_MSG(SFVM_INSERTITEM),
    MAP_MSG(SFVM_DELETEITEM),
    MAP_MSG(SFVM_UPDATESTATUSBAR),
    MAP_MSG(SFVM_BACKGROUNDENUM),
    MAP_MSG(SFVM_GETWORKINGDIR),
    MAP_MSG(SFVM_GETCOLSAVESTREAM),
    MAP_MSG(SFVM_SELECTALL),
    MAP_MSG(SFVM_DIDDRAGDROP),
    MAP_MSG(SFVM_SUPPORTSIDENTITY),
    MAP_MSG(SFVM_FOLDERISPARENT),
    MAP_MSG(SFVM_SETISFV),
    MAP_MSG(SFVM_GETVIEWS),
    MAP_MSG(SFVM_THISIDLIST),
    MAP_MSG(SFVM_GETITEMIDLIST),
    MAP_MSG(SFVM_SETITEMIDLIST),
    MAP_MSG(SFVM_INDEXOFITEMIDLIST),
    MAP_MSG(SFVM_ODFINDITEM),
    MAP_MSG(SFVM_HWNDMAIN),
    MAP_MSG(SFVM_ADDPROPERTYPAGES),
    MAP_MSG(SFVM_BACKGROUNDENUMDONE),
    MAP_MSG(SFVM_GETNOTIFY),
    MAP_MSG(SFVM_ARRANGE),
    MAP_MSG(SFVM_QUERYSTANDARDVIEWS),
    MAP_MSG(SFVM_QUERYREUSEEXTVIEW),
    MAP_MSG(SFVM_GETSORTDEFAULTS),
    MAP_MSG(SFVM_GETEMPTYTEXT),
    MAP_MSG(SFVM_GETITEMICONINDEX),
    MAP_MSG(SFVM_DONTCUSTOMIZE),
    MAP_MSG(SFVM_SIZE),
    MAP_MSG(SFVM_GETZONE),
    MAP_MSG(SFVM_GETPANE),
    MAP_MSG(SFVM_ISOWNERDATA),
    MAP_MSG(SFVM_GETODRANGEOBJECT),
    MAP_MSG(SFVM_ODCACHEHINT),
    MAP_MSG(SFVM_GETHELPTOPIC),
    MAP_MSG(SFVM_OVERRIDEITEMCOUNT),
    MAP_MSG(SFVM_GETHELPTEXTW),
    MAP_MSG(SFVM_GETTOOLTIPTEXTW),
    MAP_MSG(SFVM_GETIPERSISTHISTORY),
    MAP_MSG(SFVM_GETANIMATION),

};


const struct
{
    UINT       m_uMsg;
    LPCTSTR    m_pName;
}
_shcn_msg_map[] =
{
    MAP_MSG(SHCNE_RENAMEITEM),
    MAP_MSG(SHCNE_CREATE),
    MAP_MSG(SHCNE_DELETE),
    MAP_MSG(SHCNE_MKDIR),
    MAP_MSG(SHCNE_RMDIR),
    MAP_MSG(SHCNE_MEDIAINSERTED),
    MAP_MSG(SHCNE_MEDIAREMOVED),
    MAP_MSG(SHCNE_DRIVEREMOVED),
    MAP_MSG(SHCNE_DRIVEADD),
    MAP_MSG(SHCNE_NETSHARE),
    MAP_MSG(SHCNE_NETUNSHARE),
    MAP_MSG(SHCNE_ATTRIBUTES),
    MAP_MSG(SHCNE_UPDATEDIR),
    MAP_MSG(SHCNE_UPDATEITEM),
    MAP_MSG(SHCNE_SERVERDISCONNECT),
    MAP_MSG(SHCNE_UPDATEIMAGE),
    MAP_MSG(SHCNE_DRIVEADDGUI),
    MAP_MSG(SHCNE_RENAMEFOLDER),
    MAP_MSG(SHCNE_FREESPACE),
};




void DoTraceViewMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPCTSTR pName = NULL;
    TCHAR szBuffer[1024];
    TCHAR szTmp[25];
    TCHAR szTmp2[25];
    size_t i;
    UINT_PTR uDepth;
    GETDEPTH(uDepth);
    if (   uDepth < MAX_CALL_DEPTH )
    {
            for ( i = 0 ; i < ARRAYSIZE(_view_msg_map); i++ )
            {
                if ( _view_msg_map[i].m_uMsg == uMsg )
                {
                    pName = _view_msg_map[i].m_pName;
                    break;
                }
            }

            if (!pName)
            {
                wsprintf( szTmp, TEXT("SFVM_(%d)"), uMsg );
                pName = szTmp;
            }

            if (uMsg == SFVM_FSNOTIFY)
            {
                LPCTSTR pEvent = NULL;

                for (i= 0; i < ARRAYSIZE(_shcn_msg_map); i++)
                {
                    if ( _shcn_msg_map[i].m_uMsg == uMsg )
                    {
                        pEvent = _shcn_msg_map[i].m_pName;
                        break;
                    }
                }

                if (!pEvent)
                {
                    lstrcpy(szTmp2,TEXT("Unknown"));
                    pEvent = szTmp2;
                }

                wsprintf(szBuffer, TEXT("%s w(%08X) l(%08X == %s)"), pName, wParam, lParam, pEvent);
                _indent(uDepth+1, szBuffer);

            }
            else
            {
                wsprintf(szBuffer, TEXT("%s w(%08X) l(%08X)"), pName, wParam, lParam);
                _indent(uDepth+1, szBuffer);
            }
    }
}

const struct
{
    UINT       m_uMsg;
    LPCTSTR    m_pName;
}
_menu_msg_map[] =
{
    MAP_MSG(DFM_MERGECONTEXTMENU),
    MAP_MSG(DFM_INVOKECOMMAND),
    MAP_MSG(DFM_ADDREF),
    MAP_MSG(DFM_RELEASE),
    MAP_MSG(DFM_GETHELPTEXT),
    MAP_MSG(DFM_WM_MEASUREITEM),
    MAP_MSG(DFM_WM_DRAWITEM),
    MAP_MSG(DFM_WM_INITMENUPOPUP),
    MAP_MSG(DFM_VALIDATECMD),
    MAP_MSG(DFM_MERGECONTEXTMENU_TOP),
    MAP_MSG(DFM_GETHELPTEXTW),
    MAP_MSG(DFM_INVOKECOMMANDEX),
    MAP_MSG(DFM_MAPCOMMANDNAME),
    MAP_MSG(DFM_GETDEFSTATICID),
    MAP_MSG(DFM_GETVERBW),

};

const struct
{
    WPARAM     m_uMsg;
    LPCTSTR    m_pName;
}
_menu_invk_cmd_msg_map[] =
{
    MAP_MSG(DFM_CMD_RENAME),
    MAP_MSG(DFM_CMD_MODALPROP),
    MAP_MSG(DFM_CMD_PASTESPECIAL),
    MAP_MSG(DFM_CMD_PASTELINK),
    MAP_MSG(DFM_CMD_VIEWDETAILS),
    MAP_MSG(DFM_CMD_VIEWLIST),
    MAP_MSG(DFM_CMD_PASTE),
    MAP_MSG(DFM_CMD_NEWFOLDER),
    MAP_MSG(DFM_CMD_PROPERTIES),
    MAP_MSG(DFM_CMD_LINK),
    MAP_MSG(DFM_CMD_COPY),
    MAP_MSG(DFM_CMD_MOVE),
    MAP_MSG(DFM_CMD_DELETE),

};



void DoTraceMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LPCTSTR pName = NULL;
    TCHAR szBuffer[1024];
    TCHAR szTmp[25];
    size_t i;
    UINT_PTR uDepth;
    GETDEPTH (uDepth);
    if (  uDepth < MAX_CALL_DEPTH  )
    {

            for ( i = 0 ; i < ARRAYSIZE(_menu_msg_map); i++ )
            {
                if ( _menu_msg_map[i].m_uMsg == uMsg )
                {
                    pName = _menu_msg_map[i].m_pName;
                    break;
                }
            }

            if (!pName)
            {
                wsprintf( szTmp, TEXT("DFM_(%d)"), uMsg );
                pName = szTmp;
            }

            if ((uMsg == DFM_INVOKECOMMAND) && (wParam >= DFM_CMD_RENAME))
            {
                wsprintf(szBuffer, TEXT("%s w(%s) l(%08X)"), pName, _menu_invk_cmd_msg_map[wParam-DFM_CMD_RENAME].m_pName, lParam);
            }
            else
            {
                wsprintf(szBuffer, TEXT("%s w(%08X) l(%08X)"), pName, wParam, lParam);
            }
            _indent(uDepth+1, szBuffer);

    }
}


/*-----------------------------------------------------------------------------
/ DoTraceAssert
/ -------------
/   Our assert handler, out faults it the trace mask as enabled assert
/   faulting.
/
/ In:
/   iLine = line
/   pFilename -> filename of the file we asserted in
/
/ Out:
/   -
/----------------------------------------------------------------------------*/
void DoTraceAssert(int iLine, LPTSTR pFilename)
{
    TCHAR szBuffer[1024];
    UINT_PTR uDepth;
    GETDEPTH(uDepth);

    wsprintf(szBuffer, TEXT("Assert failed in %s, line %d"), pFilename, iLine);

    _indent(uDepth+1, szBuffer);

    if ( g_dwTraceMask & TRACE_COMMON_ASSERT )
        DebugBreak();
}


#endif