text: #include "masternodelist.h"
  #include "ui_masternodelist.h"
  
  #include "activemasternode.h"
  #include "clientmodel.h"
  #include "init.h"
  #include "guiutil.h"
  #include "masternode-sync.h"
  #include "masternodeconfig.h"
  #include "masternodeman.h"
  #include "sync.h"
  #include "wallet/wallet.h"
  #include "walletmodel.h"
  
  #include <georeward.h>
  
  #include <QTimer>
  #include <QMessageBox>
  #include <QIcon>
  
  int GetOffsetFromUtc()
  {
  #if QT_VERSION < 0x050200
      const QDateTime dateTime1 = QDateTime::currentDateTime();
      const QDateTime dateTime2 = QDateTime(dateTime1.date(), dateTime1.time(), Qt::UTC);
      return dateTime1.secsTo(dateTime2);
  #else
      return QDateTime::currentDateTime().offsetFromUtc();
  #endif
  }
  
  MasternodeList::MasternodeList(const PlatformStyle *platformStyle, QWidget *parent) :
      QWidget(parent),
      ui(new Ui::MasternodeList),
      clientModel(0),
      walletModel(0)
  {
      ui->setupUi(this);
  
      ui->startButton->setEnabled(false);
  
      int columnAliasWidth = 100;
      int columnAddressWidth = 180;
      int columnProtocolWidth = 60;
      int columnStatusWidth = 80;
      int columnActiveWidth = 110;
      int columnLastSeenWidth = 130;
  
      ui->tableWidgetMyMasternodes->setColumnWidth(0, columnAliasWidth);
      ui->tableWidgetMyMasternodes->setColumnWidth(1, columnAddressWidth);
      ui->tableWidgetMyMasternodes->setColumnWidth(2, columnProtocolWidth);
      ui->tableWidgetMyMasternodes->setColumnWidth(3, columnStatusWidth);
      ui->tableWidgetMyMasternodes->setColumnWidth(4, columnActiveWidth);
      ui->tableWidgetMyMasternodes->setColumnWidth(5, columnLastSeenWidth);
  
      ui->tableWidgetMasternodes->setColumnWidth(0, columnAddressWidth);
      ui->tableWidgetMasternodes->setColumnWidth(1, 60);
      ui->tableWidgetMasternodes->setColumnWidth(2, 60);
      ui->tableWidgetMasternodes->setColumnWidth(3, columnProtocolWidth);
      ui->tableWidgetMasternodes->setColumnWidth(4, columnStatusWidth);
      ui->tableWidgetMasternodes->setColumnWidth(5, columnActiveWidth);
      ui->tableWidgetMasternodes->setColumnWidth(6, columnLastSeenWidth);
  
      ui->tableWidgetMyMasternodes->setContextMenuPolicy(Qt::CustomContextMenu);
  
      QAction *startAliasAction = new QAction(tr("Start alias"), this);
      contextMenu = new QMenu();
      contextMenu->addAction(startAliasAction);
      connect(ui->tableWidgetMyMasternodes, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenu(const QPoint&)));
      connect(startAliasAction, SIGNAL(triggered()), this, SLOT(on_startButton_clicked()));
  
      timer = new QTimer(this);
      connect(timer, SIGNAL(timeout()), this, SLOT(updateNodeList()));
      connect(timer, SIGNAL(timeout()), this, SLOT(updateMyNodeList()));
      timer->start(1000);
  
      fFilterUpdated = false;
      nTimeFilterUpdated = GetTime();
      updateNodeList();
  }
  
  MasternodeList::~MasternodeList()
  {
      delete ui;
  }
  
  void MasternodeList::setClientModel(ClientModel *model)
  {
      this->clientModel = model;
      if(model) {
          // try to update list when masternode count changes
          connect(clientModel, SIGNAL(strMasternodesChanged(QString)), this, SLOT(updateNodeList()));
      }
  }
  
  void MasternodeList::setWalletModel(WalletModel *model)
  {
      this->walletModel = model;
  }
  
  void MasternodeList::showContextMenu(const QPoint &point)
  {
      QTableWidgetItem *item = ui->tableWidgetMyMasternodes->itemAt(point);
      if(item) contextMenu->exec(QCursor::pos());
  }
  
  void MasternodeList::StartAlias(std::string strAlias)
  {
      std::string strStatusHtml;
      strStatusHtml += "<center>Alias: " + strAlias;
  
      BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
          if(mne.getAlias() == strAlias) {
              std::string strError;
              CMasternodeBroadcast mnb;
  
              bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
  
              if(fSuccess) {
                  strStatusHtml += "<br>Successfully started masternode.";
                  mnodeman.UpdateMasternodeList(mnb, *g_connman);
                  mnb.Relay(*g_connman);
                  mnodeman.NotifyMasternodeUpdates(*g_connman);
              } else {
                  strStatusHtml += "<br>Failed to start masternode.<br>Error: " + strError;
              }
              break;
          }
      }
      strStatusHtml += "</center>";
  
      QMessageBox msg;
      msg.setText(QString::fromStdString(strStatusHtml));
      msg.exec();
  
      updateMyNodeList(true);
  }
  
  void MasternodeList::StartAll(std::string strCommand)
  {
      int nCountSuccessful = 0;
      int nCountFailed = 0;
      std::string strFailedHtml;
  
      BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
          std::string strError;
          CMasternodeBroadcast mnb;
  
          int32_t nOutputIndex = 0;
          if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) {
              continue;
          }
  
          COutPoint outpoint = COutPoint(uint256S(mne.getTxHash()), nOutputIndex);
  
          if(strCommand == "start-missing" && mnodeman.Has(outpoint)) continue;
  
          bool fSuccess = CMasternodeBroadcast::Create(mne.getIp(), mne.getPrivKey(), mne.getTxHash(), mne.getOutputIndex(), strError, mnb);
  
          if(fSuccess) {
              nCountSuccessful++;
              mnodeman.UpdateMasternodeList(mnb, *g_connman);
              mnb.Relay(*g_connman);
              mnodeman.NotifyMasternodeUpdates(*g_connman);
          } else {
              nCountFailed++;
              strFailedHtml += "\nFailed to start " + mne.getAlias() + ". Error: " + strError;
          }
      }
      pwalletMain->Lock();
  
      std::string returnObj;
      returnObj = strprintf("Successfully started %d masternodes, failed to start %d, total %d", nCountSuccessful, nCountFailed, nCountFailed + nCountSuccessful);
      if (nCountFailed > 0) {
          returnObj += strFailedHtml;
      }
  
      QMessageBox msg;
      msg.setText(QString::fromStdString(returnObj));
      msg.exec();
  
      updateMyNodeList(true);
  }
  
  void MasternodeList::updateMyMasternodeInfo(QString strAlias, QString strAddr, const COutPoint& outpoint)
  {
      bool fOldRowFound = false;
      int nNewRow = 0;
  
      for(int i = 0; i < ui->tableWidgetMyMasternodes->rowCount(); i++) {
          if(ui->tableWidgetMyMasternodes->item(i, 0)->text() == strAlias) {
              fOldRowFound = true;
              nNewRow = i;
              break;
          }
      }
  
      if(nNewRow == 0 && !fOldRowFound) {
          nNewRow = ui->tableWidgetMyMasternodes->rowCount();
          ui->tableWidgetMyMasternodes->insertRow(nNewRow);
      }
  
      masternode_info_t infoMn;
      bool fFound = mnodeman.GetMasternodeInfo(outpoint, infoMn);
  
      QTableWidgetItem *aliasItem = new QTableWidgetItem(strAlias);
      QTableWidgetItem *addrItem = new QTableWidgetItem(fFound ? QString::fromStdString(infoMn.addr.ToString()) : strAddr);
      QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(fFound ? infoMn.nProtocolVersion : -1));
      QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(fFound ? CMasternode::StateToString(infoMn.nActiveState) : "MISSING"));
      QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(fFound ? (infoMn.nTimeLastPing - infoMn.sigTime) : 0)));
      QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M",
                                                                                                     fFound ? infoMn.nTimeLastPing + GetOffsetFromUtc() : 0)));
      QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(fFound ? CBitcoinAddress(infoMn.pubKeyCollateralAddress.GetID()).ToString() : ""));
  
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 0, aliasItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 1, addrItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 2, protocolItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 3, statusItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 4, activeSecondsItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 5, lastSeenItem);
      ui->tableWidgetMyMasternodes->setItem(nNewRow, 6, pubkeyItem);
  }
  
  void MasternodeList::updateMyNodeList(bool fForce)
  {
      TRY_LOCK(cs_mymnlist, fLockAcquired);
      if(!fLockAcquired) {
          return;
      }
      static int64_t nTimeMyListUpdated = 0;
  
      // automatically update my masternode list only once in MY_MASTERNODELIST_UPDATE_SECONDS seconds,
      // this update still can be triggered manually at any time via button click
      int64_t nSecondsTillUpdate = nTimeMyListUpdated + MY_MASTERNODELIST_UPDATE_SECONDS - GetTime();
      ui->secondsLabel->setText(QString::number(nSecondsTillUpdate));
  
      if(nSecondsTillUpdate > 0 && !fForce) return;
      nTimeMyListUpdated = GetTime();
  
      ui->tableWidgetMasternodes->setSortingEnabled(false);
      BOOST_FOREACH(CMasternodeConfig::CMasternodeEntry mne, masternodeConfig.getEntries()) {
          int32_t nOutputIndex = 0;
          if(!ParseInt32(mne.getOutputIndex(), &nOutputIndex)) {
              continue;
          }
  
          updateMyMasternodeInfo(QString::fromStdString(mne.getAlias()), QString::fromStdString(mne.getIp()), COutPoint(uint256S(mne.getTxHash()), nOutputIndex));
      }
      ui->tableWidgetMasternodes->setSortingEnabled(true);
  
      // reset "timer"
      ui->secondsLabel->setText("0");
  }
  
  void MasternodeList::updateNodeList()
  {
      TRY_LOCK(cs_mnlist, fLockAcquired);
      if(!fLockAcquired) {
          return;
      }
  
      static int64_t nTimeListUpdated = GetTime();
  
      // to prevent high cpu usage update only once in MASTERNODELIST_UPDATE_SECONDS seconds
      // or MASTERNODELIST_FILTER_COOLDOWN_SECONDS seconds after filter was last changed
      int64_t nSecondsToWait = fFilterUpdated
                              ? nTimeFilterUpdated - GetTime() + MASTERNODELIST_FILTER_COOLDOWN_SECONDS
                              : nTimeListUpdated - GetTime() + MASTERNODELIST_UPDATE_SECONDS;
  
      if(fFilterUpdated) ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", nSecondsToWait)));
      if(nSecondsToWait > 0) return;
  
      nTimeListUpdated = GetTime();
      fFilterUpdated = false;
  
      QString strToFilter;
      ui->countLabel->setText("Updating...");
      ui->tableWidgetMasternodes->setSortingEnabled(false);
      ui->tableWidgetMasternodes->clearContents();
      ui->tableWidgetMasternodes->setRowCount(0);
      std::map<COutPoint, CMasternode> mapMasternodes = mnodeman.GetFullMasternodeMap();
      int offsetFromUtc = GetOffsetFromUtc();
  
      for(auto& mnpair : mapMasternodes)
      {
          CMasternode mn = mnpair.second;
          CGEOReward geor;
          // populate list
          // Address, Protocol, Status, Active Seconds, Last Seen, Pub Key
          std::vector<std::string> mnip;
          boost::split(mnip, (const std::string) mn.addr.ToString(), boost::is_any_of(":"));
          if(mnip.size() != 2)
              return;
  
          char *isluso=(char *) "No";
          if (geor.isLUSO(mnip[0].c_str(),(char *) mn.country.c_str()))isluso=(char *) "Yes";
  
          std::string country(mn.country.c_str());
          std::transform(country.begin(), country.end(),country.begin(), ::tolower);
          QString countryqs=QString::fromStdString(":/flags/" + country + ".png");
  
          QTableWidgetItem *addressItem = new QTableWidgetItem(QString::fromStdString(mn.addr.ToString()));
          //QTableWidgetItem *countryItem = new QTableWidgetItem(QIcon(countryqs),QString::fromStdString(mn.country));
          QTableWidgetItem *countryItem = new QTableWidgetItem(QString::fromStdString(mn.country));
          QTableWidgetItem *lusoItem = new QTableWidgetItem(QString::fromStdString(isluso));
          QTableWidgetItem *protocolItem = new QTableWidgetItem(QString::number(mn.nProtocolVersion));
          QTableWidgetItem *statusItem = new QTableWidgetItem(QString::fromStdString(mn.GetStatus()));
          QTableWidgetItem *activeSecondsItem = new QTableWidgetItem(QString::fromStdString(DurationToDHMS(mn.lastPing.sigTime - mn.sigTime)));
          QTableWidgetItem *lastSeenItem = new QTableWidgetItem(QString::fromStdString(DateTimeStrFormat("%Y-%m-%d %H:%M", mn.lastPing.sigTime + offsetFromUtc)));
          QTableWidgetItem *pubkeyItem = new QTableWidgetItem(QString::fromStdString(CBitcoinAddress(mn.pubKeyCollateralAddress.GetID()).ToString()));
  
          if (strCurrentFilter != "")
          {
              strToFilter =   addressItem->text() + " " +
                              countryItem->text() + " " +
                              lusoItem->text() + " " +
                              protocolItem->text() + " " +
                              statusItem->text() + " " +
                              activeSecondsItem->text() + " " +
                              lastSeenItem->text() + " " +
                              pubkeyItem->text();
              if (!strToFilter.contains(strCurrentFilter)) continue;
          }
  /*
          QPixmap pix(countryqs);
          QPixmap resPix = pix.scaled(22,14, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
          QLabel *lblTest = new QLabel;
          lblTest->setPixmap(resPix);
          lblTest->setAlignment(Qt::AlignCenter);
  */
          ui->tableWidgetMasternodes->insertRow(0);
          ui->tableWidgetMasternodes->setItem(0, 0, addressItem);
  //        ui->tableWidgetMasternodes->setCellWidget(0, 1, lblTest);
          ui->tableWidgetMasternodes->setItem(0, 1, countryItem);
          ui->tableWidgetMasternodes->setItem(0, 2, lusoItem);
          ui->tableWidgetMasternodes->setItem(0, 3, protocolItem);
          ui->tableWidgetMasternodes->setItem(0, 4, statusItem);
          ui->tableWidgetMasternodes->setItem(0, 5, activeSecondsItem);
          ui->tableWidgetMasternodes->setItem(0, 6, lastSeenItem);
          ui->tableWidgetMasternodes->setItem(0, 7, pubkeyItem);
      }
  
      ui->countLabel->setText(QString::number(ui->tableWidgetMasternodes->rowCount()));
      ui->tableWidgetMasternodes->setSortingEnabled(true);
  }
  
  void MasternodeList::on_filterLineEdit_textChanged(const QString &strFilterIn)
  {
      strCurrentFilter = strFilterIn;
      nTimeFilterUpdated = GetTime();
      fFilterUpdated = true;
      ui->countLabel->setText(QString::fromStdString(strprintf("Please wait... %d", MASTERNODELIST_FILTER_COOLDOWN_SECONDS)));
  }
  
  void MasternodeList::on_startButton_clicked()
  {
      std::string strAlias;
      {
          LOCK(cs_mymnlist);
          // Find selected node alias
          QItemSelectionModel* selectionModel = ui->tableWidgetMyMasternodes->selectionModel();
          QModelIndexList selected = selectionModel->selectedRows();
  
          if(selected.count() == 0) return;
  
          QModelIndex index = selected.at(0);
          int nSelectedRow = index.row();
          strAlias = ui->tableWidgetMyMasternodes->item(nSelectedRow, 0)->text().toStdString();
      }
  
      // Display message box
      QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm masternode start"),
          tr("Are you sure you want to start masternode %1?").arg(QString::fromStdString(strAlias)),
          QMessageBox::Yes | QMessageBox::Cancel,
          QMessageBox::Cancel);
  
      if(retval != QMessageBox::Yes) return;
  
      WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus();
  
      if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) {
          WalletModel::UnlockContext ctx(walletModel->requestUnlock());
  
          if(!ctx.isValid()) return; // Unlock wallet was cancelled
  
          StartAlias(strAlias);
          return;
      }
  
      StartAlias(strAlias);
  }
  
  void MasternodeList::on_startAllButton_clicked()
  {
      // Display message box
      QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm all masternodes start"),
          tr("Are you sure you want to start ALL masternodes?"),
          QMessageBox::Yes | QMessageBox::Cancel,
          QMessageBox::Cancel);
  
      if(retval != QMessageBox::Yes) return;
  
      WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus();
  
      if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) {
          WalletModel::UnlockContext ctx(walletModel->requestUnlock());
  
          if(!ctx.isValid()) return; // Unlock wallet was cancelled
  
          StartAll();
          return;
      }
  
      StartAll();
  }
  
  void MasternodeList::on_startMissingButton_clicked()
  {
  
      if(!masternodeSync.IsMasternodeListSynced()) {
          QMessageBox::critical(this, tr("Command is not available right now"),
              tr("You can't use this command until masternode list is synced"));
          return;
      }
  
      // Display message box
      QMessageBox::StandardButton retval = QMessageBox::question(this,
          tr("Confirm missing masternodes start"),
          tr("Are you sure you want to start MISSING masternodes?"),
          QMessageBox::Yes | QMessageBox::Cancel,
          QMessageBox::Cancel);
  
      if(retval != QMessageBox::Yes) return;
  
      WalletModel::EncryptionStatus encStatus = walletModel->getEncryptionStatus();
  
      if(encStatus == walletModel->Locked || encStatus == walletModel->UnlockedForMixingOnly) {
          WalletModel::UnlockContext ctx(walletModel->requestUnlock());
  
          if(!ctx.isValid()) return; // Unlock wallet was cancelled
  
          StartAll("start-missing");
          return;
      }
  
      StartAll("start-missing");
  }
  
  void MasternodeList::on_tableWidgetMyMasternodes_itemSelectionChanged()
  {
      if(ui->tableWidgetMyMasternodes->selectedItems().count() > 0) {
          ui->startButton->setEnabled(true);
      }
  }
  
  void MasternodeList::on_UpdateButton_clicked()
  {
      updateMyNodeList(true);
  }
  