text: // Copyright (c) 2016-2018 The Cicoin Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include <qt/modaloverlay.h>
  #include <qt/forms/ui_modaloverlay.h>
  
  #include <qt/guiutil.h>
  
  #include <chainparams.h>
  
  #include <QResizeEvent>
  #include <QPropertyAnimation>
  
  ModalOverlay::ModalOverlay(bool enable_wallet, QWidget *parent) :
  QWidget(parent),
  ui(new Ui::ModalOverlay),
  bestHeaderHeight(0),
  bestHeaderDate(QDateTime()),
  layerIsVisible(false),
  userClosed(false)
  {
      ui->setupUi(this);
      connect(ui->closeButton, &QPushButton::clicked, this, &ModalOverlay::closeClicked);
      if (parent) {
          parent->installEventFilter(this);
          raise();
      }
  
      blockProcessTime.clear();
      setVisible(false);
      if (!enable_wallet) {
          ui->infoText->setVisible(false);
          ui->infoTextStrong->setText(tr("Cicoin Core is currently syncing.  It will download headers and blocks from peers and validate them until reaching the tip of the block chain."));
      }
  }
  
  ModalOverlay::~ModalOverlay()
  {
      delete ui;
  }
  
  bool ModalOverlay::eventFilter(QObject * obj, QEvent * ev) {
      if (obj == parent()) {
          if (ev->type() == QEvent::Resize) {
              QResizeEvent * rev = static_cast<QResizeEvent*>(ev);
              resize(rev->size());
              if (!layerIsVisible)
                  setGeometry(0, height(), width(), height());
  
          }
          else if (ev->type() == QEvent::ChildAdded) {
              raise();
          }
      }
      return QWidget::eventFilter(obj, ev);
  }
  
  //! Tracks parent widget changes
  bool ModalOverlay::event(QEvent* ev) {
      if (ev->type() == QEvent::ParentAboutToChange) {
          if (parent()) parent()->removeEventFilter(this);
      }
      else if (ev->type() == QEvent::ParentChange) {
          if (parent()) {
              parent()->installEventFilter(this);
              raise();
          }
      }
      return QWidget::event(ev);
  }
  
  void ModalOverlay::setKnownBestHeight(int count, const QDateTime& blockDate)
  {
      if (count > bestHeaderHeight) {
          bestHeaderHeight = count;
          bestHeaderDate = blockDate;
          UpdateHeaderSyncLabel();
      }
  }
  
  void ModalOverlay::tipUpdate(int count, const QDateTime& blockDate, double nVerificationProgress)
  {
      QDateTime currentDate = QDateTime::currentDateTime();
  
      // keep a vector of samples of verification progress at height
      blockProcessTime.push_front(qMakePair(currentDate.toMSecsSinceEpoch(), nVerificationProgress));
  
      // show progress speed if we have more than one sample
      if (blockProcessTime.size() >= 2) {
          double progressDelta = 0;
          double progressPerHour = 0;
          qint64 timeDelta = 0;
          qint64 remainingMSecs = 0;
          double remainingProgress = 1.0 - nVerificationProgress;
          for (int i = 1; i < blockProcessTime.size(); i++) {
              QPair<qint64, double> sample = blockProcessTime[i];
  
              // take first sample after 500 seconds or last available one
              if (sample.first < (currentDate.toMSecsSinceEpoch() - 500 * 1000) || i == blockProcessTime.size() - 1) {
                  progressDelta = blockProcessTime[0].second - sample.second;
                  timeDelta = blockProcessTime[0].first - sample.first;
                  progressPerHour = progressDelta / (double) timeDelta * 1000 * 3600;
                  remainingMSecs = (progressDelta > 0) ? remainingProgress / progressDelta * timeDelta : -1;
                  break;
              }
          }
          // show progress increase per hour
          ui->progressIncreasePerH->setText(QString::number(progressPerHour * 100, 'f', 2)+"%");
  
          // show expected remaining time
          if(remainingMSecs >= 0) {
              ui->expectedTimeLeft->setText(GUIUtil::formatNiceTimeOffset(remainingMSecs / 1000.0));
          } else {
              ui->expectedTimeLeft->setText(QObject::tr("unknown"));
          }
  
          static const int MAX_SAMPLES = 5000;
          if (blockProcessTime.count() > MAX_SAMPLES) {
              blockProcessTime.remove(MAX_SAMPLES, blockProcessTime.count() - MAX_SAMPLES);
          }
      }
  
      // show the last block date
      ui->newestBlockDate->setText(blockDate.toString());
  
      // show the percentage done according to nVerificationProgress
      ui->percentageProgress->setText(QString::number(nVerificationProgress*100, 'f', 2)+"%");
      ui->progressBar->setValue(nVerificationProgress*100);
  
      if (!bestHeaderDate.isValid())
          // not syncing
          return;
  
      // estimate the number of headers left based on nPowTargetSpacing
      // and check if the gui is not aware of the best header (happens rarely)
      int estimateNumHeadersLeft = bestHeaderDate.secsTo(currentDate) / Params().GetConsensus().nPowTargetSpacing;
      bool hasBestHeader = bestHeaderHeight >= count;
  
      // show remaining number of blocks
      if (estimateNumHeadersLeft < HEADER_HEIGHT_DELTA_SYNC && hasBestHeader) {
          ui->numberOfBlocksLeft->setText(QString::number(bestHeaderHeight - count));
      } else {
          UpdateHeaderSyncLabel();
          ui->expectedTimeLeft->setText(tr("Unknown..."));
      }
  }
  
  void ModalOverlay::UpdateHeaderSyncLabel() {
      int est_headers_left = bestHeaderDate.secsTo(QDateTime::currentDateTime()) / Params().GetConsensus().nPowTargetSpacing;
      ui->numberOfBlocksLeft->setText(tr("Unknown. Syncing Headers (%1, %2%)...").arg(bestHeaderHeight).arg(QString::number(100.0 / (bestHeaderHeight + est_headers_left) * bestHeaderHeight, 'f', 1)));
  }
  
  void ModalOverlay::toggleVisibility()
  {
      showHide(layerIsVisible, true);
      if (!layerIsVisible)
          userClosed = true;
  }
  
  void ModalOverlay::showHide(bool hide, bool userRequested)
  {
      if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested))
          return;
  
      if (!isVisible() && !hide)
          setVisible(true);
  
      setGeometry(0, hide ? 0 : height(), width(), height());
  
      QPropertyAnimation* animation = new QPropertyAnimation(this, "pos");
      animation->setDuration(300);
      animation->setStartValue(QPoint(0, hide ? 0 : this->height()));
      animation->setEndValue(QPoint(0, hide ? this->height() : 0));
      animation->setEasingCurve(QEasingCurve::OutQuad);
      animation->start(QAbstractAnimation::DeleteWhenStopped);
      layerIsVisible = !hide;
  }
  
  void ModalOverlay::closeClicked()
  {
      showHide(true);
      userClosed = true;
  }
  