/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corp., 1991                **/
/**********************************************************************/

/*
    dbgstr.hxx
    A "stream" protocol for debugging output

    This lets me use a "stream" style of emitting debugging information,
    e.g.

        cdebug << "Couldn't allocate " << _cbFoo << " bytes." << dbgEOL;
    or
        cdebug << "Window moved to " << xyFinalRestingPlace << dbgEOL;

    All client applications have a "cdebug" stream already defined and
    constructed.  Actually, only BLT apps have it already established;
    console apps must construct an output sink, and init the package
    like so:

        main()
        {
            OUTPUT_TO_STDERR out;
            DBGSTREAM::SetCurrent(&out);
            ... your code here ...
            DBGSTREAM::SetCurrent(NULL);
        }

    The macros DBGOUT and DBGEOL work with this global stream, ensuring
    that its data disappears from the object image in retail builds.
    DBGOUT automatically names cdebug as its destination stream; DBGEOL
    works likewise, but appends an end-of-line to its output.  Both
    take a stream-output-expression, like so:

        DBGOUT("Number of pels: " << cPels);
        DBGEOL("; number of lims: " << cLims);

    or just

        DBGEOL("Number of pels: " << cPels << "; number of lims: " << cLims);

    If a client wants its own separate stream, it should first construct
    an output sink (OUTPUTSINK or derived class) for the stream, then
    construct the stream, passing the sink as a ctor param.  E.g.

        OUTPUT_TO_WHEREVER out;
        DBGSTREAM dbgWherever(&out);

        dbgWherever << mumble;


    FILE HISTORY:
        beng        21-May-1991 Created
        beng        22-May-1991 Added IFTRACE and non-Windows sinks
        beng        27-Jun-1991 Removed INCL_WINDOWS.  How else to
                                use non-Win sinks?  Bozo.
        beng        06-Jul-1991 Added OUTPUT_TO_STDOUT
        Johnl       07-Aug-1991 Added some explanatory text
        beng        25-Oct-1991 Add DBG macros
        beng        12-Feb-1992 Rename DBG to DBGOUT (conflict w/ NT)
        beng        28-Feb-1992 New USE_CONSOLE def'n
        beng        16-Mar-1992 Simplify cdebug setups
        jonn        08-May-1992 Added TRACEOUT and TRACEEOL
        beng        10-May-1992 Removed obsolete IFTRACE
*/

#ifndef _DBGSTR_HXX_
#define _DBGSTR_HXX_

// All the non-Windows versions communicate through stdio.

#if !defined(INCL_WINDOWS) || defined(USE_CONSOLE)
#include <stdio.h>
#endif


/*************************************************************************

    NAME:       OUTPUTSINK

    SYNOPSIS:   Abstract class describing any destination
                of debugging output.

    INTERFACE:  Render()    - Takes a character string and renders
                              it on the destination.  Supplies an
                              optional second argument with the number
                              of characters in the string as a hint
                              for some 'sink implementations.

                EndOfLine() - Generate a linebreak on the destination.

    USES:

    CAVEATS:
        Client must supply some implementation

    NOTES:
        The OUTPUTSINK abstract class will let me extend this package
        to render output into a file, into a secondary window, etc.

    HISTORY:
        beng        21-May-1991     Created

**************************************************************************/

DLL_CLASS OUTPUTSINK
{
public:
    virtual VOID Render(const TCHAR *psz) = 0;
    virtual VOID Render(const TCHAR *psz, UINT cch) = 0;
    virtual VOID EndOfLine() = 0;
};


#if defined(INCL_WINDOWS)
/*************************************************************************

    NAME:       OUTPUT_TO_AUX

    SYNOPSIS:   Delivers the debugging port for trace messages

    INTERFACE:  Render()    - Takes a character string and renders
                              it on the debugging port.
                EndOfLine() - Generate a linebreak on the destination.

    PARENT:     OUTPUTSINK

    HISTORY:
        beng        21-May-1991 Created
        beng        25-Oct-1991 Implementation outlined

**************************************************************************/

DLL_CLASS OUTPUT_TO_AUX: public OUTPUTSINK
{
public:
    virtual VOID Render(const TCHAR *psz);
    virtual VOID Render(const TCHAR *psz, UINT cch);
    virtual VOID EndOfLine();
};
#endif // WINDOWS


#if defined(stderr)
/*************************************************************************

    NAME:       OUTPUT_TO_STDERR

    SYNOPSIS:   Delivers the stderr stdio stream for trace messages

    INTERFACE:  Render()    - Takes a character string and renders
                              it on handle 2.
                EndOfLine() - Generate a linebreak on the destination.

    PARENT:     OUTPUTSINK

    HISTORY:
        beng        22-May-1991 Created
        beng        06-Jul-1991 Always assume output is cooked
        beng        25-Oct-1991 Implementation outlined

**************************************************************************/

DLL_CLASS OUTPUT_TO_STDERR: public OUTPUTSINK
{
public:
    virtual VOID Render(const TCHAR *psz);
    virtual VOID Render(const TCHAR *psz, UINT cch);
    virtual VOID EndOfLine();
};
#endif // STDIO


#if defined(stdout)
/*************************************************************************

    NAME:       OUTPUT_TO_STDOUT

    SYNOPSIS:   Delivers the stdout stdio stream for trace messages

    INTERFACE:  Render()    - Takes a character string and renders
                              it on handle 1.
                EndOfLine() - Generate a linebreak on the destination.

    PARENT:     OUTPUTSINK

    HISTORY:
        beng        06-Jul-1991 Created from OUTPUT_TO_STDERR
        beng        25-Oct-1991 Implementation outlined

**************************************************************************/

DLL_CLASS OUTPUT_TO_STDOUT: public OUTPUTSINK
{
public:
    virtual VOID Render(const TCHAR *psz);
    virtual VOID Render(const TCHAR *psz, UINT cch);
    virtual VOID EndOfLine();
};
#endif // STDIO


/*************************************************************************

    NAME:       OUTPUT_TO_NUL

    SYNOPSIS:   Disables all trace messages.

                Messages to a DBGSTREAM using this sink will fall
                into the bitbucket w/o ado.

    PARENT:     OUTPUTSINK

    NOTES:
        This class exists so that a stream can shut off messages
        on itself via SetSink.

    HISTORY:
        beng        22-May-1991 Created
        beng        25-Oct-1991 Implementation outlined

**************************************************************************/

DLL_CLASS OUTPUT_TO_NUL: public OUTPUTSINK
{
    virtual VOID Render(const TCHAR *psz);
    virtual VOID Render(const TCHAR *psz, UINT cch);
    virtual VOID EndOfLine();
};


#if 0
/*************************************************************************

    NAME:       OUTPUT_TO_WIN

    SYNOPSIS:   Pops up a scrollable window for trace messages.

    PARENT:     OUTPUTSINK

    CAVEATS:
        Not yet implemented.

    NOTES:

    HISTORY:
        beng        21-May-1991     Daydreamed.

**************************************************************************/

DLL_CLASS OUTPUT_TO_WIN: public OUTPUTSINK
{
    // ...
};
#endif


// Dropping one of these on a stream results in a special action.
//
enum DBGSTR_SPECIAL
{
    dbgEOL  // End-of-line
};


/*************************************************************************

    NAME:       DBGSTREAM

    SYNOPSIS:   Stream-style object for debugging output

    INTERFACE:  DBGSTREAM()  - constructor, associating a
                               sink with the stream

                SetSink()    - change the sink on a stream

                operator<<() - render onto the stream.  The class
                               supports most primitive objects;
                               in addition, "dbgEOL" will generate
                               a linebreak.

                QueryCurrent() - returns a reference to the default
                               global debugging stream, as aliased to
                               "cdebug" in the source and used by
                               the DBGEOL etc. macros.

    USES:       OUTPUTSINK

    NOTES:
        Clients may supply their own operators for rendering
        themselves onto a DBGSTREAM.

        BUGBUG - the operators should use the "cch" form of Render.

    HISTORY:
        beng        21-May-1991 Created
        beng        25-Oct-1991 Use CCH_x manifests; implementation outlined
        beng        16-Mar-1992 Added QueryCurrent static etc.
        beng        29-Jun-1992 Outlined Set/QueryCurrent for dllization

**************************************************************************/

DLL_CLASS DBGSTREAM
{
private:
    OUTPUTSINK * _pout;

    // The home of cdebug
    //
    static DBGSTREAM * _pdbgCurrentGlobal;

public:
    DBGSTREAM(OUTPUTSINK * pout);
    ~DBGSTREAM();

    static DBGSTREAM& QueryCurrent();
    static VOID SetCurrent(DBGSTREAM* pdbg);

    VOID SetSink(OUTPUTSINK * pout);

    DBGSTREAM& operator<<(TCHAR c);
    DBGSTREAM& operator<<(const TCHAR* psz);
#if defined(UNICODE)
    // need these for simple output
    DBGSTREAM& operator<<(CHAR c);
    DBGSTREAM& operator<<(const CHAR* psz);
#endif
    DBGSTREAM& operator<<(INT n);
    DBGSTREAM& operator<<(UINT n);
    DBGSTREAM& operator<<(SHORT n);
#if !defined(UNICODE)
    // if defined, conflicts with TCHAR def'n
    DBGSTREAM& operator<<(USHORT n);
#endif
    DBGSTREAM& operator<<(LONG n);
    DBGSTREAM& operator<<(ULONG n);
    DBGSTREAM& operator<<(DBGSTR_SPECIAL dbgSpecial);
};


// For traditional stream-style output, declare an object named
// "cdebug" (as in cin, cout, cerr).
//
// The BLT startup code points this at the AUX debugging console.
// Command-line unit tests should hook it to stderr.
//

#define cdebug (DBGSTREAM::QueryCurrent())


// We have to use the preprocessor to remove completely all trace
// messages from the release product, since the compiler will not
// optimize away unreferenced message strings.

#if defined(DEBUG)
#define DBGOUT(x)   { cdebug << x ; }
#define DBGEOL(x)   { cdebug << x << dbgEOL; }
#else
#define DBGOUT(x)   { ; }
#define DBGEOL(x)   { ; }
#endif

#if defined(DEBUG) && defined(TRACE)
#define TRACEOUT(x)   { cdebug << x ; }
#define TRACEEOL(x)   { cdebug << x << dbgEOL; }
// must use semicolons after using these macros
#define TRACETIMESTART DWORD dwTraceTime = ::GetTickCount()
#define TRACETIMEEND(printthis)  TRACEEOL( printthis << ::GetTickCount() - (dwTraceTime) << " msec" )
#define TRACETIMESTART2(name)  DWORD dwTraceTime##name = ::GetTickCount()
#define TRACETIMEEND2(name,printthis)  TRACEEOL( printthis << ::GetTickCount() - (dwTraceTime##name) << " msec" )
#else
#define TRACEOUT(x)   { ; }
#define TRACEEOL(x)   { ; }
#define TRACETIMESTART  { ; }
#define TRACETIMEEND(printthis) { ; }
#define TRACETIMESTART2(name)  { ; }
#define TRACETIMEEND2(name,printthis) { ; }
#endif


#endif // _DBGSTR_HXX_