/*
 * freescope - Free source browser
 * Copyright (C) 2001  Olivier Deme
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * FILE:        Trace.h
 *
 * DESCRIPTION: This file defines the class Trace and its relatives used
 *              for tracing and logging purpose.
 *              For more information, see readme.txt
 */

#ifndef _TRACE_H_
#define _TRACE_H_

/************/
/* INCLUDES */
/************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */

#include <assert.h>

#ifndef NOT_ANSI

#include <string>
#include <fstream>
#include <new>
using namespace std;

#else // NOT_ANSI

#include <string.h>
#include <fstream.h>
#include <new.h>

#endif // NOT_ANSI


/********************/
/* GLOBAL VARIABLES */
/********************/

#ifdef USE_TRACE_OFF
static int g_trace_off;
#endif // USE_TRACE_OFF


/**********/
/* MACROS */
/**********/

#define TRACE_CFG               (Trace::instance())

/*
 * WARNING: Do not use LOG and TRACE the following way:
 * 
 * if (bla)
 *      TRACE(module, level) << "blabla" << endl;
 * else
 *      do_something();
 *
 *
 * This won't compile, instead use curly braces:
 *
 * if (bla)
 * {
 *      TRACE(module, level) << "blabla" << endl;
 * }
 * else
 * {
 *      do_something();
 * }
 */

#define LOG(module, level)      if (Trace::m_flags & Trace::FLAGS_CALLER) \
                                {                                         \
                                    Trace::m_locLine = __LINE__;          \
                                    Trace::m_locFile = __FILE__;          \
                                }                                         \
                                Trace::m_module = module;                 \
                                Trace::m_level  = level;                  \
                                (Trace::instance())


// Define USE_TRACE if you want to use trace
#ifndef NO_TRACE

#define TRACE(module, level)    if (Trace::m_flags & Trace::FLAGS_CALLER) \
                                {                                         \
                                    Trace::m_locLine = __LINE__;          \
                                    Trace::m_locFile = __FILE__;          \
                                }                                         \
                                Trace::m_module = module;                 \
                                Trace::m_level  = level;                  \
                                (Trace::instance())

#else  // NO_TRACE

#   ifndef USE_TRACE_OFF

/*
 * NOTE:        If possible, do not define USE_TRACE_OFF. The reason is that
 *              the following code (the one using if(0)) should be removed
 *              from the executable when compiled with optimization flag.
 *              This avoids the overhead of an extra check with 0. when 
 *              NO_TRACE is defined.
 *              Define USE_TRACE_OFF when the compiler generates a warning
 *              (code unreachable).
 */

#define TRACE(module, level)    if (0)                  \
                                    (Trace::instance())
#   else // USE_TRACE_OFF

#define TRACE(module, level)    if (g_trace_off)        \
                                    (Trace::instance())
#   endif // USE_TRACE_OFF
#endif // NO_TRACE


/********************/
/* PRE-DECLARATIONS */
/********************/

class Hex;
class Width;


/*********************/
/* CLASS DEFINITIONS */
/*********************/

class Trace
{
    // TYPE DEFINITIONS
public:
    typedef enum
    {
        FLAGS_CALLER             = 0x1 << 0,
        FLAGS_ALIGNMENT          = 0x1 << 1,
        FLAGS_LEVEL_DESCRIPTION  = 0x1 << 2,
        FLAGS_MODULE_DESCRIPTION = 0x1 << 3,
        FLAGS_PROGRAM_NAME       = 0x1 << 4,
        FLAGS_TIMESTAMP          = 0x1 << 5,
        FLAGS_TIME_LOCAL         = 0x1 << 6,
        FLAGS_TIME_GMT           = 0x1 << 7
    } Flag;

    typedef enum
    {
        OSTREAM_STDOUT = 0x1 << 0,
        OSTREAM_STDERR = 0x1 << 1,
        OSTREAM_FILE   = 0x1 << 2,
        OSTREAM_OTHER  = 0x1 << 3
    } Ostream;

    typedef int Level;
    typedef int Module;
    typedef int Stream;
    typedef int Flags;

protected:
    typedef struct
    {
        Level   levels;
        Level   levDescrBits;
        string  levDescr [sizeof(Level)*8];
        string  description;
        bool    descriptionSet;
        
    } Module_t;

    typedef Trace& (Trace_manip) (Trace&);

    // CONSTANTS
public:
    static const  Module GLOBAL;  // Application-level module
           const  char   HEADER_DELIMITER;
           const  Level  LEVEL_ERROR;


    // FRIENDS
    friend Trace& flush (Trace&);
    friend Trace& endl  (Trace&);
    friend Trace& operator<< (Trace&, const Hex&);
    friend Trace& operator<< (Trace&, const Width&);


    // CONSTRUCTORS
private:
             Trace () throw();

protected:
    virtual ~Trace () {};


    // METHODS
public:
            void   set_program_name       (const string& name)        throw();
            Level  add_to_mask            (const Module module,
                                           const Level levels)        throw();
            Level  remove_from_mask       (const Module module,
                                           const Level levels)        throw();
            Level  set_mask               (const Module module, 
                                           const Level levels)        throw();
            void   set_level_description  (const Module module,
                                           const Level levels,
                                           const string& description) throw();
            void   set_module_description (const Module modules,
                                           const string& description) throw();
    virtual Flags  enable                 (const Flags flags)         throw();
    virtual Flags  disable                (const Flags flags)         throw();
    virtual Flags  set_flags              (const Flags flags)         throw();
//    virtual void   setbuf                 (const unsigned int)        throw();
    virtual Stream close                  (const Stream streams)      throw();
    virtual Stream open                   (const Stream streams)      throw();
    virtual Stream open                   (Stream streams,
                                           const string& file);
    static  bool   create                 ()       throw(bad_alloc);

    static  inline Trace& instance        ()       throw();

protected:
    virtual int   time_stamp             (char* buffer) throw();
    virtual void   do_header             ();

private:
    void set_level_max_length  () throw();
    void set_module_max_length () throw();

#ifdef USE_TRACE_OFF
    void dummy                 () throw() {g_trace_off = 0;}; // Remove warning
#endif // USE_TRACE_OFF


    // OPERATORS
public:
    virtual Trace& operator<< (char character);
    virtual Trace& operator<< (unsigned char character);
    virtual Trace& operator<< (char* string);
    virtual Trace& operator<< (unsigned char* string);
    virtual Trace& operator<< (const string& text);
    virtual Trace& operator<< (int nbr);
    virtual Trace& operator<< (unsigned int nbr);
    virtual Trace& operator<< (long nbr);
    virtual Trace& operator<< (unsigned long nbr);
    virtual Trace& operator<< (short nbr);
    virtual Trace& operator<< (unsigned short nbr);
    virtual Trace& operator<< (float nbr);
    virtual Trace& operator<< (double nbr);
    virtual Trace& operator<< (Trace_manip);
    
    // ATTRIBUTES
   /*
    * The following attributes are static and public.
    * Don't scream! This is for efficiency reason, cf. macro.
    */
public: 
    static Flags        m_flags;
    static unsigned int m_locLine;
    static char*        m_locFile;
    static Module       m_module;
    static Level        m_level;

protected:
           bool     m_start;
           Module_t m_modules[sizeof(Module)*8];    // The module structures
           Stream   m_streams;                      // Opened streams
           ofstream m_file;                         // File streaming
           string   m_filename;                     // File name
           string   m_programName;                  // Program name
           int      m_lookupLevel[sizeof(Level)*8]; // Powers of two
    static Trace*   m_instance;                     // The singleton

private:
            unsigned int m_moduleMaxLength;
            unsigned int m_levelMaxLength;
};

class Hex
{
    // CONSTRUCTORS
public:
    Hex (int nbr)          { m_nbr = (unsigned int)nbr; };
    Hex (unsigned int nbr) { m_nbr = nbr; };

    // FRIEND OPERATORS
    friend Trace& operator<< (Trace& trace, const Hex& h);

    // ATTRIBUTES
    unsigned int m_nbr;
};

class Width
{
    // FRIENDS
    friend Trace& operator<< (Trace& trace, const Width& w);

    // TYPE DEFINITIONS
public:
    typedef enum
    {
        LEFT,
        RIGHT
    } Format;

    // CONSTRUCTORS
public:
    Width (Format justify, unsigned int width, char fill = ' ');

    // ATTRIBUTES
    Format       m_fmt;
    unsigned int m_width;
    char         m_fill;

};


/****************/
/* MANIPULATORS */
/****************/

extern Trace& endl  (Trace& trace);
extern Trace& flush (Trace& trace);


/********************/
/* INLINE FUNCTIONS */
/********************/

inline Trace& Trace::instance()
        throw ()
{
    assert(m_instance != 0);   // Instance must be created
    return *m_instance;
}

#endif // _TRACE_H_


syntax highlighted by Code2HTML, v. 0.9.1