text: // Copyright (c) 2011-2013 The CICcoin developers
  // Distributed under the MIT/X11 software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "trafficgraphwidget.h"
  #include "clientmodel.h"
  
  #include <QPainter>
  #include <QColor>
  #include <QTimer>
  
  #include <cmath>
  
  #define DESIRED_SAMPLES         800
  
  #define XMARGIN                 10
  #define YMARGIN                 10
  
  TrafficGraphWidget::TrafficGraphWidget(QWidget *parent) :
      QWidget(parent),
      timer(0),
      fMax(0.0f),
      nMins(0),
      vSamplesIn(),
      vSamplesOut(),
      nLastBytesIn(0),
      nLastBytesOut(0),
      clientModel(0)
  {
      timer = new QTimer(this);
      connect(timer, SIGNAL(timeout()), SLOT(updateRates()));
  }
  
  void TrafficGraphWidget::setClientModel(ClientModel *model)
  {
      clientModel = model;
      if(model) {
          nLastBytesIn = model->getTotalBytesRecv();
          nLastBytesOut = model->getTotalBytesSent();
      }
  }
  
  int TrafficGraphWidget::getGraphRangeMins() const
  {
      return nMins;
  }
  
  void TrafficGraphWidget::paintPath(QPainterPath &path, QQueue<float> &samples)
  {
      int h = height() - YMARGIN * 2, w = width() - XMARGIN * 2;
      int sampleCount = samples.size(), x = XMARGIN + w, y;
      if(sampleCount > 0) {
          path.moveTo(x, YMARGIN + h);
          for(int i = 0; i < sampleCount; ++i) {
              x = XMARGIN + w - w * i / DESIRED_SAMPLES;
              y = YMARGIN + h - (int)(h * samples.at(i) / fMax);
              path.lineTo(x, y);
          }
          path.lineTo(x, YMARGIN + h);
      }
  }
  
  void TrafficGraphWidget::paintEvent(QPaintEvent *)
  {
      QPainter painter(this);
      painter.fillRect(rect(), Qt::black);
  
      if(fMax <= 0.0f) return;
  
      QColor axisCol(Qt::gray);
      int h = height() - YMARGIN * 2;
      painter.setPen(axisCol);
      painter.drawLine(XMARGIN, YMARGIN + h, width() - XMARGIN, YMARGIN + h);
  
      // decide what order of magnitude we are
      int base = floor(log10(fMax));
      float val = pow(10.0f, base);
  
      const QString units = tr("KB/s");
      // draw lines
      painter.setPen(axisCol);
      painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units));
      for(float y = val; y < fMax; y += val) {
          int yy = YMARGIN + h - h * y / fMax;
          painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
      }
      // if we drew 3 or fewer lines, break them up at the next lower order of magnitude
      if(fMax / val <= 3.0f) {
          axisCol = axisCol.darker();
          val = pow(10.0f, base - 1);
          painter.setPen(axisCol);
          painter.drawText(XMARGIN, YMARGIN + h - h * val / fMax, QString("%1 %2").arg(val).arg(units));
          int count = 1;
          for(float y = val; y < fMax; y += val, count++) {
              // don't overwrite lines drawn above
              if(count % 10 == 0)
                  continue;
              int yy = YMARGIN + h - h * y / fMax;
              painter.drawLine(XMARGIN, yy, width() - XMARGIN, yy);
          }
      }
  
      if(!vSamplesIn.empty()) {
          QPainterPath p;
          paintPath(p, vSamplesIn);
          painter.fillPath(p, QColor(0, 255, 0, 128));
          painter.setPen(Qt::green);
          painter.drawPath(p);
      }
      if(!vSamplesOut.empty()) {
          QPainterPath p;
          paintPath(p, vSamplesOut);
          painter.fillPath(p, QColor(255, 0, 0, 128));
          painter.setPen(Qt::red);
          painter.drawPath(p);
      }
  }
  
  void TrafficGraphWidget::updateRates()
  {
      if(!clientModel) return;
  
      quint64 bytesIn = clientModel->getTotalBytesRecv(),
              bytesOut = clientModel->getTotalBytesSent();
      float inRate = (bytesIn - nLastBytesIn) / 1024.0f * 1000 / timer->interval();
      float outRate = (bytesOut - nLastBytesOut) / 1024.0f * 1000 / timer->interval();
      vSamplesIn.push_front(inRate);
      vSamplesOut.push_front(outRate);
      nLastBytesIn = bytesIn;
      nLastBytesOut = bytesOut;
  
      while(vSamplesIn.size() > DESIRED_SAMPLES) {
          vSamplesIn.pop_back();
      }
      while(vSamplesOut.size() > DESIRED_SAMPLES) {
          vSamplesOut.pop_back();
      }
  
      float tmax = 0.0f;
      foreach(float f, vSamplesIn) {
          if(f > tmax) tmax = f;
      }
      foreach(float f, vSamplesOut) {
          if(f > tmax) tmax = f;
      }
      fMax = tmax;
      update();
  }
  
  void TrafficGraphWidget::setGraphRangeMins(int mins)
  {
      nMins = mins;
      int msecsPerSample = nMins * 60 * 1000 / DESIRED_SAMPLES;
      timer->stop();
      timer->setInterval(msecsPerSample);
  
      clear();
  }
  
  void TrafficGraphWidget::clear()
  {
      timer->stop();
  
      vSamplesOut.clear();
      vSamplesIn.clear();
      fMax = 0.0f;
  
      if(clientModel) {
          nLastBytesIn = clientModel->getTotalBytesRecv();
          nLastBytesOut = clientModel->getTotalBytesSent();
      }
      timer->start();
  }
  