/*! @file

    @id $Id$
*/
//       1         2         3         4         5         6         7         8
// 45678901234567890123456789012345678901234567890123456789012345678901234567890

#ifndef QBROWSERLIB_LOG
#define QBROWSERLIB_LOG

#include <qbrowserlib/ui_log.hxx>
#include <QDialog>

#include <stdexcept>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <typeinfo>

// supported Qt logging types
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QUrl>
#include <QtCore/QByteArray>

namespace qbrowserlib {

#ifndef LOG
#define LOG                                                             \
  qbrowserlib::Log(this, __PRETTY_FUNCTION__, __FILE__, __LINE__, false)
#endif

#ifndef LOG_FN
#define LOG_FN                                                          \
  qbrowserlib::Log(0, __PRETTY_FUNCTION__, __FILE__, __LINE__, false)
#endif

#ifndef TRC
#define TRC                                                             \
  qbrowserlib::Log __TRC_LOCAL(this, __PRETTY_FUNCTION__, __FILE__, __LINE__)
#endif
      
#ifndef TRC_FN
#define TRC_FN                                                  \
  qbrowserlib::Log __TRC_LOCAL(0, __PRETTY_FUNCTION__, __FILE__, __LINE__)
#endif

#ifdef CRYPTOLOG
#undef CRYPTOLOG
#endif
#define CRYPTOLOG(X)                                                    \
  qbrowserlib::Log(0, __PRETTY_FUNCTION__, __FILE__, __LINE__, false)<<X;

#ifdef OPENSSL_CHECK
#undef OPENSSL_CHECK
#endif
#define OPENSSL_CHECK(X)                                                \
  if (!(X)) {                                                           \
    ERR_load_ENGINE_strings();                                          \
    qbrowserlib::Log ss(0, __PRETTY_FUNCTION__, __FILE__, __LINE__, false); \
    for (unsigned int err(0); err=ERR_get_error();) {                   \
      ss<<"Error: "<<ERR_error_string(err, 0)<<"; ";                    \
    }                                                                   \
    ss<<"Command "<<#X<<" failed in function "<<__PRETTY_FUNCTION__     \
      <<" in file "<<__FILE__<<":"<<__LINE__;                           \
    throw std::runtime_error(ss.str());                                 \
  }

  class LogDialog;

  #ifdef DEBUG
  #undef DEBUG
  #endif
  
  class Log {
      
    public:
      
      static bool DEBUG;
      
    public:
      
      Log();
      
      Log(const void* addr, const std::string& name,
          const std::string& file, unsigned long line,
          bool traceOpenClose=true);
      
      template<typename TYPE> Log& operator<<(TYPE arg);
      
      ~Log() throw();
      
      static void show(QWidget* p);

      std::string str() {
        return _ss.str();
      }
      
    private:
      
      std::ostream& init(std::ostream& ss);
      
      std::ostream& indent(std::ostream& ss);
      
      std::ostream& close(std::ostream& ss);
      
    private:

      friend class LogDialog;
      
      static LogDialog* _dialog;
      static unsigned int _level;
      bool _debug;
      bool _close;
      const void* _addr;
      const std::string _name;
      const std::string _file;
      unsigned long _line;
      std::stringstream _ss;
      bool _clean;
  };

  class LogDialog: public QDialog, public Ui::LogDialog {

      Q_OBJECT;
          
    public:
          
      LogDialog(QWidget* p=0): QDialog(p) {
        setupUi(this);
      }
          
      template<typename TYPE> LogDialog& append(const Log& log, TYPE* arg) {
        std::ostringstream ss;
        ss<<"("<<typeid(TYPE*).name()<<")"<<arg;
        return append(log, ss.str());
      }
      
      template<typename TYPE> LogDialog& append(const Log& log, TYPE arg) {
        int pos(_logs->rowCount());
        _logs->insertRow(pos);
        _logs->setItem
          (pos, FILE,
           new QTableWidgetItem(QString::fromStdString(log._file)));
        _logs->setItem
          (pos, LINE, new QTableWidgetItem(QString::number(log._line)));
        if (log._addr) _logs->setItem
                         (pos, INSTANCE,
                          new QTableWidgetItem
                          (QString::number((qulonglong)log._addr)));
        _logs->setItem
          (pos, FUNCTION,
           new QTableWidgetItem(QString::fromStdString(log._name)));
        _logs->setItem
          (pos, MESSAGE,
           new QTableWidgetItem(QVariant(arg).toString()));
        return *this;
      }

    protected Q_SLOTS:

      void on__fileLine_toggled(bool checked) {
        if (checked) {
          _logs->showColumn(FILE);
          _logs->showColumn(LINE);
        } else {
          _logs->hideColumn(FILE);
          _logs->hideColumn(LINE);
        }
      }

      void on__instance_toggled(bool checked) {
        if (checked) {
          _logs->showColumn(INSTANCE);
        } else {
          _logs->hideColumn(INSTANCE);
        }
      }

      void on__function_toggled(bool checked) {
        if (checked) {
          _logs->showColumn(FUNCTION);
        } else {
          _logs->hideColumn(FUNCTION);
        }
      }

      void on__buttons_clicked(QAbstractButton* button) {
        if (_buttons->buttonRole(button)==QDialogButtonBox::ResetRole)
          for (int i(_logs->rowCount()); i>1; --i)
            _logs->removeRow(i-1);
      }

      void on__find_clicked(bool) {
        setCursor(QCursor(Qt::BusyCursor));
        QList<QTableWidgetItem*> items
          (_logs->findItems(_search->text(),
                            Qt::MatchWrap|Qt::MatchContains
                            |Qt::MatchFixedString));
        _logs->clearSelection();
        for(QList<QTableWidgetItem*>::iterator item(items.begin());
            item!=items.end(); ++item) {
          (*item)->setSelected(true);
        }
        if (!items.isEmpty()) _logs->scrollToItem(items[0]);
        unsetCursor();
      }
         
    private:

      friend class Log;
      enum {FILE, LINE, INSTANCE, FUNCTION, MESSAGE} Columns;
      
  };

  // note: template class method must be defined in the header
  template<typename TYPE> Log& Log::operator<<(TYPE arg) {
    if (!_debug) return *this;
    _clean = false;
    _ss<<" "<<arg;
    return *this;
  }
      
  template<> LogDialog& LogDialog::append
      (const Log& log, const char* arg);
  template<> LogDialog& LogDialog::append
      (const Log& log, std::string arg);
  template<> LogDialog& LogDialog::append
      (const Log& log, unsigned long arg);

  std::ostream& operator<<(std::ostream& ss, QString arg);
  std::ostream& operator<<(std::ostream& ss, QStringList arg);
  std::ostream& operator<<(std::ostream& ss, QUrl arg);
  std::ostream& operator<<(std::ostream& ss, QByteArray arg);
}

#endif
