/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 * In particular this file is part of the Framework Core Library
 *
 * The iWear Framework 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 as in version 2 of the License.

 * 
 * The iWear Framework 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
 * The iWear Framework; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifndef __IWEAR_DEBUGSTREAM_H
#define __IWEAR_DEBUGSTREAM_H

#ifndef __IWEAR_DEBUGMANAGER_H
#include <iwear/debugmanager.h>
#endif

#ifndef __IWEAR_DEBUG_ENUM_H
#include <iwear/debug_enum.h>
#endif

#ifndef __IWEAR_THREADLOCKER_H
#include <iwear/threadlocker.h>
#endif

#ifndef __IWEAR_THREADLOCKED_H
#include <iwear/threadlocked.h>
#endif

#include "iwear/debugarea.h"

#ifndef __IWEAR_MUTEX_H
#include <iwear/mutex.h>
#endif

#include <set>

#ifndef IWEAR_DEBUGLEVEL
#define IWEAR_DEBUGLEVEL 4
#endif

#define DEBUG_ERROR 0
#define DEBUG_WARNING 1
#define DEBUG_INFO 2
#define DEBUG_DEBUG 3
#define DEBUG_NONSENSE 4

#define DEBUG_BUFFER 512

using std::ios_base;
using std::streambuf;
using std::set;

namespace iwear
{

/**
 * This class is a stream that is flexibly configurable to be enable and
 * disabled for certain variable conditions.
 * While there exist several debuglevels, each corresponding to a single
 * pseudo-object, every usage of those streams can be made belonging to a
 * certain area. Additionally all those streams are in that sense thread safe
 * that you don't actually get the same object for two threads (therefore
 * pseudo-objects), but every thread has its own object. This is necessary to
 * allow for easy settings of debugareas.
 *
 * Examples:\n
 * d_dbg << "Hell World\n"; // Would only be visible if the debuglevel for the
 * // corresponding debugmanager is set to debug or higher.
 *
 * d_warn << debugarea::iwear_core << "World Hello\n"; // Would only be visible
 * if the debuglevel is set to warning or higher and if the core debugarea is
 * set to be active.
 */    
class debugstream : protected streambuf, public std::iostream, public iwear::threadlocked
{
private:
    /**
     * The debuglevel of this stream object. It will be send to the
     * debugmanager upon sending the buffer to it, and then it will be decided
     * if the message will be emitted.
     */
    debuglevel my_lev;

    /**
     * The debugmanager this stream is attached to.
     */
    debugmanager& dman;

    /**
     * The buffers needed for the streambuf functionality.
     */
    char * gbuf;
    char * pbuf;

    size_t buffer_size;
    
    /**
     * Internal help function to allocate the correct buffer sizes.
     */
    void allocate_buffer( size_t bytes );

    /**
     * function of basic_streambuf, being called mainly whenever some
     * std::flush went into the stream.
     */
    virtual int sync( void );

    /**
     * Whenever the buffer is full, this function will be called with the char
     * that should be filled next into the buffer.
     */
    virtual int overflow( int c );

protected:
public:
    /**
     */
    debugstream( debugmanager& dbgman, debuglevel dbglvl = error, size_t buffer_size = DEBUG_BUFFER );

    ~debugstream( );

    /**
     * Convenience function for those who like printf style logging more. This
     * actually does not fill data into the internal buffer, so this
     * additionally is a way to bypass the buffer and write directly to the
     * debugmanager.
     */
    int log( const char*, ... );

    inline void set_debuglevel( debuglevel dlev ) { dman.set_debuglevel(dlev); }

    inline void set_debuglevel( debuglevel dlev, const debugarea& da ) { dman.set_debuglevel(dlev,da); }

    void set_debugarea( const debugarea& ar ) { dman.set_debugarea(ar.value); }

    inline void set_levelmode( debugleveltype nl ) { dman.set_levelmode(nl); }

    inline debuglevel get_debuglevel( void ) { return dman.get_debuglevel(); }

    inline void set_as_default( void ) { dman.set_debuglevel(my_lev); }

    /**
     * This function returns a stream that is handling the data locally for the
     * current thread. It should not be called directly, but only invoked by
     * the macros conveniently wrapping it up into the old d_dbg etc. stream
     * object names.
     * @note The macros deliberately choose to not include the iwear::
     * namespace scope resolution. That way code using iwear::d_dbg should
     * function correctly, and also code using d_dbg without specifying the
     * namespace should fail as it would be expected by d_dbg being an actual
     * object. Just the name of the function being mentioned in the error
     * message will be a different one.
     */
    static debugstream& get_thread_local_stream( debuglevel );

    static bool registered_for_thread_creation;

    /**
     * This is the mutex protecting the static structures of the debugstream
     * class.
     */
    static iwear::Mutex* global_mutex;

    static void check_mutex( void );

    typedef 
    std::map<
    std::pair<pthread_t, debuglevel>,
	    debugstream*> tls_stream_container;

    static tls_stream_container* tls_streams;

    /**
     * On the event of a destruction of a thread this function shall be called.
     * We will register this the first time any thread local stream is
     * requested, therefore there will be a flag indicating the successful
     * registration with the thread creation and destruction mechanism.
     */
    static void thread_destroyed( pthread_t thread_id, bool );

    static void set_debugarea( std::ostream& o, const debugarea& );

};

inline std::ostream& operator<<( std::ostream& o, const iwear::debugarea& da )
{
    debugstream::set_debugarea(o,da);
    return o;
}

class nulldebugstream : public std::ostream
{
private: 
protected:
public:
    inline nulldebugstream& operator<<( 
	    std::ostream::__ostream_type& (*)(std::ostream::__ostream_type&)) { return *this; }
    inline nulldebugstream& operator<<( 
	    std::ostream::__ios_type& (*)(std::ostream::__ios_type&)) { return *this;}
    inline nulldebugstream& operator<<( ios_base& (*) (ios_base&)) { return *this; }

    template<class T>
    inline nulldebugstream& operator<< ( const T& ) { return *this; }

    inline int log( const char*, ... ) { return -1; }

    inline void set_debuglevel( debuglevel dlev ) { debugmanager::DebugManager.set_debuglevel(dlev); }

    inline void set_levelmode( debugleveltype nl ) { debugmanager::DebugManager.set_levelmode(nl); }

    inline debuglevel get_debuglevel( void ) { return debugmanager::DebugManager.get_debuglevel(); }

    inline void set_as_default( void ) {  }
};


// This one is alway emitted
//extern debugstream<DEBUG_BUFFER> d_err;
#define d_err debugstream::get_thread_local_stream( iwear::error )

#if( IWEAR_DEBUGLEVEL >= DEBUG_WARNING )
#define D_WARN(x) x
//extern debugstream<DEBUG_BUFFER> d_warn;
#define d_warn debugstream::get_thread_local_stream( iwear::warning )
#else
#define D_WARN(x)
extern nulldebugstream d_warn;
#endif

#if( IWEAR_DEBUGLEVEL >= DEBUG_INFO)
#define D_INF(x) x
//extern debugstream<DEBUG_BUFFER> d_inf;
#define d_inf debugstream::get_thread_local_stream( iwear::info )
#else
#define D_INF(x)
extern nulldebugstream d_inf;
#endif

#if( IWEAR_DEBUGLEVEL >= DEBUG_DEBUG)
#define D_DBG(x) x
//extern debugstream<DEBUG_BUFFER> d_dbg;
#define d_dbg debugstream::get_thread_local_stream( iwear::debug )
#else
#define D_DBG(x)
extern nulldebugstream d_dbg;
#endif

#if( IWEAR_DEBUGLEVEL >= DEBUG_NONSENSE)
#define D_NONS(x) x
//extern debugstream<DEBUG_BUFFER> d_nons;
#define d_nons debugstream::get_thread_local_stream( iwear::nonsens )
#else
#define D_NONS(x)
extern nulldebugstream d_nons;
#endif

}
#endif
