text: // Copyright (c) 2011-2015 The Bitcoin Core developers
  // Copyright (c) 2014-2017 The Dash Core developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "sendcoinsdialog.h"
  #include "ui_sendcoinsdialog.h"
  
  #include "addresstablemodel.h"
  #include "bitcoinunits.h"
  #include "clientmodel.h"
  #include "coincontroldialog.h"
  #include "guiutil.h"
  #include "optionsmodel.h"
  #include "platformstyle.h"
  #include "sendcoinsentry.h"
  #include "walletmodel.h"
  
  #include "base58.h"
  #include "wallet/coincontrol.h"
  #include "validation.h" // mempool and minRelayTxFee
  #include "ui_interface.h"
  #include "txmempool.h"
  #include "wallet/wallet.h"
  
  #include "privatesend.h"
  
  #include <QFontMetrics>
  #include <QMessageBox>
  #include <QScrollBar>
  #include <QSettings>
  #include <QTextDocument>
  #include <QTimer>
  
  #define SEND_CONFIRM_DELAY   3
  
  SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
      QDialog(parent),
      ui(new Ui::SendCoinsDialog),
      clientModel(0),
      model(0),
      fNewRecipientAllowed(true),
      fFeeMinimized(true),
      platformStyle(_platformStyle)
  {
      ui->setupUi(this);
      QString theme = GUIUtil::getThemeName();
  
      if (!_platformStyle->getImagesOnButtons()) {
          ui->addButton->setIcon(QIcon());
          ui->clearButton->setIcon(QIcon());
          ui->sendButton->setIcon(QIcon());
      } else {
          ui->addButton->setIcon(QIcon(":/icons/" + theme + "/add"));
          ui->clearButton->setIcon(QIcon(":/icons/" + theme + "/remove"));
          ui->sendButton->setIcon(QIcon(":/icons/" + theme + "/send"));
      }
  
      GUIUtil::setupAddressWidget(ui->lineEditCoinControlChange, this);
  
      addEntry();
  
      connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
      connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
  
      // Coin Control
      connect(ui->pushButtonCoinControl, SIGNAL(clicked()), this, SLOT(coinControlButtonClicked()));
      connect(ui->checkBoxCoinControlChange, SIGNAL(stateChanged(int)), this, SLOT(coinControlChangeChecked(int)));
      connect(ui->lineEditCoinControlChange, SIGNAL(textEdited(const QString &)), this, SLOT(coinControlChangeEdited(const QString &)));
  
      // Dash specific
      QSettings settings;
      if (!settings.contains("bUseDarkSend"))
          settings.setValue("bUseDarkSend", false);
      if (!settings.contains("bUseInstantX"))
          settings.setValue("bUseInstantX", false);
  
      bool fUsePrivateSend = settings.value("bUseDarkSend").toBool();
      bool fUseInstantSend = settings.value("bUseInstantX").toBool();
      if(fLiteMode) {
          ui->checkUsePrivateSend->setChecked(false);
          ui->checkUsePrivateSend->setVisible(false);
          ui->checkUseInstantSend->setVisible(false);
          CoinControlDialog::coinControl->fUsePrivateSend = false;
          CoinControlDialog::coinControl->fUseInstantSend = false;
      }
      else{
          ui->checkUsePrivateSend->setChecked(fUsePrivateSend);
          ui->checkUseInstantSend->setChecked(fUseInstantSend);
          CoinControlDialog::coinControl->fUsePrivateSend = fUsePrivateSend;
          CoinControlDialog::coinControl->fUseInstantSend = fUseInstantSend;
      }
  
      connect(ui->checkUsePrivateSend, SIGNAL(stateChanged ( int )), this, SLOT(updateDisplayUnit()));
      connect(ui->checkUseInstantSend, SIGNAL(stateChanged ( int )), this, SLOT(updateInstantSend()));
  
      // Coin Control: clipboard actions
      QAction *clipboardQuantityAction = new QAction(tr("Copy quantity"), this);
      QAction *clipboardAmountAction = new QAction(tr("Copy amount"), this);
      QAction *clipboardFeeAction = new QAction(tr("Copy fee"), this);
      QAction *clipboardAfterFeeAction = new QAction(tr("Copy after fee"), this);
      QAction *clipboardBytesAction = new QAction(tr("Copy bytes"), this);
      QAction *clipboardLowOutputAction = new QAction(tr("Copy dust"), this);
      QAction *clipboardChangeAction = new QAction(tr("Copy change"), this);
      connect(clipboardQuantityAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardQuantity()));
      connect(clipboardAmountAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAmount()));
      connect(clipboardFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardFee()));
      connect(clipboardAfterFeeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardAfterFee()));
      connect(clipboardBytesAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardBytes()));
      connect(clipboardLowOutputAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardLowOutput()));
      connect(clipboardChangeAction, SIGNAL(triggered()), this, SLOT(coinControlClipboardChange()));
      ui->labelCoinControlQuantity->addAction(clipboardQuantityAction);
      ui->labelCoinControlAmount->addAction(clipboardAmountAction);
      ui->labelCoinControlFee->addAction(clipboardFeeAction);
      ui->labelCoinControlAfterFee->addAction(clipboardAfterFeeAction);
      ui->labelCoinControlBytes->addAction(clipboardBytesAction);
      ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
      ui->labelCoinControlChange->addAction(clipboardChangeAction);
  
      // init transaction fee section
      if (!settings.contains("fFeeSectionMinimized"))
          settings.setValue("fFeeSectionMinimized", true);
      if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
          settings.setValue("nFeeRadio", 1); // custom
      if (!settings.contains("nFeeRadio"))
          settings.setValue("nFeeRadio", 0); // recommended
      if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
          settings.setValue("nCustomFeeRadio", 1); // total at least
      if (!settings.contains("nCustomFeeRadio"))
          settings.setValue("nCustomFeeRadio", 0); // per kilobyte
      if (!settings.contains("nSmartFeeSliderPosition"))
          settings.setValue("nSmartFeeSliderPosition", 0);
      if (!settings.contains("nTransactionFee"))
          settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
      if (!settings.contains("fPayOnlyMinFee"))
          settings.setValue("fPayOnlyMinFee", false);
  
      ui->groupFee->setId(ui->radioSmartFee, 0);
      ui->groupFee->setId(ui->radioCustomFee, 1);
      ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
      ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
      ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
      ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
      ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
      ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
      minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
  }
  
  void SendCoinsDialog::setClientModel(ClientModel *_clientModel)
  {
      this->clientModel = _clientModel;
  
      if (_clientModel) {
          connect(_clientModel, SIGNAL(numBlocksChanged(int,QDateTime,double,bool)), this, SLOT(updateSmartFeeLabel()));
      }
  }
  
  void SendCoinsDialog::setModel(WalletModel *_model)
  {
      this->model = _model;
  
      if(_model && _model->getOptionsModel())
      {
          for(int i = 0; i < ui->entries->count(); ++i)
          {
              SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
              if(entry)
              {
                  entry->setModel(_model);
              }
          }
  
          setBalance(_model->getBalance(), _model->getUnconfirmedBalance(), _model->getImmatureBalance(), _model->getAnonymizedBalance(),
                     _model->getWatchBalance(), _model->getWatchUnconfirmedBalance(), _model->getWatchImmatureBalance());
          connect(_model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
          connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
          updateDisplayUnit();
  
          // Coin Control
          connect(_model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
          connect(_model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
          ui->frameCoinControl->setVisible(_model->getOptionsModel()->getCoinControlFeatures());
          coinControlUpdateLabels();
  
          // fee section
          connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
          connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
          connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
          connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
          connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
          connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
          connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
          connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
          connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
          connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
          connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
          connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
          connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
          connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
          ui->customFee->setSingleStep(CWallet::GetRequiredFee(1000));
          updateFeeSectionControls();
          updateMinFeeLabel();
          updateSmartFeeLabel();
          updateGlobalFeeVariables();
  
          // set the smartfee-sliders default value (wallets default conf.target or last stored value)
          QSettings settings;
          if (settings.value("nSmartFeeSliderPosition").toInt() == 0)
              ui->sliderSmartFee->setValue(ui->sliderSmartFee->maximum() - model->getDefaultConfirmTarget() + 2);
          else
              ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
      }
  }
  
  SendCoinsDialog::~SendCoinsDialog()
  {
      QSettings settings;
      settings.setValue("fFeeSectionMinimized", fFeeMinimized);
      settings.setValue("nFeeRadio", ui->groupFee->checkedId());
      settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
      settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
      settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
      settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
  
      delete ui;
  }
  
  void SendCoinsDialog::on_sendButton_clicked()
  {
      if(!model || !model->getOptionsModel())
          return;
  
      QList<SendCoinsRecipient> recipients;
      bool valid = true;
  
      for(int i = 0; i < ui->entries->count(); ++i)
      {
          SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
          if(entry)
          {
              if(entry->validate())
              {
                  recipients.append(entry->getValue());
              }
              else
              {
                  valid = false;
              }
          }
      }
  
      if(!valid || recipients.isEmpty())
      {
          return;
      }
  
      QString strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
      QString strFee = "";
      recipients[0].inputType = ONLY_DENOMINATED;
  
      if(ui->checkUsePrivateSend->isChecked()) {
          recipients[0].inputType = ONLY_DENOMINATED;
          strFunds = tr("using") + " <b>" + tr("anonymous funds") + "</b>";
          QString strNearestAmount(
              BitcoinUnits::formatWithUnit(
                  model->getOptionsModel()->getDisplayUnit(), CPrivateSend::GetSmallestDenomination()));
          strFee = QString(tr(
              "(privatesend requires this amount to be rounded up to the nearest %1)."
          ).arg(strNearestAmount));
      } else {
          recipients[0].inputType = ALL_COINS;
          strFunds = tr("using") + " <b>" + tr("any available funds (not anonymous)") + "</b>";
      }
  
      if(ui->checkUseInstantSend->isChecked()) {
          recipients[0].fUseInstantSend = true;
          strFunds += " ";
          strFunds += tr("and InstantSend");
      } else {
          recipients[0].fUseInstantSend = false;
      }
  
  
      fNewRecipientAllowed = false;
      // request unlock only if was locked or unlocked for mixing:
      // this way we let users unlock by walletpassphrase or by menu
      // and make many transactions while unlocking through this dialog
      // will call relock
      WalletModel::EncryptionStatus encStatus = model->getEncryptionStatus();
      if(encStatus == model->Locked || encStatus == model->UnlockedForMixingOnly)
      {
          WalletModel::UnlockContext ctx(model->requestUnlock());
          if(!ctx.isValid())
          {
              // Unlock wallet was cancelled
              fNewRecipientAllowed = true;
              return;
          }
          send(recipients, strFee, strFunds);
          return;
      }
      // already unlocked or not encrypted at all
      send(recipients, strFee, strFunds);
  }
  
  void SendCoinsDialog::send(QList<SendCoinsRecipient> recipients, QString strFee, QString strFunds)
  {
      // prepare transaction for getting txFee earlier
      WalletModelTransaction currentTransaction(recipients);
      WalletModel::SendCoinsReturn prepareStatus;
  
      // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled
      CCoinControl ctrl;
      if (model->getOptionsModel()->getCoinControlFeatures())
          ctrl = *CoinControlDialog::coinControl;
      if (ui->radioSmartFee->isChecked())
          ctrl.nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
      else
          ctrl.nConfirmTarget = 0;
  
      prepareStatus = model->prepareTransaction(currentTransaction, &ctrl);
  
      // process prepareStatus and on error generate message shown to user
      processSendCoinsReturn(prepareStatus,
          BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), currentTransaction.getTransactionFee()));
  
      if(prepareStatus.status != WalletModel::OK) {
          fNewRecipientAllowed = true;
          return;
      }
  
      CAmount txFee = currentTransaction.getTransactionFee();
  
      // Format confirmation message
      QStringList formatted;
      Q_FOREACH(const SendCoinsRecipient &rcp, currentTransaction.getRecipients())
      {
          // generate bold amount string
          QString amount = "<b>" + BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), rcp.amount);
          amount.append("</b> ").append(strFunds);
  
          // generate monospace address string
          QString address = "<span style='font-family: monospace;'>" + rcp.address;
          address.append("</span>");
  
          QString recipientElement;
  
          if (!rcp.paymentRequest.IsInitialized()) // normal payment
          {
              if(rcp.label.length() > 0) // label with address
              {
                  recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label));
                  recipientElement.append(QString(" (%1)").arg(address));
              }
              else // just address
              {
                  recipientElement = tr("%1 to %2").arg(amount, address);
              }
          }
          else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request
          {
              recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant));
          }
          else // unauthenticated payment request
          {
              recipientElement = tr("%1 to %2").arg(amount, address);
          }
  
          formatted.append(recipientElement);
      }
  
      QString questionString = tr("Are you sure you want to send?");
      questionString.append("<br /><br />%1");
  
      if(txFee > 0)
      {
          // append fee string if a fee is required
          questionString.append("<hr /><span style='color:#aa0000;'>");
          questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
          questionString.append("</span> ");
          questionString.append(tr("are added as transaction fee"));
          questionString.append(" ");
          questionString.append(strFee);
  
          // append transaction size
          questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
      }
  
      // add total amount in all subdivision units
      questionString.append("<hr />");
      CAmount totalAmount = currentTransaction.getTotalTransactionAmount() + txFee;
      QStringList alternativeUnits;
      Q_FOREACH(BitcoinUnits::Unit u, BitcoinUnits::availableUnits())
      {
          if(u != model->getOptionsModel()->getDisplayUnit())
              alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
      }
  
      // Show total amount + all alternative units
      questionString.append(tr("Total Amount = <b>%1</b><br />= %2")
          .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))
          .arg(alternativeUnits.join("<br />= ")));
  
      // Limit number of displayed entries
      int messageEntries = formatted.size();
      int displayedEntries = 0;
      for(int i = 0; i < formatted.size(); i++){
          if(i >= MAX_SEND_POPUP_ENTRIES){
              formatted.removeLast();
              i--;
          }
          else{
              displayedEntries = i+1;
          }
      }
      questionString.append("<hr />");
      questionString.append(tr("<b>(%1 of %2 entries displayed)</b>").arg(displayedEntries).arg(messageEntries));
  
      // Display message box
      SendConfirmationDialog confirmationDialog(tr("Confirm send coins"),
          questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this);
      confirmationDialog.exec();
      QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
  
      if(retval != QMessageBox::Yes)
      {
          fNewRecipientAllowed = true;
          return;
      }
  
      // now send the prepared transaction
      WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
      // process sendStatus and on error generate message shown to user
      processSendCoinsReturn(sendStatus);
  
      if (sendStatus.status == WalletModel::OK)
      {
          accept();
          CoinControlDialog::coinControl->UnSelectAll();
          coinControlUpdateLabels();
      }
      fNewRecipientAllowed = true;
  }
  
  void SendCoinsDialog::clear()
  {
      // Remove entries until only one left
      while(ui->entries->count())
      {
          ui->entries->takeAt(0)->widget()->deleteLater();
      }
      addEntry();
  
      updateTabsAndLabels();
  }
  
  void SendCoinsDialog::reject()
  {
      clear();
  }
  
  void SendCoinsDialog::accept()
  {
      clear();
  }
  
  SendCoinsEntry *SendCoinsDialog::addEntry()
  {
      SendCoinsEntry *entry = new SendCoinsEntry(platformStyle, this);
      entry->setModel(model);
      ui->entries->addWidget(entry);
      connect(entry, SIGNAL(removeEntry(SendCoinsEntry*)), this, SLOT(removeEntry(SendCoinsEntry*)));
      connect(entry, SIGNAL(payAmountChanged()), this, SLOT(coinControlUpdateLabels()));
      connect(entry, SIGNAL(subtractFeeFromAmountChanged()), this, SLOT(coinControlUpdateLabels()));
  
      // Focus the field, so that entry can start immediately
      entry->clear();
      entry->setFocus();
      ui->scrollAreaWidgetContents->resize(ui->scrollAreaWidgetContents->sizeHint());
      qApp->processEvents();
      QScrollBar* bar = ui->scrollArea->verticalScrollBar();
      if(bar)
          bar->setSliderPosition(bar->maximum());
  
      updateTabsAndLabels();
      return entry;
  }
  
  void SendCoinsDialog::updateTabsAndLabels()
  {
      setupTabChain(0);
      coinControlUpdateLabels();
  }
  
  void SendCoinsDialog::removeEntry(SendCoinsEntry* entry)
  {
      entry->hide();
  
      // If the last entry is about to be removed add an empty one
      if (ui->entries->count() == 1)
          addEntry();
  
      entry->deleteLater();
  
      updateTabsAndLabels();
  }
  
  QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
  {
      for(int i = 0; i < ui->entries->count(); ++i)
      {
          SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
          if(entry)
          {
              prev = entry->setupTabChain(prev);
          }
      }
      QWidget::setTabOrder(prev, ui->sendButton);
      QWidget::setTabOrder(ui->sendButton, ui->clearButton);
      QWidget::setTabOrder(ui->clearButton, ui->addButton);
      return ui->addButton;
  }
  
  void SendCoinsDialog::setAddress(const QString &address)
  {
      SendCoinsEntry *entry = 0;
      // Replace the first entry if it is still unused
      if(ui->entries->count() == 1)
      {
          SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
          if(first->isClear())
          {
              entry = first;
          }
      }
      if(!entry)
      {
          entry = addEntry();
      }
  
      entry->setAddress(address);
  }
  
  void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
  {
      if(!fNewRecipientAllowed)
          return;
  
      SendCoinsEntry *entry = 0;
      // Replace the first entry if it is still unused
      if(ui->entries->count() == 1)
      {
          SendCoinsEntry *first = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(0)->widget());
          if(first->isClear())
          {
              entry = first;
          }
      }
      if(!entry)
      {
          entry = addEntry();
      }
  
      entry->setValue(rv);
      updateTabsAndLabels();
  }
  
  bool SendCoinsDialog::handlePaymentRequest(const SendCoinsRecipient &rv)
  {
      // Just paste the entry, all pre-checks
      // are done in paymentserver.cpp.
      pasteEntry(rv);
      return true;
  }
  
  void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfirmedBalance, const CAmount& immatureBalance, const CAmount& anonymizedBalance,
                                   const CAmount& watchBalance, const CAmount& watchUnconfirmedBalance, const CAmount& watchImmatureBalance)
  {
      Q_UNUSED(unconfirmedBalance);
      Q_UNUSED(immatureBalance);
      Q_UNUSED(anonymizedBalance);
      Q_UNUSED(watchBalance);
      Q_UNUSED(watchUnconfirmedBalance);
      Q_UNUSED(watchImmatureBalance);
  
      if(model && model->getOptionsModel())
      {
  	    uint64_t bal = 0;
          QSettings settings;
          settings.setValue("bUseDarkSend", ui->checkUsePrivateSend->isChecked());
  	    if(ui->checkUsePrivateSend->isChecked()) {
  		    bal = anonymizedBalance;
  	    } else {
  		    bal = balance;
  	    }
  
          ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), bal));
      }
  }
  
  void SendCoinsDialog::updateDisplayUnit()
  {
      setBalance(model->getBalance(), model->getUnconfirmedBalance(), model->getImmatureBalance(), model->getAnonymizedBalance(),
                     model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
      CoinControlDialog::coinControl->fUsePrivateSend = ui->checkUsePrivateSend->isChecked();
      coinControlUpdateLabels();
      ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
      updateMinFeeLabel();
      updateSmartFeeLabel();
  }
  
  void SendCoinsDialog::updateInstantSend()
  {
      QSettings settings;
      settings.setValue("bUseInstantX", ui->checkUseInstantSend->isChecked());
      CoinControlDialog::coinControl->fUseInstantSend = ui->checkUseInstantSend->isChecked();
      coinControlUpdateLabels();
  }
  
  void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
  {
      QPair<QString, CClientUIInterface::MessageBoxFlags> msgParams;
      // Default to a warning message, override if error message is needed
      msgParams.second = CClientUIInterface::MSG_WARNING;
  
      // This comment is specific to SendCoinsDialog usage of WalletModel::SendCoinsReturn.
      // WalletModel::TransactionCommitFailed is used only in WalletModel::sendCoins()
      // all others are used only in WalletModel::prepareTransaction()
      switch(sendCoinsReturn.status)
      {
      case WalletModel::InvalidAddress:
          msgParams.first = tr("The recipient address is not valid. Please recheck.");
          break;
      case WalletModel::InvalidAmount:
          msgParams.first = tr("The amount to pay must be larger than 0.");
          break;
      case WalletModel::AmountExceedsBalance:
          msgParams.first = tr("The amount exceeds your balance.");
          break;
      case WalletModel::AmountWithFeeExceedsBalance:
          msgParams.first = tr("The total exceeds your balance when the %1 transaction fee is included.").arg(msgArg);
          break;
      case WalletModel::DuplicateAddress:
          msgParams.first = tr("Duplicate address found: addresses should only be used once each.");
          break;
      case WalletModel::TransactionCreationFailed:
          msgParams.first = tr("Transaction creation failed!");
          msgParams.second = CClientUIInterface::MSG_ERROR;
          break;
      case WalletModel::TransactionCommitFailed:
          msgParams.first = tr("The transaction was rejected with the following reason: %1").arg(sendCoinsReturn.reasonCommitFailed);
          msgParams.second = CClientUIInterface::MSG_ERROR;
          break;
      case WalletModel::AbsurdFee:
          msgParams.first = tr("A fee higher than %1 is considered an absurdly high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), maxTxFee));
          break;
      case WalletModel::PaymentRequestExpired:
          msgParams.first = tr("Payment request expired.");
          msgParams.second = CClientUIInterface::MSG_ERROR;
          break;
      // included to prevent a compiler warning.
      case WalletModel::OK:
      default:
          return;
      }
  
      Q_EMIT message(tr("Send Coins"), msgParams.first, msgParams.second);
  }
  
  void SendCoinsDialog::minimizeFeeSection(bool fMinimize)
  {
      ui->labelFeeMinimized->setVisible(fMinimize);
      ui->buttonChooseFee  ->setVisible(fMinimize);
      ui->buttonMinimizeFee->setVisible(!fMinimize);
      ui->frameFeeSelection->setVisible(!fMinimize);
      ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
      fFeeMinimized = fMinimize;
  }
  
  void SendCoinsDialog::on_buttonChooseFee_clicked()
  {
      minimizeFeeSection(false);
  }
  
  void SendCoinsDialog::on_buttonMinimizeFee_clicked()
  {
      updateFeeMinimizedLabel();
      minimizeFeeSection(true);
  }
  
  void SendCoinsDialog::setMinimumFee()
  {
      ui->radioCustomPerKilobyte->setChecked(true);
      ui->customFee->setValue(CWallet::GetRequiredFee(1000));
  }
  
  void SendCoinsDialog::updateFeeSectionControls()
  {
      ui->sliderSmartFee          ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelSmartFee           ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelSmartFee2          ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelSmartFee3          ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelFeeEstimation      ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelSmartFeeNormal     ->setEnabled(ui->radioSmartFee->isChecked());
      ui->labelSmartFeeFast       ->setEnabled(ui->radioSmartFee->isChecked());
      ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
      ui->checkBoxMinimumFee      ->setEnabled(ui->radioCustomFee->isChecked());
      ui->labelMinFeeWarning      ->setEnabled(ui->radioCustomFee->isChecked());
      ui->radioCustomPerKilobyte  ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
      ui->radioCustomAtLeast      ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() && CoinControlDialog::coinControl->HasSelected());
      ui->customFee               ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
  }
  
  void SendCoinsDialog::updateGlobalFeeVariables()
  {
      if (ui->radioSmartFee->isChecked())
      {
          int nConfirmTarget = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
          payTxFee = CFeeRate(0);
  
          // set nMinimumTotalFee to 0 to not accidentally pay a custom fee
          CoinControlDialog::coinControl->nMinimumTotalFee = 0;
  
          // show the estimated required time for confirmation
          ui->confirmationTargetLabel->setText(GUIUtil::formatDurationStr(nConfirmTarget * Params().GetConsensus().nPowTargetSpacing) + " / " + tr("%n block(s)", "", nConfirmTarget));
      }
      else
      {
          payTxFee = CFeeRate(ui->customFee->value());
  
          // if user has selected to set a minimum absolute fee, pass the value to coincontrol
          // set nMinimumTotalFee to 0 in case of user has selected that the fee is per KB
          CoinControlDialog::coinControl->nMinimumTotalFee = ui->radioCustomAtLeast->isChecked() ? ui->customFee->value() : 0;
      }
  }
  
  void SendCoinsDialog::updateFeeMinimizedLabel()
  {
      if(!model || !model->getOptionsModel())
          return;
  
      if (ui->radioSmartFee->isChecked())
          ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
      else {
          ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
              ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
      }
  }
  
  void SendCoinsDialog::updateMinFeeLabel()
  {
      if (model && model->getOptionsModel())
          ui->checkBoxMinimumFee->setText(tr("Pay only the required fee of %1").arg(
              BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::GetRequiredFee(1000)) + "/kB")
          );
  }
  
  void SendCoinsDialog::updateSmartFeeLabel()
  {
      if(!model || !model->getOptionsModel())
          return;
  
      int nBlocksToConfirm = ui->sliderSmartFee->maximum() - ui->sliderSmartFee->value() + 2;
      int estimateFoundAtBlocks = nBlocksToConfirm;
      CFeeRate feeRate = mempool.estimateSmartFee(nBlocksToConfirm, &estimateFoundAtBlocks);
      if (feeRate <= CFeeRate(0)) // not enough data => minfee
      {
          ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
                                                                  std::max(CWallet::fallbackFee.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
          ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
          ui->labelFeeEstimation->setText("");
          ui->fallbackFeeWarningLabel->setVisible(true);
          int lightness = ui->fallbackFeeWarningLabel->palette().color(QPalette::WindowText).lightness();
          QColor warning_colour(255 - (lightness / 5), 176 - (lightness / 3), 48 - (lightness / 14));
          ui->fallbackFeeWarningLabel->setStyleSheet("QLabel { color: " + warning_colour.name() + "; }");
          ui->fallbackFeeWarningLabel->setIndent(QFontMetrics(ui->fallbackFeeWarningLabel->font()).width("x"));
      }
      else
      {
          ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(),
                                                                  std::max(feeRate.GetFeePerK(), CWallet::GetRequiredFee(1000))) + "/kB");
          ui->labelSmartFee2->hide();
          ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %n block(s).", "", estimateFoundAtBlocks));
          ui->fallbackFeeWarningLabel->setVisible(false);
      }
  
      updateFeeMinimizedLabel();
  }
  
  // Coin Control: copy label "Quantity" to clipboard
  void SendCoinsDialog::coinControlClipboardQuantity()
  {
      GUIUtil::setClipboard(ui->labelCoinControlQuantity->text());
  }
  
  // Coin Control: copy label "Amount" to clipboard
  void SendCoinsDialog::coinControlClipboardAmount()
  {
      GUIUtil::setClipboard(ui->labelCoinControlAmount->text().left(ui->labelCoinControlAmount->text().indexOf(" ")));
  }
  
  // Coin Control: copy label "Fee" to clipboard
  void SendCoinsDialog::coinControlClipboardFee()
  {
      GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
  }
  
  // Coin Control: copy label "After fee" to clipboard
  void SendCoinsDialog::coinControlClipboardAfterFee()
  {
      GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
  }
  
  // Coin Control: copy label "Bytes" to clipboard
  void SendCoinsDialog::coinControlClipboardBytes()
  {
      GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace(ASYMP_UTF8, ""));
  }
  
  // Coin Control: copy label "Dust" to clipboard
  void SendCoinsDialog::coinControlClipboardLowOutput()
  {
      GUIUtil::setClipboard(ui->labelCoinControlLowOutput->text());
  }
  
  // Coin Control: copy label "Change" to clipboard
  void SendCoinsDialog::coinControlClipboardChange()
  {
      GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace(ASYMP_UTF8, ""));
  }
  
  // Coin Control: settings menu - coin control enabled/disabled by user
  void SendCoinsDialog::coinControlFeatureChanged(bool checked)
  {
      ui->frameCoinControl->setVisible(checked);
  
      if (!checked && model) // coin control features disabled
          CoinControlDialog::coinControl->SetNull();
  
      // make sure we set back the confirmation target
      updateGlobalFeeVariables();
      coinControlUpdateLabels();
  }
  
  // Coin Control: button inputs -> show actual coin control dialog
  void SendCoinsDialog::coinControlButtonClicked()
  {
      CoinControlDialog dlg(platformStyle);
      dlg.setModel(model);
      dlg.exec();
      coinControlUpdateLabels();
  }
  
  // Coin Control: checkbox custom change address
  void SendCoinsDialog::coinControlChangeChecked(int state)
  {
      if (state == Qt::Unchecked)
      {
          CoinControlDialog::coinControl->destChange = CNoDestination();
          ui->labelCoinControlChangeLabel->clear();
      }
      else
          // use this to re-validate an already entered address
          coinControlChangeEdited(ui->lineEditCoinControlChange->text());
  
      ui->lineEditCoinControlChange->setEnabled((state == Qt::Checked));
  }
  
  // Coin Control: custom change address changed
  void SendCoinsDialog::coinControlChangeEdited(const QString& text)
  {
      if (model && model->getAddressTableModel())
      {
          // Default to no change address until verified
          CoinControlDialog::coinControl->destChange = CNoDestination();
          ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}");
  
          CBitcoinAddress addr = CBitcoinAddress(text.toStdString());
  
          if (text.isEmpty()) // Nothing entered
          {
              ui->labelCoinControlChangeLabel->setText("");
          }
          else if (!addr.IsValid()) // Invalid address
          {
              ui->labelCoinControlChangeLabel->setText(tr("Warning: Invalid Pyrk address"));
          }
          else // Valid address
          {
              CKeyID keyid;
              addr.GetKeyID(keyid);
              if (!model->havePrivKey(keyid)) // Unknown change address
              {
                  ui->labelCoinControlChangeLabel->setText(tr("Warning: Unknown change address"));
  
                  // confirmation dialog
                  QMessageBox::StandardButton btnRetVal = QMessageBox::question(this, tr("Confirm custom change address"), tr("The address you selected for change is not part of this wallet. Any or all funds in your wallet may be sent to this address. Are you sure?"),
                      QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel);
  
                  if(btnRetVal == QMessageBox::Yes)
                      CoinControlDialog::coinControl->destChange = addr.Get();
                  else
                  {
                      ui->lineEditCoinControlChange->setText("");
                      ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
                      ui->labelCoinControlChangeLabel->setText("");
                  }
              }
              else // Known change address
              {
                  ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:black;}");
  
                  // Query label
                  QString associatedLabel = model->getAddressTableModel()->labelForAddress(text);
                  if (!associatedLabel.isEmpty())
                      ui->labelCoinControlChangeLabel->setText(associatedLabel);
                  else
                      ui->labelCoinControlChangeLabel->setText(tr("(no label)"));
  
                  CoinControlDialog::coinControl->destChange = addr.Get();
              }
          }
      }
  }
  
  // Coin Control: update labels
  void SendCoinsDialog::coinControlUpdateLabels()
  {
      if (!model || !model->getOptionsModel())
          return;
  
      if (model->getOptionsModel()->getCoinControlFeatures())
      {
          // enable minimum absolute fee UI controls
          ui->radioCustomAtLeast->setVisible(true);
  
          // only enable the feature if inputs are selected
          ui->radioCustomAtLeast->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked() &&CoinControlDialog::coinControl->HasSelected());
      }
      else
      {
          // in case coin control is disabled (=default), hide minimum absolute fee UI controls
          ui->radioCustomAtLeast->setVisible(false);
          return;
      }
  
      // set pay amounts
      CoinControlDialog::payAmounts.clear();
      CoinControlDialog::fSubtractFeeFromAmount = false;
      for(int i = 0; i < ui->entries->count(); ++i)
      {
          SendCoinsEntry *entry = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget());
          if(entry && !entry->isHidden())
          {
              SendCoinsRecipient rcp = entry->getValue();
              CoinControlDialog::payAmounts.append(rcp.amount);
              if (rcp.fSubtractFeeFromAmount)
                  CoinControlDialog::fSubtractFeeFromAmount = true;
          }
      }
  
      ui->checkUsePrivateSend->setChecked(CoinControlDialog::coinControl->fUsePrivateSend);
  
      if (CoinControlDialog::coinControl->HasSelected())
      {
          // actual coin control calculation
          CoinControlDialog::updateLabels(model, this);
  
          // show coin control stats
          ui->labelCoinControlAutomaticallySelected->hide();
          ui->widgetCoinControl->show();
      }
      else
      {
          // hide coin control stats
          ui->labelCoinControlAutomaticallySelected->show();
          ui->widgetCoinControl->hide();
          ui->labelCoinControlInsuffFunds->hide();
      }
  }
  
  SendConfirmationDialog::SendConfirmationDialog(const QString &title, const QString &text, int _secDelay,
      QWidget *parent) :
      QMessageBox(QMessageBox::Question, title, text, QMessageBox::Yes | QMessageBox::Cancel, parent), secDelay(_secDelay)
  {
      setDefaultButton(QMessageBox::Cancel);
      yesButton = button(QMessageBox::Yes);
      updateYesButton();
      connect(&countDownTimer, SIGNAL(timeout()), this, SLOT(countDown()));
  }
  
  int SendConfirmationDialog::exec()
  {
      updateYesButton();
      countDownTimer.start(1000);
      return QMessageBox::exec();
  }
  
  void SendConfirmationDialog::countDown()
  {
      secDelay--;
      updateYesButton();
  
      if(secDelay <= 0)
      {
          countDownTimer.stop();
      }
  }
  
  void SendConfirmationDialog::updateYesButton()
  {
      if(secDelay > 0)
      {
          yesButton->setEnabled(false);
          yesButton->setText(tr("Yes") + " (" + QString::number(secDelay) + ")");
      }
      else
      {
          yesButton->setEnabled(true);
          yesButton->setText(tr("Yes"));
      }
  }
  