// Copyright (c) 2011-2016 The Bitcoin Core developers
// Copyright (c) 2021 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#if defined(HAVE_CONFIG_H)
#include <config/bitcoin-config.h>
#endif

#include <qt/addressbookpage.h>
#include <qt/forms/ui_addressbookpage.h>

#include <qt/addresstablemodel.h>
#include <qt/bitcoingui.h>
#include <qt/csvmodelwriter.h>
#include <qt/editaddressdialog.h>
#include <qt/guiutil.h>
#include <qt/platformstyle.h>

#include <QIcon>
#include <QMenu>
#include <QMessageBox>
#include <QSortFilterProxyModel>

class AddressBookSortFilterProxyModel final : public QSortFilterProxyModel {
    const QString m_type;

public:
    AddressBookSortFilterProxyModel(const QString &type, QObject *parent)
        : QSortFilterProxyModel(parent), m_type(type) {
        setDynamicSortFilter(true);
        setFilterCaseSensitivity(Qt::CaseInsensitive);
        setSortCaseSensitivity(Qt::CaseInsensitive);
    }

protected:
    bool filterAcceptsRow(int row, const QModelIndex &parent) const {
        auto model = sourceModel();
        auto label = model->index(row, AddressTableModel::Label, parent);

        if (model->data(label, AddressTableModel::TypeRole).toString() !=
            m_type) {
            return false;
        }

        auto address = model->index(row, AddressTableModel::Address, parent);

        if (filterRegExp().indexIn(model->data(address).toString()) < 0 &&
            filterRegExp().indexIn(model->data(label).toString()) < 0) {
            return false;
        }

        return true;
    }
};

AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode,
                                 Tabs _tab, QWidget *parent)
    : QDialog(parent), ui(new Ui::AddressBookPage), model(nullptr), mode(_mode),
      tab(_tab) {
    ui->setupUi(this);

    if (!platformStyle->getImagesOnButtons()) {
        ui->newAddress->setIcon(QIcon());
        ui->copyAddress->setIcon(QIcon());
        ui->deleteAddress->setIcon(QIcon());
        ui->exportButton->setIcon(QIcon());
    } else {
        ui->newAddress->setIcon(platformStyle->SingleColorIcon(":/icons/add"));
        ui->copyAddress->setIcon(
            platformStyle->SingleColorIcon(":/icons/editcopy"));
        ui->deleteAddress->setIcon(
            platformStyle->SingleColorIcon(":/icons/remove"));
        ui->exportButton->setIcon(
            platformStyle->SingleColorIcon(":/icons/export"));
    }

    switch (mode) {
        case ForSelection:
            switch (tab) {
                case SendingTab:
                    setWindowTitle(tr("Choose the address to send coins to"));
                    break;
                case ReceivingTab:
                    setWindowTitle(
                        tr("Choose the address to receive coins with"));
                    break;
            }
            connect(ui->tableView, &QTableView::doubleClicked, this,
                    &QDialog::accept);
            ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
            ui->tableView->setFocus();
            ui->closeButton->setText(tr("C&hoose"));
            ui->exportButton->hide();
            break;
        case ForEditing:
            switch (tab) {
                case SendingTab:
                    setWindowTitle(tr("Sending Addresses"));
                    break;
                case ReceivingTab:
                    setWindowTitle(tr("Receiving Addresses"));
                    break;
            }
            break;
    }
    switch (tab) {
        case SendingTab:
            ui->labelExplanation->setText(
                tr("These are your Bitcoin Cash addresses for sending payments. "
                   "Always check the amount and the receiving address before "
                   "sending coins."));
            ui->deleteAddress->setVisible(true);
            ui->newAddress->setVisible(true);
            break;
        case ReceivingTab:
            ui->labelExplanation->setText(
                tr("These are your Bitcoin Cash addresses for receiving payments. "
                   "It is recommended to use a new receiving address for each "
                   "transaction."));
            ui->deleteAddress->setVisible(false);
            ui->newAddress->setVisible(false);
            break;
    }

    // Context menu actions
    QAction *copyAddressAction = new QAction(tr("&Copy Address"), this);
    QAction *copyLabelAction = new QAction(tr("Copy &Label"), this);
    QAction *editAction = new QAction(tr("&Edit"), this);
    deleteAction = new QAction(ui->deleteAddress->text(), this);

    // Build context menu
    contextMenu = new QMenu(this);
    contextMenu->addAction(copyAddressAction);
    contextMenu->addAction(copyLabelAction);
    contextMenu->addAction(editAction);
    if (tab == SendingTab) {
        contextMenu->addAction(deleteAction);
    }
    contextMenu->addSeparator();

    // Connect signals for context menu actions
    connect(copyAddressAction, &QAction::triggered, this,
            &AddressBookPage::on_copyAddress_clicked);
    connect(copyLabelAction, &QAction::triggered, this,
            &AddressBookPage::onCopyLabelAction);
    connect(editAction, &QAction::triggered, this,
            &AddressBookPage::onEditAction);
    connect(deleteAction, &QAction::triggered, this,
            &AddressBookPage::on_deleteAddress_clicked);

    connect(ui->tableView, &QWidget::customContextMenuRequested, this,
            &AddressBookPage::contextualMenu);

    connect(ui->closeButton, &QPushButton::clicked, this, &QDialog::accept);

    GUIUtil::handleCloseWindowShortcut(this);
}

AddressBookPage::~AddressBookPage() {
    delete ui;
}

void AddressBookPage::setModel(AddressTableModel *_model) {
    this->model = _model;
    if (!_model) {
        return;
    }

    auto type = tab == ReceivingTab ? AddressTableModel::Receive
                                    : AddressTableModel::Send;
    proxyModel = new AddressBookSortFilterProxyModel(type, this);
    proxyModel->setSourceModel(_model);

    connect(ui->searchLineEdit, &QLineEdit::textChanged, proxyModel,
            &QSortFilterProxyModel::setFilterWildcard);

    ui->tableView->setModel(proxyModel);
    ui->tableView->sortByColumn(0, Qt::AscendingOrder);

    // Set column widths
    ui->tableView->horizontalHeader()->setSectionResizeMode(
        AddressTableModel::Label, QHeaderView::Stretch);
    ui->tableView->horizontalHeader()->setSectionResizeMode(
        AddressTableModel::Address, QHeaderView::ResizeToContents);

    connect(ui->tableView->selectionModel(),
            &QItemSelectionModel::selectionChanged, this,
            &AddressBookPage::selectionChanged);

    // Select row for newly created address
    connect(_model, &AddressTableModel::rowsInserted, this,
            &AddressBookPage::selectNewAddress);

    selectionChanged();
}

void AddressBookPage::on_copyAddress_clicked() {
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Address);
}

void AddressBookPage::onCopyLabelAction() {
    GUIUtil::copyEntryData(ui->tableView, AddressTableModel::Label);
}

void AddressBookPage::onEditAction() {
    if (!model) {
        return;
    }

    if (!ui->tableView->selectionModel()) {
        return;
    }
    QModelIndexList indexes = ui->tableView->selectionModel()->selectedRows();
    if (indexes.isEmpty()) {
        return;
    }

    EditAddressDialog dlg(tab == SendingTab
                              ? EditAddressDialog::EditSendingAddress
                              : EditAddressDialog::EditReceivingAddress,
                          this);
    dlg.setModel(model);
    QModelIndex origIndex = proxyModel->mapToSource(indexes.at(0));
    dlg.loadRow(origIndex.row());
    dlg.exec();
}

void AddressBookPage::on_newAddress_clicked() {
    if (!model) {
        return;
    }

    if (tab == ReceivingTab) {
        return;
    }

    EditAddressDialog dlg(EditAddressDialog::NewSendingAddress, this);
    dlg.setModel(model);
    if (dlg.exec()) {
        newAddressToSelect = dlg.getAddress();
    }
}

void AddressBookPage::on_deleteAddress_clicked() {
    QTableView *table = ui->tableView;
    if (!table->selectionModel()) {
        return;
    }

    QModelIndexList indexes = table->selectionModel()->selectedRows();
    if (!indexes.isEmpty()) {
        table->model()->removeRow(indexes.at(0).row());
    }
}

void AddressBookPage::selectionChanged() {
    // Set button states based on selected tab and selection
    QTableView *table = ui->tableView;
    if (!table->selectionModel()) {
        return;
    }

    if (table->selectionModel()->hasSelection()) {
        switch (tab) {
            case SendingTab:
                // In sending tab, allow deletion of selection
                ui->deleteAddress->setEnabled(true);
                ui->deleteAddress->setVisible(true);
                deleteAction->setEnabled(true);
                break;
            case ReceivingTab:
                // Deleting receiving addresses, however, is not allowed
                ui->deleteAddress->setEnabled(false);
                ui->deleteAddress->setVisible(false);
                deleteAction->setEnabled(false);
                break;
        }
        ui->copyAddress->setEnabled(true);
    } else {
        ui->deleteAddress->setEnabled(false);
        ui->copyAddress->setEnabled(false);
    }
}

void AddressBookPage::done(int retval) {
    QTableView *table = ui->tableView;
    if (!table->selectionModel() || !table->model()) {
        return;
    }

    // Figure out which address was selected, and return it
    QModelIndexList indexes =
        table->selectionModel()->selectedRows(AddressTableModel::Address);

    for (const QModelIndex &index : indexes) {
        QVariant address = table->model()->data(index);
        returnValue = address.toString();
    }

    if (returnValue.isEmpty()) {
        // If no address entry selected, return rejected
        retval = Rejected;
    }

    QDialog::done(retval);
}

void AddressBookPage::on_exportButton_clicked() {
    // CSV is currently the only supported format
    QString filename =
        GUIUtil::getSaveFileName(this, tr("Export Address List"), QString(),
                                 tr("Comma separated file (*.csv)"), nullptr);

    if (filename.isNull()) {
        return;
    }

    CSVModelWriter writer(filename);

    // name, column, role
    writer.setModel(proxyModel);
    writer.addColumn("Label", AddressTableModel::Label, Qt::EditRole);
    writer.addColumn("Address", AddressTableModel::Address, Qt::EditRole);

    if (!writer.write()) {
        QMessageBox::critical(this, tr("Exporting Failed"),
                              tr("There was an error trying to save the "
                                 "address list to %1. Please try again.")
                                  .arg(filename));
    }
}

void AddressBookPage::contextualMenu(const QPoint &point) {
    QModelIndex index = ui->tableView->indexAt(point);
    if (index.isValid()) {
        contextMenu->exec(QCursor::pos());
    }
}

void AddressBookPage::selectNewAddress(const QModelIndex &parent, int begin,
                                       int /*end*/) {
    QModelIndex idx = proxyModel->mapFromSource(
        model->index(begin, AddressTableModel::Address, parent));
    if (idx.isValid() &&
        (idx.data(Qt::EditRole).toString() == newAddressToSelect)) {
        // Select row of newly created address, once
        ui->tableView->setFocus();
        ui->tableView->selectRow(idx.row());
        newAddressToSelect.clear();
    }
}
