/*
    SPDX-FileCopyrightText: 2010 Casey Link <unnamedrambler@gmail.com>
    SPDX-FileCopyrightText: 2009-2010 Klaralvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "recipientline.h"

#include <KEmailAddress>
#include <KLocalizedString>
#include <KMime/Types>

#include <QHBoxLayout>
#include <QKeyEvent>
#include <QToolButton>

using namespace KPIM;
using namespace Qt::Literals::StringLiterals;

RecipientComboBox::RecipientComboBox(QWidget *parent)
    : QComboBox(parent)
{
}

void RecipientComboBox::keyPressEvent(QKeyEvent *ev)
{
    if (ev->key() == Qt::Key_Right) {
        Q_EMIT rightPressed();
    } else {
        QComboBox::keyPressEvent(ev);
    }
}

RecipientLineEdit::RecipientLineEdit(QWidget *parent)
    : AddresseeLineEdit(parent)
    , mToolButton(new QToolButton(this))
{
    mToolButton->setVisible(false);
    mToolButton->setCursor(Qt::ArrowCursor);
    const int size = sizeHint().height() - 5;
    mToolButton->setFixedSize(size, size);
    int padding = (sizeHint().height() - size) / 2;
    mToolButton->move(2, padding);
    mToolButton->setStyleSheet(QStringLiteral("QToolButton { border: none; }"));
    connect(mToolButton, &QToolButton::clicked, this, &RecipientLineEdit::iconClicked);
}

void RecipientLineEdit::keyPressEvent(QKeyEvent *ev)
{
    if (ev->key() == Qt::Key_Left && cursorPosition() == 0 && !ev->modifiers().testFlag(Qt::ShiftModifier)) { // Shift would be pressed during selection
        Q_EMIT leftPressed();
    } else if (ev->key() == Qt::Key_Right && cursorPosition() == text().length()
               && !ev->modifiers().testFlag(Qt::ShiftModifier)) { // Shift would be pressed during selection
        Q_EMIT rightPressed();
    } else if ((ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return)) { // && !completionBox()->isVisible()) {
        Q_EMIT focusDown();
        KLineEdit::keyPressEvent(ev);
        return;
    } else if (ev->key() == Qt::Key_Up) {
        Q_EMIT focusUp();
        return;
    } else if (ev->key() == Qt::Key_Down) {
        Q_EMIT focusDown();
        return;
    } else {
        KLineEdit::keyPressEvent(ev);
    }
}

void RecipientLineEdit::setIcon(const QIcon &icon, const QString &tooltip)
{
    if (icon.isNull()) {
        mToolButton->setVisible(false);
        setStyleSheet(QString());
    } else {
        mToolButton->setIcon(icon);
        mToolButton->setToolTip(tooltip);
        const int padding = mToolButton->width() - style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
        setStyleSheet(QStringLiteral("QLineEdit { padding-left: %1px }").arg(padding));
        mToolButton->setVisible(true);
    }
}

RecipientLineNG::RecipientLineNG(QWidget *parent)
    : MultiplyingLine(parent)
    , mData(new Recipient)
{
    setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);

    auto topLayout = new QHBoxLayout(this);
    topLayout->setContentsMargins({});

    const QStringList recipientTypes = Recipient::allTypeLabels();

    mCombo = new RecipientComboBox(this);
    mCombo->addItems(recipientTypes);
    topLayout->addWidget(mCombo);
    mCombo->setToolTip(i18nc("@label:listbox", "Select type of recipient"));
    mEdit = new RecipientLineEdit(this);
    mEdit->setToolTip(i18n("Set the list of email addresses to receive this message"));
    mEdit->setClearButtonEnabled(true);
    topLayout->addWidget(mEdit);
    mEdit->installEventFilter(this);

    connect(mEdit, &RecipientLineEdit::returnPressed, this, &RecipientLineNG::slotReturnPressed);
    connect(mEdit, &RecipientLineEdit::deleteMe, this, &RecipientLineNG::slotPropagateDeletion);
    connect(mEdit, &QLineEdit::textChanged, this, &RecipientLineNG::analyzeLine);
    connect(mEdit, &RecipientLineEdit::focusUp, this, &RecipientLineNG::slotFocusUp);
    connect(mEdit, &RecipientLineEdit::focusDown, this, &RecipientLineNG::slotFocusDown);
    connect(mEdit, &RecipientLineEdit::rightPressed, this, &RecipientLineNG::rightPressed);
    connect(mEdit, &RecipientLineEdit::iconClicked, this, &RecipientLineNG::iconClicked);

    connect(mEdit, &RecipientLineEdit::leftPressed, mCombo, qOverload<>(&QWidget::setFocus));
    connect(mEdit, &RecipientLineEdit::editingFinished, this, &RecipientLineNG::slotEditingFinished);
    connect(mEdit, &RecipientLineEdit::clearButtonClicked, this, &RecipientLineNG::slotPropagateDeletion);
    connect(mCombo, &RecipientComboBox::rightPressed, mEdit, qOverload<>(&QWidget::setFocus));

    connect(mCombo, &RecipientComboBox::activated, this, &RecipientLineNG::slotTypeModified);

    connect(mEdit, &RecipientLineEdit::addAddress, this, &RecipientLineNG::slotAddRecipient);
}

void RecipientLineNG::slotEditingFinished()
{
    if (mEdit->text().isEmpty()) {
        Q_EMIT deleteLine(this);
    }
}

void RecipientLineNG::slotAddRecipient(const QString &email)
{
    Q_EMIT addRecipient(this, email);
    slotReturnPressed();
}

void RecipientLineNG::slotTypeModified()
{
    mModified = true;

    Q_EMIT typeModified(this);
}

void RecipientLineNG::analyzeLine(const QString &text)
{
    const QStringList r = KEmailAddress::splitAddressList(text);
    mRecipientsCount = r.count();
    mModified = true;
    Q_EMIT countChanged();
}

int RecipientLineNG::recipientsCount() const
{
    return mRecipientsCount;
}

void RecipientLineNG::setData(const MultiplyingLineData::Ptr &data)
{
    Recipient::Ptr rec = qSharedPointerDynamicCast<Recipient>(data);
    if (rec.isNull()) {
        return;
    }
    mData = rec;
    fieldsFromData();
}

MultiplyingLineData::Ptr RecipientLineNG::data() const
{
    if (isModified()) {
        const_cast<RecipientLineNG *>(this)->dataFromFields();
    }
    return mData;
}

void RecipientLineNG::dataFromFields()
{
    if (!mData) {
        return;
    }
    const QString editStr(mEdit->text());
    QString displayName;
    QString addrSpec;
    QString comment;
    if (KEmailAddress::splitAddress(editStr, displayName, addrSpec, comment) == KEmailAddress::AddressOk) {
        mData->setName(displayName);
    }

    KMime::Types::Mailbox mbox;
    mbox.from7BitString(editStr.toUtf8());
    if (mbox.hasAddress()) {
        mData->setEmail(mbox.addrSpec().asString());
        mData->setName(mbox.name());
    } else {
        mData->setEmail(editStr);
    }
    mData->setType(Recipient::idToType(mCombo->currentIndex()));
    mModified = false;
}

void RecipientLineNG::fieldsFromData()
{
    if (!mData) {
        return;
    }
    mCombo->setCurrentIndex(Recipient::typeToId(mData->type()));
    mEdit->setText(mData->name().isEmpty() ? mData->email() : mData->name() + u" <"_s + mData->email() + u'>');
}

void RecipientLineNG::activate()
{
    mEdit->setFocus();
}

bool RecipientLineNG::isActive() const
{
    return mEdit->hasFocus();
}

bool RecipientLineNG::isEmpty() const
{
    return mEdit->text().isEmpty();
}

bool RecipientLineNG::isModified() const
{
    return mModified || mEdit->isModified();
}

void RecipientLineNG::clearModified()
{
    mModified = false;
    mEdit->setModified(false);
}

int RecipientLineNG::setColumnWidth(int w)
{
    w = qMax(w, mCombo->sizeHint().width());
    mCombo->setFixedWidth(w);
    mCombo->updateGeometry();
    parentWidget()->updateGeometry();
    return w;
}

void RecipientLineNG::fixTabOrder(QWidget *previous)
{
    setTabOrder(previous, mCombo);
    setTabOrder(mCombo, mEdit);
}

QWidget *RecipientLineNG::tabOut() const
{
    return mEdit;
}

void RecipientLineNG::clear()
{
    mRecipientsCount = 0;
    mEdit->clear();
}

bool RecipientLineNG::canDeleteLineEdit() const
{
    return true;
}

void RecipientLineNG::setCompletionMode(KCompletion::CompletionMode mode)
{
    mEdit->setCompletionMode(mode);
}

Recipient::Type RecipientLineNG::recipientType() const
{
    return Recipient::idToType(mCombo->currentIndex());
}

void RecipientLineNG::setRecipientType(Recipient::Type type)
{
    mCombo->setCurrentIndex(Recipient::typeToId(type));
    slotTypeModified();
}

Recipient::Ptr RecipientLineNG::recipient() const
{
    return qSharedPointerDynamicCast<Recipient>(data());
}

void RecipientLineNG::setIcon(const QIcon &icon, const QString &tooltip)
{
    mEdit->setIcon(icon, tooltip);
}

QString RecipientLineNG::rawData() const
{
    return mEdit->text();
}

bool RecipientLineNG::eventFilter(QObject *watched, QEvent *event)
{
    if (watched == mEdit) {
        if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) {
            Q_EMIT activeChanged();
        }
    }

    return false;
}
