/**
 * @file
 * $Id$
 * $Revision$
 * $Author$
 * $Date$
 *
 * This file is part of The iWear Framework.
 *
 * 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_SCOPETRACER_H
#define __IWEAR_SCOPETRACER_H


#include "iwear/debugstream.h"
#include <map>
#include <sstream>
#include <stdexcept>

namespace iwear
{

#ifdef IWEAR_DEBUG
#define SCOPET_FNBODY ;
#define SCOPET_CTORINITIALIZER
#else
#define SCOPET_FNBODY {}
#define SCOPET_CTORINITIALIZER :  level(0),upper(0), out(DBOUT)
#endif

#if( IWEAR_DEBUGLEVEL < DEBUG_WARNING )
#define DBOUT std::cout
#else
#define DBOUT iwear::d_nons
#endif

#ifdef IWEAR_DEBUG
#define SCOPE_TRACE_UVARNO(x) SCOPE_TRACE_VARNO(x)
#define SCOPE_TRACE_VARNO(x) _stvar##x

#define TRACE_SCOPE(...) scope_tracer_base<> SCOPE_TRACE_UVARNO(__LINE__) (__FFL__+ "{ "#__VA_ARGS__ " }",##__VA_ARGS__ )
#else
#define TRACE_SCOPE(...)
#endif

/**
 * Never ever create an object of this type via new !
 */    
template<class S = std::ostream>
class scope_tracer_base
{ 
public:
    typedef S stream_type;
private:
    /**
     * Keep track of the latest scope_tracer_base in the actual thread, to form a backtrace.
     */
    static std::map<pthread_t, scope_tracer_base*>* latest;

    /**
     * This is the level of the actual scope_tracer_base. Its kept here mainly for
     * output reasons, so we can format a nice indented backtrace.
     */
    uint32_t level;

    /**
     * That is the text that should be outputted at every backtrace line. It
     * should be a single relatesalso see line of text, ideally only the function name and its
     * the parameters.
     * @param
     */
    std::string tracetext;    

    /**
     * Simple single link to the upper scope_tracer_base so we can just iterate
     * through to get the backtrace.
     */
    scope_tracer_base* upper;

    /**
     * We dont want to have a tracer object without text, or copies
     */
    inline scope_tracer_base( ) : out(DBOUT) { }

    inline scope_tracer_base( const scope_tracer_base& ) : out(DBOUT) { } 

    /**
     * The stream to do the output on.
     */
    stream_type& out;

    static bool activated;
public:

    static void activate( void ) { activated = true; }

    scope_tracer_base( const std::string& , std::ostream& ) SCOPET_CTORINITIALIZER SCOPET_FNBODY

    scope_tracer_base( const std::string& ) SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * @param fn is the function name thats currently being called, the rest is
     * the parameters to the function, if applicable (don't add them here, if
     * the don't have a defined operator<<.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5 , class T6 ,
	class T7 , class T8 , class T9 ,
	class T10
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8, const T9& t9,
	    const T10& t10)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 9 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5 , class T6 ,
	class T7 , class T8 , class T9 
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8, const T9& t9)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 8 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5 , class T6 ,
	class T7 , class T8 
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 7 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5 , class T6 ,
	class T7 
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 6 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5 , class T6
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 5 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 , class T5
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY


    /**
     * The 4 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3 ,
	class T4 
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 3 Parameter variant of the above function.
     */
    template<
	class T1 , class T2 , class T3
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY

    /**
     * The 2 Parameter variant of the above function.
     */
    template<
	class T1 , class T2
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2)
    SCOPET_CTORINITIALIZER SCOPET_FNBODY


    /**
     * The 1 Parameter variant of the above function.
     */
    template<
	class T1
	>
    scope_tracer_base( const std::string& fn,
	    const T1& t1 )
    SCOPET_CTORINITIALIZER SCOPET_FNBODY


    static void print_backtrace( std::ostream& o );

    static void print_all_backtraces( std::ostream& o );

    ~scope_tracer_base() SCOPET_FNBODY

#ifdef IWEAR_DEBUG
private:

    void do_enter( void );

    void do_leave( void );

    void do_prefix_output( void );

    void do_leave_output( void );

    void do_enter_output( void );

    /**
     * Prevent anyone from using new with this class. In case anyone within
     * this class calls new for it, we even throw.
     */
    void* operator new(size_t) { throw std::bad_alloc(); }

    template<class T>
    void add_parameter( std::string& s, const T& t, const char* );
#endif

};

typedef scope_tracer_base<std::ostream> ScopeTracer;

#ifdef IWEAR_DEBUG

template<class S>
template<class T>
void scope_tracer_base<S>::add_parameter( std::string& s, const T& t, const char* txt )
{
    s+=typenameof(t);
    s+= " ";
    s+= txt;
    s+= " = ";
    std::stringstream ss;
    ss << t;
    s+= ss.str();
    s+=",";
}

// 10 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5, class T6,
    class T7, class T8, class T9,
    class T10
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8, const T9& t9,
	    const T10& t10)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    this->add_parameter(tracetext,t6,"p6");
    this->add_parameter(tracetext,t7,"p7");
    this->add_parameter(tracetext,t8,"p8");
    this->add_parameter(tracetext,t9,"p9");
    this->add_parameter(tracetext,t10,"p10");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 9 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5, class T6,
    class T7, class T8, class T9
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8, const T9& t9)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    this->add_parameter(tracetext,t6,"p6");
    this->add_parameter(tracetext,t7,"p7");
    this->add_parameter(tracetext,t8,"p8");
    this->add_parameter(tracetext,t9,"p9");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 
// 8 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5, class T6,
    class T7, class T8
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7, const T8& t8)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    this->add_parameter(tracetext,t6,"p6");
    this->add_parameter(tracetext,t7,"p7");
    this->add_parameter(tracetext,t8,"p8");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 
// 7 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5, class T6,
    class T7
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6,
	    const T7& t7)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    this->add_parameter(tracetext,t6,"p6");
    this->add_parameter(tracetext,t7,"p7");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 6 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5, class T6
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5, const T6& t6)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    this->add_parameter(tracetext,t6,"p6");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 5 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4, class T5
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4, const T5& t5)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    this->add_parameter(tracetext,t5,"p5");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 4 Parameter variant
template<class S>
template<
    class T1, class T2, class T3,
    class T4
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3,
	    const T4& t4)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    this->add_parameter(tracetext,t4,"p4");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 3 Parameter variant
template<class S>
template<
    class T1, class T2, class T3
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2, const T3& t3)
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    this->add_parameter(tracetext,t3,"p3");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 

// 2 Parameter variant
template<class S>
template<
    class T1, class T2
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1, const T2& t2 )
	: level(0), tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    this->add_parameter(tracetext,t2,"p2");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
 
template<class S>
template<
    class T1
    >
scope_tracer_base<S>::scope_tracer_base( const std::string& fn,
	    const T1& t1)
	: level(0),tracetext(fn), upper(0), out(DBOUT)
{
    tracetext += "(";
    this->add_parameter(tracetext,t1,"p1");
    tracetext += ")";
    do_enter();
    do_enter_output();
}
   

template<class S>
scope_tracer_base<S>::scope_tracer_base( const string& tt, std::ostream& o) : level(0),tracetext(tt), out(o)
{
    do_enter();
    do_enter_output();
}


template<class S>
scope_tracer_base<S>::scope_tracer_base( const string& tt) : level(0),tracetext(tt), out(DBOUT) 
{ 
    do_enter();
    do_enter_output();
}

template<class S>
inline
scope_tracer_base<S>::~scope_tracer_base( void ) 
{ 
    do_leave_output();
    do_leave();
}

template<class S>
void scope_tracer_base<S>::do_prefix_output( void )
{
    string spc(level,' ');
    out << spc;
}

template<class S>
void scope_tracer_base<S>::do_enter( void )
{
    if( ! latest )
    {
	latest = new std::map<pthread_t, scope_tracer_base*>;
    }
    scope_tracer_base*& ur = (*latest)[pthread_self()];
    upper = ur;
    if(upper)
    {
	level = upper->level + 1;
    }
    ur = this;
}

template<class S>
void scope_tracer_base<S>::do_leave( void )
{
    if( ! latest )
    {
	BUG("this should not happen, latest should have been set by do_enter");
    }
    (*latest)[pthread_self()] = upper;
}

template<class S>
void scope_tracer_base<S>::do_leave_output( void )
{
    if( ! activated ) return;
    do_prefix_output();
    out << "Leave : " << tracetext << std::endl;
}

template<class S>
void scope_tracer_base<S>::do_enter_output( void )
{
    if( ! activated ) return;
    do_prefix_output();
    out << "Enter : " << tracetext << std::endl;
}


#endif
template<class S>
std::map<pthread_t,scope_tracer_base<S>*>* scope_tracer_base<S>::latest = 0;

template<class S>
bool scope_tracer_base<S>::activated = false;


template<class S>
void scope_tracer_base<S>::print_backtrace( std::ostream& o )
{
    if( latest )
    {
	scope_tracer_base* st = (*latest)[pthread_self()];
	if( ! st )
	{
	    o << "No Backtrace Available" << std::endl;
	    return;
	}

	o << "Backtrace saved in Thread 0x" << std::hex << pthread_self() << std::endl;
	int lev = 0;
	while( st )
	{
	    o << "#" << lev++ << "  " << st->tracetext << std::endl;
	    st = st->upper;
	}
    }
    else
    {
	o << "No Backtrace Available" << std::endl;
    }
}

template<class S>
void scope_tracer_base<S>::print_all_backtraces( std::ostream& o )
{
    if( latest )
    {
	typename std::map<pthread_t,scope_tracer_base*>::iterator it(latest->begin());
	for(; it != latest->end(); ++it )
	{
	    scope_tracer_base* st = it->second;
	    if( ! st )
	    {
		o << "No Backtrace Available" << std::endl;
		return;
	    }

	    if( it->first == Thread::get_main_thread() )
	    {
		o << "Backtrace saved in Thread 0x" << std::hex << it->first << " (main thread)" << std::endl;
	    }
	    else
	    {
		o << "Backtrace saved in Thread 0x" << std::hex << it->first << std::endl;
	    }
	    int lev = 0;
	    while( st )
	    {
		o << "#" << lev++ << "  " << st->tracetext << std::endl;
		st = st->upper;
	    }
	}
    }
    else
    {
	o << "No Backtrace Available" << std::endl;
    }
}


}

#ifndef NO_SCOPE_UNDEF
#undef SCOPET_FNBODY
#undef SCOPET_CTORINITIALIZER
#undef DBOUT
#endif

#endif
