/*
    Copyright 2005-2014 Intel Corporation.  All Rights Reserved.

    This file is part of Threading Building Blocks. Threading Building Blocks is free software;
    you can redistribute it and/or modify it under the terms of the GNU General Public License
    version 2  as  published  by  the  Free Software Foundation.  Threading Building Blocks is
    distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
    implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    See  the GNU General Public License for more details.   You should have received a copy of
    the  GNU General Public License along with Threading Building Blocks; if not, write to the
    Free Software Foundation, Inc.,  51 Franklin St,  Fifth Floor,  Boston,  MA 02110-1301 USA

    As a special exception,  you may use this file  as part of a free software library without
    restriction.  Specifically,  if other files instantiate templates  or use macros or inline
    functions from this file, or you compile this file and link it with other files to produce
    an executable,  this file does not by itself cause the resulting executable to be covered
    by the GNU General Public License. This exception does not however invalidate any other
    reasons why the executable file might be covered by the GNU General Public License.
*/

// Just the tracing portion of the harness.
//
// This header defines TRACE and TRACENL macros, which use REPORT like syntax and 
// are useful for duplicating trace output to the standard debug output on Windows.
// It is possible to add the ability of automatic extending messages with additional
// info (file, line, function, time, thread ID, ...).
//
// Macros output nothing when test app runs in non-verbose mode (default).
//

#ifndef tbb_tests_harness_report_H
#define tbb_tests_harness_report_H

#if defined(MAX_TRACE_SIZE) && MAX_TRACE_SIZE < 1024
    #undef MAX_TRACE_SIZE
#endif
#ifndef MAX_TRACE_SIZE
    #define MAX_TRACE_SIZE  1024
#endif

#if __SUNPRO_CC
#include <stdio.h>
#else
#include <cstdio>
#endif

#include <cstdarg>

#include "tbb/tbb_config.h"

#if __TBB_DEFINE_MIC
#include "harness_mic.h"
#endif

#ifdef HARNESS_INCOMPLETE_SOURCES
#error Source files are not complete. Check the build environment
#endif

#if _MSC_VER
    #define snprintf _snprintf
#if _MSC_VER<=1400
    #define vsnprintf _vsnprintf
#endif
#endif

namespace Harness {
    namespace internal {

#ifndef TbbHarnessReporter
    struct TbbHarnessReporter {
        void Report ( const char* msg ) {
            printf( "%s", msg );
            fflush(stdout);
#ifdef _WINDOWS_
            OutputDebugStringA(msg);
#endif
        }
    }; // struct TbbHarnessReporter
#endif /* !TbbHarnessReporter */

    class Tracer {
        int         m_flags;
        const char  *m_file;
        const char  *m_func;
        size_t      m_line;

        TbbHarnessReporter m_reporter;

    public:
        enum  { 
            prefix = 1,
            need_lf = 2
        };

        Tracer*  set_trace_info ( int flags, const char *file, size_t line, const char *func ) {
            m_flags = flags;
            m_line = line;
            m_file = file;
            m_func = func;
            return  this;
        }

        void  trace ( const char* fmt, ... ) {
            char    msg[MAX_TRACE_SIZE];
            char    msg_fmt_buf[MAX_TRACE_SIZE];
            const char  *msg_fmt = fmt;
            if ( m_flags & prefix ) {
                snprintf (msg_fmt_buf, MAX_TRACE_SIZE, "[%s] %s", m_func, fmt);
                msg_fmt = msg_fmt_buf;
            }
            std::va_list argptr;
            va_start (argptr, fmt);
            int len = vsnprintf (msg, MAX_TRACE_SIZE, msg_fmt, argptr);
            va_end (argptr);
            if ( m_flags & need_lf &&  
                 len < MAX_TRACE_SIZE - 1  &&  msg_fmt[len-1] != '\n' )
            {
                msg[len] = '\n';
                msg[len + 1] = 0;
            }
            m_reporter.Report(msg);
        }
    }; // class Tracer

    static Tracer tracer;

    template<int>
    bool not_the_first_call () {
        static bool first_call = false;
        bool res = first_call;
        first_call = true;
        return res;
    }

    } // namespace internal
} // namespace Harness

#if defined(_MSC_VER)  &&  _MSC_VER >= 1300  ||  defined(__GNUC__)  ||  defined(__GNUG__)
    #define HARNESS_TRACE_ORIG_INFO __FILE__, __LINE__, __FUNCTION__
#else
    #define HARNESS_TRACE_ORIG_INFO __FILE__, __LINE__, ""
    #define __FUNCTION__ ""
#endif


//! printf style tracing macro
/** This variant of TRACE adds trailing line-feed (new line) character, if it is absent. **/
#define TRACE Harness::internal::tracer.set_trace_info(Harness::internal::Tracer::need_lf, HARNESS_TRACE_ORIG_INFO)->trace

//! printf style tracing macro without automatic new line character adding
#define TRACENL Harness::internal::tracer.set_trace_info(0, HARNESS_TRACE_ORIG_INFO)->trace

//! printf style tracing macro with additional information prefix (e.g. current function name)
#define TRACEP Harness::internal::tracer.set_trace_info(Harness::internal::Tracer::prefix | \
                                    Harness::internal::Tracer::need_lf, HARNESS_TRACE_ORIG_INFO)->trace

//! printf style remark macro
/** Produces output only when the test is run with the -v (verbose) option. **/
#define REMARK  !Verbose ? (void)0 : TRACENL

//! printf style remark macro
/** Produces output only when invoked first time. 
    Only one instance of this macro is allowed per source code line. **/
#define REMARK_ONCE (!Verbose || Harness::internal::not_the_first_call<__LINE__>()) ? (void)0 : TRACE

//! printf style reporting macro
/** On heterogeneous platforms redirects its output to the host side. **/
#define REPORT TRACENL

//! printf style reporting macro
/** Produces output only when invoked first time.
    Only one instance of this macro is allowed per source code line. **/
#define REPORT_ONCE (Harness::internal::not_the_first_call<__LINE__>()) ? (void)0 : TRACENL

#endif /* tbb_tests_harness_report_H */
