#include "ads_debug.h"

#include <QDockWidget>
#include <QTreeWidget>
#include <QAction>
#include <QMainWindow>
#include <QStatusBar>
#include <QLabel>
#include <QHBoxLayout>
#include <QPlainTextEdit>
#include <QDebug>

#include "time_metrics.h"
#include "itc_triplebuffer.h"
//#include "itc_multibuffer.h"



//static metricRegistered mShowUpd("dbg-update", 20000);

class MWidget;
class MbWidget;

class StsLed: public QLabel
{
public:
  StsLed(const QString& lbl):
      QLabel(lbl)
  {
    level=0;
  }

  void setLevel(int lvl)
  {
    if (level==lvl)
      return;
    level=lvl;
    if (level<1)
      setStyleSheet("");
    if (level<2)
      setStyleSheet("background-color: green");
    else if (level<3)
      setStyleSheet("background-color: yellow");
    else
      setStyleSheet("background-color: red");
  }

  int level;

};

struct dbg_widgets_t
{
  QDockWidget* dockMetrics;
  //QDockWidget* dockLog;
  QDockWidget* dockBuffers;

  MWidget* metricW;
  MbWidget* bufferW;
  QPlainTextEdit* logW;

  StsLed* dlLabel;

  int timer_id;

  //QWidget* stsLineWidget;
  //QHBoxLayout* stsLayout;
  StsLed* logErrLabel;

  AdsDebug* instance;

  int last_log_level;
};

static dbg_widgets_t dbg;

//static QPlainTextEdit* dbgQtOutput;

#if 0
static void my_qdebug_output(QtMsgType type, const QMessageLogContext &/*context*/, const QString &msg)
{
   if (dbgQtOutput)
   {
     QString tmp;

     switch (type) {
     case QtDebugMsg:
       tmp=QString("[QT]D: %1").arg(msg);
         break;
     case QtWarningMsg:
       tmp=QString("[QT]W: %1").arg(msg);
         break;
     case QtCriticalMsg:
       tmp=QString("[QT]C: %1").arg(msg);
         break;
     case QtFatalMsg:
       tmp=QString("[QT]F: %1").arg(msg);
       break;
     default:
       tmp=QString("[QT]: %1").arg(msg);
     }
     dbgQtOutput->appendPlainText(tmp);
   }
}
#endif

class MbWidget: public QTreeWidget
{
public:
    int t_id;

    MbWidget(QWidget* parent):
            QTreeWidget(parent)
    {
        setObjectName("MOnMbTreeWidget");
        this->setHeaderLabels(
                QStringList()
                  <<QString("Name")
                  <<QString("Produced")
                  <<QString("Consumed")
                  <<QString("Not Prod.")
                  <<QString("Not Cons."));
        this->setAlternatingRowColors(true);

        {
            itc_triplebuffer_register* mb=itc_triplebuffer_register::root();
            for( ; mb; mb=mb->next())
            {
                QTreeWidgetItem* item=new QTreeWidgetItem(this);

                item->setText(0, mb->name());
                item->setData(0, Qt::UserRole, (unsigned int)mb);
            }
        }
        //t_id=startTimer(1000);
    }

    void update()
    {
      if (!isHidden())
        {
          QTreeWidgetItem* r=invisibleRootItem();

          for(int i=0; i<r->childCount(); ++i)
          {
            QTreeWidgetItem* c=r->child(i);
            itc_triplebuffer_register* mb=(itc_triplebuffer_register*)c->data(0, Qt::UserRole).toUInt();
            if (!mb)
                continue;
            c->setData(1, Qt::DisplayRole, mb->mb()->produced_counter());
            c->setData(2, Qt::DisplayRole, mb->mb()->consumed_counter());
            c->setData(3, Qt::DisplayRole, mb->mb()->not_produced_counter());
            c->setData(4, Qt::DisplayRole, mb->mb()->not_consumed_counter());
          }
      }
    }
};

class MWidget: public QObject /*: public QTreeWidget*/
{
public:
    QTreeWidget& w;
    QAction* actReset;
    QAction* actResetAll;
    QAction* actRefresh;
    QAction* actResize;

    //QLabel stsLabel;

    int some_dl_miss;

    MWidget(QWidget* parent):
        w(*new QTreeWidget(parent))
    {
        setObjectName("MonMetricsWidget");

        w.setHeaderLabels(
                QStringList()
                  <<QString("Name")
                  <<QString("samples")
                  <<QString("Last")
                  <<QString("avg")
                  <<QString("min")
                  <<QString("max")
                  <<QString("miss")
                  <<QString("p-last")
                  <<QString("p-avg")
                  <<QString("p-min")
                  <<QString("p-max")
                  <<QString("dl-miss")
                  );
        m_update_turn=false;
        /*
        QTreeWidgetItem* root=new QTreeWidgetItem(this);
        root->setText(0, "Time Metrics");
        root->setText(1, "");
        root->setExpanded(true);
        */
        w.setAlternatingRowColors(true);

        fillMetrics(metricRegistered::first(), w.invisibleRootItem());
        //t_id=startTimer(1000);

        actReset=new QAction(QString("Reset Selected"), &w);
        actResetAll=new QAction(QString("Reset All"), &w);
        actRefresh=new QAction(QString("Refresh"), &w);
        actResize=new QAction(QString("Resize"), &w);
        w.addAction(actReset);
        w.addAction(actResetAll);
        w.addAction(actRefresh);
        w.addAction(actResize);
        w.setContextMenuPolicy(Qt::ActionsContextMenu);

        //stsLabel.setText("DL");
        //dbg.stsLayout.addWidget(&stsLabel);

        some_dl_miss=0;
    }

    void resize()
    {
      for(int i=0; i<12; ++i)
        w.resizeColumnToContents(i);
    }

    void fillMetrics(metricRegistered* m, QTreeWidgetItem* p)
    {
       static int level;
       for(; m; m=m->next())
       {
         QTreeWidgetItem* item=new QTreeWidgetItem(p);
         item->setText(0, m->name());
         item->setData(0, Qt::UserRole, /*QVariant((unsigned int)m)*/(unsigned int)m);
         item->setData(0, Qt::UserRole+1, 0);
         item->setToolTip(0,
                          QString("Deadlines: %1, %2").arg(m->time().get_deadline()).arg(m->period().get_deadline())
                          );
         for(int i=1; i<12; ++i)
           item->setData(i, Qt::TextAlignmentRole, Qt::AlignRight);
         //if (p)
         //  p->addChild(item);
         ++level;
         fillMetrics(m->sons(), item);
         --level;
       }
   }

   void resetTimes(QTreeWidgetItem* i)
   {
     if (!i)
       return;
     QVariant v=i->data(0, Qt::UserRole);

     metricRegistered* m=(metricRegistered*)v.toUInt();

     if (m)
     {
       m->reset();
       i->setData(0, Qt::UserRole+1, 0);
       i->setBackground(0, Qt::NoBrush);
     }
     for(int c=0; c<i->childCount(); ++c)
     {
       resetTimes(i->child(c));
     }
   }

   bool updateM(QTreeWidgetItem* i)
   {
       if (!i)
           return false;

       unsigned long miss=0;

       QVariant v=i->data(0, Qt::UserRole);

       metricRegistered* m=(metricRegistered*)v.toUInt();

       if (m)
       {
           i->setData(1, Qt::DisplayRole, (unsigned int)m->samples());

           miss=m->time().dl_miss() + m->period().dl_miss();

           if (miss)
           {
               //some_dl_miss=true;
               unsigned long n=i->data(0, Qt::UserRole+1).toInt();
               if (n==miss)
               {
                   i->setData(0, Qt::BackgroundRole, QBrush(Qt::yellow));
                   if (some_dl_miss<3)
                     some_dl_miss=2;
               }
               else
               {
                 i->setData(0, Qt::BackgroundRole, QBrush(Qt::red));
                 i->setData(0, Qt::UserRole+1, (unsigned int)miss);
                 some_dl_miss=3;
               }
           }


           i->setData(2, Qt::DisplayRole, m->time().last_us());
           i->setData(3, Qt::DisplayRole, m->time().avg_us());
           i->setData(4, Qt::DisplayRole, m->time().min_us());
           i->setData(5, Qt::DisplayRole, m->time().max_us());
           i->setData(6, Qt::DisplayRole, (unsigned int)m->time().dl_miss());
           i->setData(7, Qt::DisplayRole, m->period().last_us());
           i->setData(8, Qt::DisplayRole, m->period().avg_us());
           i->setData(9, Qt::DisplayRole, m->period().min_us());
           i->setData(10, Qt::DisplayRole, m->period().max_us());
           i->setData(11, Qt::DisplayRole, (unsigned int)m->period().dl_miss());
       }
       for(int c=0; c<i->childCount(); ++c)
       {
           bool xx=updateM(i->child(c));
           if (xx && miss==0)
           {
             i->setData(0, Qt::BackgroundRole, QBrush(Qt::lightGray));
           }
       }
       return miss!=0;
   }

   void update()
   {
       if (some_dl_miss>2)
         some_dl_miss=2;

       if (1) //!w.isHidden())
       {
         //metricMeasure xx(mShowUpd);
         //blockSignals(true);
         //setUpdatesEnabled(false);
         updateM(w.invisibleRootItem());

         m_update_turn=!m_update_turn;

         //setUpdatesEnabled(true);
         //blockSignals(false);
      }
   }
   bool m_update_turn;
};


AdsDebug::AdsDebug(QObject *parent) :
    QObject(parent)
{
}

AdsDebug& AdsDebug::instance()
{
  return *dbg.instance;
}

bool AdsDebug::createDebugWindows(QMainWindow *parent)
{

  if (dbg.instance)
    return false;

  dbg.instance=new AdsDebug(parent);

  return dbg.instance->create(parent);
}

#if 0
static void log_(QString msg, int lvl=0)
{
  if (!dbg.logW)
  {
    dbg.logW=new QPlainTextEdit;
    dbg.logW->setReadOnly(true);
    dbg.logW->setLineWrapMode(QPlainTextEdit::NoWrap);
    dbg.logW->setMaximumBlockCount(1000);
    dbg.logW->setCenterOnScroll(true);
    dbg.logW->setUndoRedoEnabled(false);

    dbgQtOutput=dbg.logW;
    qInstallMessageHandler(my_qdebug_output);

    qDebug()<<"debug log ready";
    if (!dbg.logErrLabel)
      dbg.logErrLabel=new StsLed("LOG");
  }
  dbg.logW->appendPlainText(msg);
  if (lvl>0)
  {
    //dbg.logW->setStyleSheet("");
  }
  dbg.logW->ensureCursorVisible();
  //dbg.logErrLabel->setBackgroundRole(QPalette::Highlight);
  //dbg.logErrLabel->setForegroundRole(QPalette::HighlightedText);
  //dbg.logErrLabel->setStyleSheet("background-color: red;");
  //dbg.logErrLabel->setLevel(1);

  //dbg.logW->appendPlainText("\n");
  if (lvl)
    dbg.last_log_level=5;
}
#endif

void AdsDebug::log_qmsg(int /*level*/, const char* const msg)
{
  //log_(QString("[%1] %2").arg(level).arg(msg), level);
    qDebug()<<"[ADS]"<<msg;
}

void AdsDebug::log_print(const QString& msg)
{
  //log_(msg);
  qInfo()<<"[ADS]"<<msg;

}


bool AdsDebug::create(QMainWindow *parent)
{

  //dbg.stsLineWidget.setLayout(&dbg.stsLayout);

  dbg.dlLabel=new StsLed("DL");

  dbg.dockMetrics=new QDockWidget("Dbg: Time Metrics", parent);
  dbg.dockMetrics->setObjectName("MonMetricsDockWindow");
  dbg.metricW=new MWidget(dbg.dockMetrics);
  dbg.dockMetrics->setWidget(&(dbg.metricW->w));
  parent->addDockWidget(Qt::BottomDockWidgetArea, dbg.dockMetrics);

  dbg.dockBuffers=new QDockWidget("Dbg: Shared Buffers", parent);
  dbg.dockBuffers->setObjectName("MonBuffersDockWindow");
  dbg.bufferW=new MbWidget(dbg.dockBuffers);
  dbg.dockBuffers->setWidget(dbg.bufferW);
  parent->addDockWidget(Qt::BottomDockWidgetArea, dbg.dockBuffers);

  dbg.dockBuffers->hide();
  dbg.dockMetrics->hide();

  //dbg.dockLog=new QDockWidget("Dbg: Log", parent);
  //dbg.dockLog->setObjectName("MonLogDockWindow");

  //if (!dbg.logW)
  //  log_print(" ");
    //  log_print(QString("Hello fomr Adm Debug Monitor"));
  //dbg.logW->setParent(dbg.dockLog);
  //dbg.dockLog->setWidget(dbg.logW);
  //parent->addDockWidget(Qt::BottomDockWidgetArea, dbg.dockLog);

  if (!dbg.logErrLabel)
  {
    dbg.logErrLabel=new StsLed("LOG");
  }
  //dbg.logErrLabel.setFrameShape(QFrame::Panel);
  //dbg.stsLayout.addWidget(&dbg.logErrLabel);
  //dbg.stsLayout.setContentsMargins(0, 0, 0, 0);

  QStatusBar* sBar=parent->statusBar();
  //sBar->addPermanentWidget(&dbg.metricW->stsLabel);
  sBar->addPermanentWidget(dbg.dlLabel);
  sBar->addPermanentWidget(dbg.logErrLabel);

  //Connection:
  connect(dbg.metricW->actResetAll, SIGNAL(triggered()), SLOT(resetAllTimesMetrics()));
  connect(dbg.metricW->actReset, SIGNAL(triggered()), SLOT(resetSelectedTimesMetrics()));
  connect(dbg.metricW->actResize, SIGNAL(triggered()), SLOT(resizeTimesMetrics()));

  //dbgQtOutput=dbg.logW;
  //qInstallMsgHandler(my_qdebug_output);
  //qDebug()<<"debug log ready";

  return true;
}

void AdsDebug::resizeTimesMetrics()
{
  dbg.metricW->resize();
}

void AdsDebug::resetAllTimesMetrics()
{
  log_print(QString("Reset all time metrics"));
  dbg.metricW->resetTimes(dbg.metricW->w.invisibleRootItem());
}

void AdsDebug::resetSelectedTimesMetrics()
{
  dbg.metricW->resetTimes(dbg.metricW->w.currentItem());
}

void AdsDebug::startMonitoring(unsigned int interval_ms)
{
  dbg.timer_id=dbg.instance->startTimer(interval_ms);
}

void AdsDebug::timerEvent(QTimerEvent *)
{
  dbg.metricW->update();
  dbg.bufferW->update();

  if (dbg.metricW->some_dl_miss)
  {
    dbg.dlLabel->setLevel(dbg.metricW->some_dl_miss);
    //dbg.dlLabel->setBackgroundRole(QPalette::Highlight);
    //dbg.dlLabel->setForegroundRole(QPalette::HighlightedText);
  }
  if (dbg.last_log_level>3)
  {
    dbg.logErrLabel->setLevel(3);
    --dbg.last_log_level;
  }
  else if (dbg.last_log_level>2)
  {
    dbg.logErrLabel->setLevel(2);
    --dbg.last_log_level;
  }

}

AdsDebug::~AdsDebug()
{

}


void AdsDebug::destroyDebugWindow()
{
  qInstallMessageHandler(0);
  if (!dbg.instance)
    return;
  dbg.instance->killTimer(dbg.timer_id);
  //delete dbg.instance;
  //dbg.instance=0;
}
