// Author:    Max Howell <max.howell@methylblue.com>, (C) 2003-5
// Copyright: See COPYING file that comes with this distribution
//

#ifndef CODEINE_DEBUG_H
#define CODEINE_DEBUG_H

#include <kdebug.h>
#include <tqcstring.h>
#include <tqvariant.h>
#include <sys/time.h>

class TQApplication; ///@see Debug::Indent
extern TQApplication *tqApp;


/**
 * @namespace Debug
 * @short kdebug with indentation functionality and convenience macros
 * @author Max Howell <max.howell@methylblue.com>
 *
 * Usage:
 *
 *     #define DEBUG_PREFIX "Blah"
 *     #include "debug.h"
 *
 *     void function()
 *     {
 *        Debug::Block myBlock( __PRETTY_FUNCTION__ );
 *
 *        debug() << "output1" << endl;
 *        debug() << "output2" << endl;
 *     }
 *
 * Will output:
 *
 * app: BEGIN: void function()
 * app:   [Blah] output1
 * app:   [Blah] output2
 * app: END: void function(): Took 0.1s
 *
 * @see Block
 * @see CrashHelper
 * @see ListStream
 */


namespace Debug
{
   inline TQCString &indent()
   {
      static TQCString indent;
      return indent;
      #if 0
      static timeval *stamp = 0;

      if( stamp == 0 ) {
         stamp = new timeval;
         return "[00:00] "; }

      timeval now;
      gettimeofday( &now, 0 );
      now.tv_sec -= stamp->tv_sec;

      TQString time( "[%1:%2]" );

      return time.arg( now.tv_sec / 60, 2 ).arg( now.tv_sec % 60, 2 ).latin1() + indent;
      #endif
   }

   #ifdef NDEBUG
      static inline kndbgstream debug()   { return kndbgstream(); }
      static inline kndbgstream warning() { return kndbgstream(); }
      static inline kndbgstream error()   { return kndbgstream(); }
      static inline kndbgstream fatal()   { return kndbgstream(); }

      static inline void debug1( TQVariant v ) {}

      typedef kndbgstream Stream;
   #else
      #ifndef DEBUG_PREFIX
      #define AMK_PREFIX ""
      #else
      #define AMK_PREFIX "[" DEBUG_PREFIX "] "
      #endif

      //from kdebug.h
      enum DebugLevels {
         KDEBUG_INFO  = 0,
         KDEBUG_WARN  = 1,
         KDEBUG_ERROR = 2,
         KDEBUG_FATAL = 3
      };

      static inline kdbgstream debug()   { return kdbgstream( indent(), 0, KDEBUG_INFO  ) << AMK_PREFIX; }
      static inline kdbgstream warning() { return kdbgstream( indent(), 0, KDEBUG_WARN  ) << AMK_PREFIX << "[WARNING!] "; }
      static inline kdbgstream error()   { return kdbgstream( indent(), 0, KDEBUG_ERROR ) << AMK_PREFIX << "[ERROR!] "; }
      static inline kdbgstream fatal()   { return kdbgstream( indent(), 0, KDEBUG_FATAL ) << AMK_PREFIX; }

      /// convenience function
      static inline void debug1( TQVariant v ) { kdbgstream( indent(), 0, KDEBUG_INFO ) << v << endl; }

      typedef kdbgstream Stream;

      #undef AMK_PREFIX
   #endif

   typedef kndbgstream DummyStream;
}

using Debug::debug;
using Debug::debug1;

/// Standard function announcer
#define DEBUG_FUNC_INFO kdDebug() << Debug::indent() << k_funcinfo << endl;

/// Announce a line
#define DEBUG_LINE_INFO kdDebug() << Debug::indent() << k_funcinfo << "Line: " << __LINE__ << endl;

/// Convenience macro for making a standard Debug::Block
#define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ );

#define DEBUG_INDENT Debug::indent() += "  ";
#define DEBUG_UNINDENT { TQCString &s = Debug::indent(); s.truncate( s.length() - 2 ); }

/// Use this to remind yourself to finish the implementation of a function
#define DEBUG_NOTIMPLEMENTED warning() << "NOT-IMPLEMENTED: " << __PRETTY_FUNCTION__ << endl;

/// Use this to alert other developers to stop using a function
#define DEBUG_DEPRECATED warning() << "DEPRECATED: " << __PRETTY_FUNCTION__ << endl;


namespace Debug
{
    /**
     * @class Debug::Block
     * @short Use this to label sections of your code
     *
     * Usage:
     *
     *     void function()
     *     {
     *         Debug::Block myBlock( "section" );
     *
     *         debug() << "output1" << endl;
     *         debug() << "output2" << endl;
     *     }
     *
     * Will output:
     *
     *     app: BEGIN: section
     *     app:  [prefix] output1
     *     app:  [prefix] output2
     *     app: END: section - Took 0.1s
     *
     */

    class Block
    {
        timeval     m_start;
        const char *m_label;

    public:
        Block( const char *label )
                : m_label( label )
        {
            gettimeofday( &m_start, 0 );

            kdDebug() << indent() << "BEGIN: " << label << "\n";
            DEBUG_INDENT
        }

        ~Block()
        {
            timeval end;
            gettimeofday( &end, 0 );

            end.tv_sec -= m_start.tv_sec;
            if( end.tv_usec < m_start.tv_usec) {
                // Manually carry a one from the seconds field.
                end.tv_usec += 1000000;
                end.tv_sec--;
            }
            end.tv_usec -= m_start.tv_usec;

            double duration = double(end.tv_sec) + (double(end.tv_usec) / 1000000.0);

            DEBUG_UNINDENT
            kdDebug() << indent() << "END__: " << m_label
                      << " - Took " << TQString::number( duration, 'g', 3 ) << "s\n";
        }
    };


    /**
     * @name Debug::stamp()
     * @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed
     *
     * Usage:
     *
     *     {
     *         Debug::stamp();
     *         function1();
     *         Debug::stamp();
     *         function2();
     *         Debug::stamp();
     *     }
     *
     * Will output (assuming the crash occurs in function2()
     *
     *     app: Stamp: 1
     *     app: Stamp: 2
     *
     */

    inline void stamp()
    {
        static int n = 0;
        debug() << "| Stamp: " << ++n << endl;
    }
}


namespace Debug
{
    /**
     * @class Debug::List
     * @short You can pass anything to this and it will output it as a list
     *
     *     debug() << (Debug::List() << anInt << aString << aTQStringList << aDouble) << endl;
     */

    typedef TQValueList<TQVariant> List;
}


#include <tdemessagebox.h>

namespace Codeine
{
   //FIXME this function is inlined, so this may cause linkage problems for some people..
   extern class VideoWindow* const videoWindow();

   namespace MessageBox
   {
      static inline void error( const TQString &message )
      {
         KMessageBox::error( (TQWidget*)videoWindow(), message );
      }

      static inline void sorry( const TQString &message )
      {
         KMessageBox::error( (TQWidget*)videoWindow(), message );
      }

      static inline void information( const TQString &message, const TQString &title )
      {
         KMessageBox::information( (TQWidget*)videoWindow(), message, title );
      }
   }
}

#endif
