#ifndef RITTER_INCLUDE_TOOLS_TRACE_H
#define RITTER_INCLUDE_TOOLS_TRACE_H
//
// Copyright (c) 2001,2003
// Torsten Robitzki 
//
// This material is provided "as is", with absolutely no warranty expressed
// or implied. Any use is at your own risk.
//
// Permission to use or copy this software for any purpose is hereby granted 
// without fee, provided the above notices are retained on all copies.
// Permission to modify the code and to distribute modified code is granted,
// provided the above notices are retained, and a notice that the code was
// modified is included with the above copyright notice.
//
 
#include <iostream>
#include <streambuf>
#include <string>

namespace ritter {
namespace tools {

struct basic_tracestreambufbase
{
  // level of information
  enum trace_level {
    trace_urgent,      // very urgent, like failed asserts        
    trace_error,       // an error
    trace_warning,     // thinks that might cause an error
    trace_information, // interresting things
    trace_main,        // main program flow, inclusive parameters and return values
    trace_detail,      // adding informations about flow within function
    trace_all };       // all available information (use with care)

  // options for a trailing line header
  typedef enum {
    add_date     = 0x01, // output Date 
    add_time     = 0x02, // output timestamp
    add_threadid = 0x04, // output a thread id
    add_level    = 0x08  // Add a prefix for the level like -%u, -%e
  } option;

  // the class has two buffers
  enum { m_BufferSize = 512 };

  basic_tracestreambufbase()
    : m_Option     (add_time)
    , m_OutputLevel(trace_urgent)
  {}
protected:
  unsigned                   m_Option;
  unsigned                   m_OutputLevel;
};

typedef basic_tracestreambufbase::trace_level trace_level;

// Kleiner Trick, da gcc 2.95.2 nicht mit Methoden-
// aufrufen zur initialiserung von defaultparametern
// umgehen kann.
inline std::streambuf* 
 getCerrStreamBuf__() {return std::cerr.rdbuf();}

template <class C, class T = std::char_traits<C> >
class basic_tracestreambuf : public std::basic_streambuf<C,T>,
                             public basic_tracestreambufbase
{
public:
  basic_tracestreambuf(std::basic_streambuf<C,T>* iobuffer = getCerrStreamBuf__());
  basic_tracestreambuf& setPrefix(const std::basic_string<C,T>&);
  basic_tracestreambuf& setOptions(option);
  basic_tracestreambuf& setTraceLevel(trace_level);
  basic_tracestreambuf& setOutputLevel(trace_level);
  void                  endtrace();
  bool                  wouldTrace(trace_level) const;
protected:
  virtual int               sync();    
  virtual typename T::int_type overflow(typename T::int_type c);
          int               writePrefix();
private:
  // not implented
  basic_tracestreambuf& operator=(const basic_tracestreambuf&);
  basic_tracestreambuf(const basic_tracestreambuf&);

  void    swapBuffer(unsigned tracelevel, unsigned outputlevel);

  C                          m_Buffer[m_BufferSize];
  C                          m_Skip[m_BufferSize];
  C*                         m_SavedPos;
  std::basic_streambuf<C,T>* m_Output;
  std::basic_string<C,T>     m_Prefix;
};


// manipulators
template <class E, class T>
std::basic_ostream<E, T>& trace_urgent(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_error(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_warning(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_information(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_main(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_detail(std::basic_ostream<E, T>&);
template <class E, class T>
std::basic_ostream<E, T>& trace_all(std::basic_ostream<E, T>&);

// vom Tracelevel unabh„ngiges vom "endl"
template <class E, class T>
std::basic_ostream<E, T>& endtrace(std::basic_ostream<E, T>&);

// setzen des Outputlevels
template <class E, class T>
std::basic_ostream<E, T>& setOutputLevel(std::basic_ostream<E, T>&,
                                         basic_tracestreambufbase::trace_level);

// setzen des Tracelevels
template <class E, class T>
std::basic_ostream<E, T>& setTraceLevel(std::basic_ostream<E, T>&,
                                        basic_tracestreambufbase::trace_level);

// Abfrage, ob mit vorgegebenem TraceLevel ein Ausgabe in den
// Streambuffer gelangen wrde
template <class E, class T>
inline bool would_trace(const std::basic_ostream<E, T>& o,
                       basic_tracestreambufbase::trace_level level)
{
  basic_tracestreambuf<E,T> const * trace = 
    dynamic_cast<basic_tracestreambuf<E,T> const *>( o.rdbuf() );

  return !trace || trace->wouldTrace(level);
}

typedef basic_tracestreambuf<char>     tracestreambuf;
typedef basic_tracestreambuf<wchar_t> wtracestreambuf;

} // namespace tools
} // namespace ritter

#if 1
#  include "tools/trace.cc"
#endif


#endif // includeguard
