/***************************************************************************
                          kmessdebug.cpp -  description
                             -------------------
    begin                : Mon Sep 5 2005
    copyright            : (C) 2005 by Diederik van der Boor
    email                : vdboor --at-- codingdomain.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/


#include "kmessdebug.h"

#include <sys/types.h>
#include <unistd.h>

#include <QTime>


// kmessdebug.h adds a KMESS_NULL macro to detect
// and display null pointer warnings. This is used
// in a " if(KMESS_NULL(var)) return; " statement
// Depening on the debug level, the filename
// and line number info will be included.

#ifdef KMESSTEST

  /**
   * @brief Warns that a pointer was null, called from the <code>KMESS_NULL</code> macro.
   *
   * The function outputs a warning when the pointer is null.
   * This is the full debug version. When KMess is configured without <code>--enable-debug-output</code>,
   * another version of this function is compiled instead; that version does not define the file and line parameters.
   * @param  isNull    Whether the pointer passed to <code>KMESS_NULL</code> is null.
   * @param  var       The name of the variable passed to the <code>KMESS_NULL</code> macro.
   * @param  funcinfo  The function where the <code>KMESS_NULL</code> macro is called from.
   * @param  file      The filename where the <code>KMESS_NULL</code> macro is called from.
   * @param  line      The line number where the <code>KMESS_NULL</code> macro is called from.
   * @ingroup Debug
   */
  bool _kmessWarnNull(bool isNull, const char *var, const char *funcinfo, const char *file, int line)
  {
    if( isNull )
    {
      kWarning().nospace() << "'" << var << "' should not be null in " << funcinfo << ", " << file << " (" << line << ")!";
    }

    return isNull;
  }

#else

  /**
   * @brief Warns that a pointer was null, called from the <code>KMESS_NULL</code> macro.
   *
   * The function outputs a warning when the pointer is null.
   * @param  isNull    Whether the pointer passed to <code>KMESS_NULL</code> is null.
   * @param  var       The name of the variable passed to the <code>KMESS_NULL</code> macro.
   * @param  funcinfo  The function where the <code>KMESS_NULL</code> macro is called from.
   * @ingroup Debug
   */
  bool _kmessWarnNull(bool isNull, const char *var, const char *funcinfo)
  {
    if(isNull)
      kWarning() << "'" << var << "' should not be null in " << funcinfo << "!";
    return isNull;
  }

#endif



/**
 * @brief Warns when a pre- or post- condition fails.
 *
 * This function is called from the <code>KMESS_ASSERT</code> macro.
 * It outputs a warning when a test fails.
 * Unlike Q_ASSERT(), this function won't crash KMess,
 * since it could be trigged by an invalid client
 *
 * @param  assertion The source code of the condition passed to the <code>KMESS_ASSERT</code> macro.
 * @param  file      The filename where the <code>KMESS_ASSERT</code> macro is called from.
 * @param  line      The line number where the <code>KMESS_ASSERT</code> macro is called from.
 * @ingroup Debug
 */
bool _kmessAssert( const char *assertion, const char *file, int line )
{
  qWarning( "ASSERT: \"%s\" in file %s, line %d", assertion, file, line );
  return false;
}



#ifdef KMESS_NETWORK_WINDOW

  // The network window can optionally be compiled.
  // The macro's from kmessdebug.h make it easy to handle this
  // without optionally depending on networkwindow.h everywhere.

  #include "dialogs/networkwindow.h"

  /**
   * @brief Inform the network window the connection was closed, used by <code>KMESS_NET_CLOSE</code>.
   * @param  connection  Reference to the caller object, to distinguish between connections.
   * @ingroup Debug
   */
  void _kmessNetClose(QObject *connection)
  {
    NetworkWindow::instance()->connectionClosed(connection);
  }

  /**
   * @brief Inform the network window about a new connection, used by <code>KMESS_NET_INIT</code>.
   * @param  connection  Reference to the caller object, to distinguish between connections.
   * @param  title       The title for the connection in the network window.
   * @ingroup Debug
   */
  void _kmessNetInit(QObject *connection, const QString &title)
  {
    NetworkWindow::instance()->setConnectionTitle(connection, title);
  }

  /**
   * @brief Add the received data to the network window, used by <code>KMESS_NET_RECEIVED</code>.
   * @param  connection  Reference to the caller object, to distinguish between connections.
   * @param  message     The data received from the connection, should be a complete message.
   * @ingroup Debug
   */
  void _kmessNetReceived(QObject *connection, const QByteArray &message)
  {
    NetworkWindow::instance()->addIncomingMessage(connection, message);
  }

  /**
   * @brief Add the sent data to the network window, used by <code>KMESS_NET_SENT</code>.
   * @param  connection  Reference to the caller object, to distinguish between connections.
   * @param  message     The data sent to the connection, should be a complete message.
   * @ingroup Debug
   */
  void _kmessNetSent(QObject *connection, const QByteArray &message)
  {
    NetworkWindow::instance()->addOutgoingMessage(connection, message);
  }

#endif


/**
 * @brief A Qt debug message handler.
 *
 * This function can be installed using @code qInstallMsgHandler( kmessDebugPrinter ); @endcode
 * It's used to avoid unwanted exit(1) or abort() calls.
 *
 * @param  type     The debug message type, generated by qDebug(), qFatal(), etc..
 * @param  message  The message to print.
 * @ingroup Debug
 */
void kmessDebugPrinter( QtMsgType type, const char *message )
{
  bool isAssert = ( strncmp( message, "ASSERT", 7 ) == 0 ); // 'ASSERT failure' or 'ASSERT: '
  static QTime timer;
  if( ! timer.isValid() )
    timer.start();
  float elapsed = (float)timer.elapsed() / (float)1000;

  if( isAssert )
  {
    // can be qWarning() by us or qFatal() from Qt.
    fprintf( stderr, "%.3f> kmess(%d) %s\n", elapsed, getpid(), message );

#ifdef KMESSTEST
    // Crash on qFatal() from Qt, called from Q_ASSERT()
    // Don't crash on qWarning() from KMESS_ASSERT(). Could be triggered by bad P2P clients for example.
    if( type == QtFatalMsg )
    {
      abort();
    }
#endif
  }
  else
  {
    switch( type )
    {
      case QtDebugMsg:
        fprintf( stderr, "%.3f> %s\n", elapsed, message );
        break;

      case QtWarningMsg:
        fprintf( stderr, "%.3f> WARNING: %s\n", elapsed, message );
        break;

      case QtCriticalMsg:
        fprintf( stderr, "%.3f> CRITICAL: %s\n", elapsed, message );
        break;

      case QtFatalMsg:
        fprintf( stderr, "%.3f> FATAL: %s\n", elapsed, message );

#ifdef KMESSTEST
        abort();
#endif

        break;
    }
  }
}

