text: // Copyright (c) 2019 The TradeTensor developers
  // Distributed under the MIT software license, see the accompanying
  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  
  #include "qt/tradetensor/coldstakingmodel.h"
  #include "uint256.h"
  #include "bitcoinunits.h"
  #include "guiutil.h"
  #include <iostream>
  #include "addressbook.h"
  
  ColdStakingModel::ColdStakingModel(WalletModel* _model,
                                     TransactionTableModel* _tableModel,
                                     AddressTableModel* _addressTableModel,
                                     QObject *parent) : QAbstractTableModel(parent), model(_model), tableModel(_tableModel), addressTableModel(_addressTableModel), cachedAmount(0){
  }
  
  void ColdStakingModel::updateCSList() {
      refresh();
      QMetaObject::invokeMethod(this, "emitDataSetChanged", Qt::QueuedConnection);
  }
  
  void ColdStakingModel::emitDataSetChanged() {
      emit dataChanged(index(0, 0, QModelIndex()), index(cachedDelegations.size(), COLUMN_COUNT, QModelIndex()) );
  }
  
  void ColdStakingModel::refresh() {
      cachedDelegations.clear();
      cachedAmount = 0;
      // First get all of the p2cs utxo inside the wallet
      std::vector<COutput> utxoList;
      pwalletMain->GetAvailableP2CSCoins(utxoList);
  
      if (!utxoList.empty()) {
          // Loop over each COutput into a CSDelegation
          for (const auto& utxo : utxoList) {
  
              const auto *wtx = utxo.tx;
              const QString txId = QString::fromStdString(wtx->GetHash().GetHex());
              const CTxOut& out = wtx->vout[utxo.i];
  
              // First parse the cs delegation
              CSDelegation delegation;
              if (!parseCSDelegation(out, delegation, txId, utxo.i))
                  continue;
  
              // it's spendable only when this wallet has the keys to spend it, a.k.a is the owner
              delegation.isSpendable = utxo.fSpendable;
              delegation.cachedTotalAmount += out.nValue;
              delegation.delegatedUtxo.insert(txId, utxo.i);
  
              // Now verify if the delegation exists in the cached list
              int indexDel = cachedDelegations.indexOf(delegation);
              if (indexDel == -1) {
                  // If it doesn't, let's append it.
                  cachedDelegations.append(delegation);
              } else {
                  CSDelegation& del = cachedDelegations[indexDel];
                  del.delegatedUtxo.unite(delegation.delegatedUtxo);
                  del.cachedTotalAmount += delegation.cachedTotalAmount;
              }
  
              // add amount to cachedAmount if either:
              // - this is a owned delegation
              // - this is a staked delegation, and the owner is whitelisted
              if (!delegation.isSpendable && !addressTableModel->isWhitelisted(delegation.ownerAddress)) continue;
              cachedAmount += delegation.cachedTotalAmount;
          }
      }
  }
  
  bool ColdStakingModel::parseCSDelegation(const CTxOut& out, CSDelegation& ret, const QString& txId, const int& utxoIndex) {
      txnouttype type;
      std::vector<CTxDestination> addresses;
      int nRequired;
  
      if (!ExtractDestinations(out.scriptPubKey, type, addresses, nRequired) || addresses.size() != 2) {
          return error("%s : Error extracting P2CS destinations for utxo: %s-%d",
                  __func__, txId.toStdString(), utxoIndex);
      }
  
      std::string stakingAddressStr = CBitcoinAddress(
              addresses[0],
              CChainParams::STAKING_ADDRESS
      ).ToString();
  
      std::string ownerAddressStr = CBitcoinAddress(
              addresses[1],
              CChainParams::PUBKEY_ADDRESS
      ).ToString();
  
      ret = CSDelegation(stakingAddressStr, ownerAddressStr);
  
      return true;
  }
  
  int ColdStakingModel::rowCount(const QModelIndex &parent) const
  {
      Q_UNUSED(parent);
      return cachedDelegations.size();
  }
  
  int ColdStakingModel::columnCount(const QModelIndex &parent) const
  {
      Q_UNUSED(parent);
      return COLUMN_COUNT;
  }
  
  
  QVariant ColdStakingModel::data(const QModelIndex &index, int role) const
  {
      if(!index.isValid())
              return QVariant();
  
      int row = index.row();
      CSDelegation rec = cachedDelegations[row];
      if (role == Qt::DisplayRole || role == Qt::EditRole) {
          switch (index.column()) {
              case OWNER_ADDRESS:
                  return QString::fromStdString(rec.ownerAddress);
              case OWNER_ADDRESS_LABEL:
                  return addressTableModel->labelForAddress(QString::fromStdString(rec.ownerAddress));
              case STAKING_ADDRESS:
                  return QString::fromStdString(rec.stakingAddress);
              case STAKING_ADDRESS_LABEL:
                  return addressTableModel->labelForAddress(QString::fromStdString(rec.stakingAddress));
              case IS_WHITELISTED:
                  return addressTableModel->purposeForAddress(rec.ownerAddress).compare(AddressBook::AddressBookPurpose::DELEGATOR) == 0;
              case IS_WHITELISTED_STRING:
                  return (addressTableModel->purposeForAddress(rec.ownerAddress) == AddressBook::AddressBookPurpose::DELEGATOR ? "Staking" : "Not staking");
              case TOTAL_STACKEABLE_AMOUNT_STR:
                  return GUIUtil::formatBalance(rec.cachedTotalAmount);
              case TOTAL_STACKEABLE_AMOUNT:
                  return qint64(rec.cachedTotalAmount);
              case IS_RECEIVED_DELEGATION:
                  return !rec.isSpendable;
          }
      }
  
      return QVariant();
  }
  
  bool ColdStakingModel::whitelist(const QModelIndex& modelIndex)
  {
      QString address = modelIndex.data(Qt::DisplayRole).toString();
      if (addressTableModel->isWhitelisted(address.toStdString())) {
          return error("trying to whitelist already whitelisted address");
      }
  
      if (!model->whitelistAddressFromColdStaking(address)) return false;
  
      // address whitelisted - update cached amount and row data
      const int idx = modelIndex.row();
      cachedAmount += cachedDelegations[idx].cachedTotalAmount;
      removeRowAndEmitDataChanged(idx);
  
      return true;
  }
  
  bool ColdStakingModel::blacklist(const QModelIndex& modelIndex)
  {
      QString address = modelIndex.data(Qt::DisplayRole).toString();
      if (!addressTableModel->isWhitelisted(address.toStdString())) {
          return error("trying to blacklist already blacklisted address");
      }
  
      if (!model->blacklistAddressFromColdStaking(address)) return false;
  
      // address blacklisted - update cached amount and row data
      const int idx = modelIndex.row();
      cachedAmount -= cachedDelegations[idx].cachedTotalAmount;
      removeRowAndEmitDataChanged(idx);
  
      return true;
  }
  
  void ColdStakingModel::removeRowAndEmitDataChanged(const int idx)
  {
      beginRemoveRows(QModelIndex(), idx, idx);
      endRemoveRows();
      emit dataChanged(index(idx, 0, QModelIndex()), index(idx, COLUMN_COUNT, QModelIndex()) );
  }
  
  