//
// Copyright (c) 2001
// 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 "tools/trace.h"
#include <cassert>
#include <ctime>

namespace ritter {
namespace tools {

template <class C, class T>
basic_tracestreambuf<C,T>::basic_tracestreambuf(std::basic_streambuf<C,T>* iobuffer)
 : tools::basic_tracestreambufbase()
 , m_SavedPos(m_Skip+0)
 , m_Output(iobuffer)
 , m_Prefix()
{
  setg(0,0,0);
  setp(m_Buffer+0, m_Buffer+m_BufferSize);
}

template <class C, class T>
basic_tracestreambuf<C,T>& 
basic_tracestreambuf<C,T>::setPrefix(const std::basic_string<C,T>& p)
{
  m_Prefix = p;
  return *this;
}

template <class C, class T>
basic_tracestreambuf<C,T>& 
basic_tracestreambuf<C,T>::setOptions(basic_tracestreambuf<C,T>::option o)
{
  m_Options = o;
  return *this;
}

template <class C, class T>
basic_tracestreambuf<C,T>& 
basic_tracestreambuf<C,T>::setTraceLevel(basic_tracestreambuf<C,T>::trace_level l)
{
  swapBuffer(l, m_OutputLevel);
  return *this;
}

template <class C, class T>
basic_tracestreambuf<C,T>& 
basic_tracestreambuf<C,T>::setOutputLevel(basic_tracestreambuf<C,T>::trace_level l)
{
  swapBuffer(trace_urgent, l);
  m_OutputLevel = l;
  return *this;
}

template <class C, class T>
void basic_tracestreambuf<C,T>::swapBuffer(unsigned tracelevel , 
                                           unsigned outputlevel)
{
  assert( epptr() == m_Skip + m_BufferSize || 
          epptr() == m_Buffer + m_BufferSize );

  if (tracelevel <= outputlevel) 
  { // Trace einschalten, wenn nicht eingeschaltet
    if ( epptr() != m_Buffer + m_BufferSize )
    {
      setp(m_SavedPos, m_Buffer + m_BufferSize);
    }
  }
  else
  {
    // Trace ausschalten, wenn nicht ausgeschaltet
    if ( epptr() != m_Skip + m_BufferSize )
    {
      m_SavedPos = pptr();
      setp(m_Skip+0, m_Skip + m_BufferSize);
    }
  }  
}

template <class C, class T>
int basic_tracestreambuf<C,T>::writePrefix()
{
  int result = 0; // success

  if (m_Option & (add_date | add_time))
  {
    using namespace std;

    time_t now = time(0);
    tm*    timeStruct = localtime(&now);

    std::locale loc("");

    if (m_Option & add_date)
    {

    }

    if (m_Option & add_time)
    {
    }
  }

  if (m_Option & add_threadid)
  {
  }

  return result;
}

template <class C, class T>
int basic_tracestreambuf<C,T>::sync()
{
  assert( epptr() == m_Skip + m_BufferSize || 
          epptr() == m_Buffer + m_BufferSize );

  int result = 0; // success
  C* putend  = ( epptr() == m_Buffer + m_BufferSize )
    ? pptr()
    : m_SavedPos;

  std::streamsize bufsize = putend - m_Buffer;

  // Schreiben, in assoziierten StreamBuffer,
  // dabei schreiben wir hinter jedes /n den
  // Zeilenprefix
  if (putend != m_Buffer)
  {
    writePrefix();
    std::streamsize written = m_Output->sputn(m_Buffer, bufsize);
    m_Output->pubsync();

    if (written != bufsize) result = -1;
  }

  // put-area neu setzen
  if ( epptr() == m_Buffer + m_BufferSize )
    // Trace war eingeschaltet und bleibt es auch
    setp(m_Buffer, m_Buffer + m_BufferSize);
  else
    setp(m_Skip, m_Skip + m_BufferSize);

  m_SavedPos = m_Buffer;

  return result;
}

template <class C, class T>
typename T::int_type 
basic_tracestreambuf<C,T>::overflow(typename T::int_type c)
{
  if ( c != traits_type::eof() )
  {
    if ( sync() != -1 )
    {
      *pbase() = traits_type::to_char_type(c);
      pbump(1);
    }
    else
      c = traits_type::eof();
  }

  return c;
}

template <class C, class T>
void basic_tracestreambuf<C,T>::endtrace()
{

}

template <class C, class T>
bool basic_tracestreambuf<C,T>::wouldTrace(trace_level level) const
{
  return level <= m_OutputLevel;
}

///////////////////////////////////////////////////////////////////////
// Setzen des Output- und Tracelevels
template <class E, class T>
std::basic_ostream<E, T>& setOutputLevel(std::basic_ostream<E, T>& o,
                                         basic_tracestreambufbase::trace_level l)
{
  basic_tracestreambuf<E,T>* trace = 
    dynamic_cast<basic_tracestreambuf<E,T>* >( o.rdbuf() );

  if (trace)
  {
    trace->setOutputLevel(l);
  }

  return o;
}

template <class E, class T>
std::basic_ostream<E, T>& setTraceLevel(std::basic_ostream<E, T>& o,
                                        basic_tracestreambufbase::trace_level l)
{
  basic_tracestreambuf<E,T>* trace = 
    dynamic_cast<basic_tracestreambuf<E,T>* >( o.rdbuf() );

  if (trace)
  {
    trace->setTraceLevel(l);
  }

  return o;
}

//////////////////////////////////////////////////////////////////
// Manipulatoren, zum setzen des Tracelevels
template <class E, class T>
std::basic_ostream<E, T>& trace_urgent(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_urgent);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_error(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_error);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_warning(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_warning);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_information(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_information);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_main(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_main);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_detail(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_detail);
}

template <class E, class T>
std::basic_ostream<E, T>& trace_all(std::basic_ostream<E, T>& o)
{
  return setTraceLevel(o, basic_tracestreambufbase::trace_all);
}

template <class E, class T>
std::basic_ostream<E, T>& endtrace(std::basic_ostream<E, T>& o)
{
  basic_tracestreambuf<E,T>* trace = 
    dynamic_cast<basic_tracestreambuf<E,T>* >( o.rdbuf() );

  if (trace)
  {
    trace->endtrace();
  }

  return o;
}

} // namespace tools
} // namespace ritter 
