#include "opt_statusgeneral.h"

#include "common.h"
#include "priorityvalidator.h"
#include "psiiconset.h"
#include "psioptions.h"
#include "ui_opt_statusgeneral.h"

#include <QComboBox>
#include <QMessageBox>

class OptStatusGeneralUI : public QWidget, public Ui::OptStatusGeneral {
public:
    OptStatusGeneralUI() : QWidget() { setupUi(this); }
};

OptionsTabStatusGeneral::OptionsTabStatusGeneral(QObject *parent) :
    OptionsTab(parent, "status_general", "", tr("General"), tr("General status preferences"))
{
}

OptionsTabStatusGeneral::~OptionsTabStatusGeneral() { }

QWidget *OptionsTabStatusGeneral::widget()
{
    if (w)
        return nullptr;

    w                     = new OptStatusGeneralUI();
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    d->te_sp->removeSoundRecButton(); // Delete sound rec button
    connect(d->lw_presets, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)),
            SLOT(currentItemChanged(QListWidgetItem *, QListWidgetItem *)));
    connect(d->lw_presets, SIGNAL(doubleClicked(const QModelIndex &)), SLOT(presetDoubleClicked(const QModelIndex &)));
    connect(d->pb_spNew, SIGNAL(clicked()), SLOT(newStatusPreset()));
    connect(d->pb_spEdit, SIGNAL(clicked()), SLOT(editStatusPreset()));
    connect(d->pb_spDelete, SIGNAL(clicked()), SLOT(deleteStatusPreset()));
    connect(d->cb_presetsMenus, SIGNAL(currentIndexChanged(int)), SLOT(statusMenusIndexChanged(int)));
    connect(d->bb_selPreset, SIGNAL(accepted()), SLOT(statusPresetAccepted()));
    connect(d->bb_selPreset, SIGNAL(rejected()), SLOT(statusPresetRejected()));

    PriorityValidator *prValidator = new PriorityValidator(d->le_sp_priority);
    d->le_sp_priority->setValidator(prValidator);

    spContextMenu = new QMenu(w);
    spContextMenu->addAction(tr("Edit"), this, SLOT(editStatusPreset()));
    spContextMenu->addAction(tr("Delete"), this, SLOT(deleteStatusPreset()));
    connect(d->lw_presets, SIGNAL(customContextMenuRequested(const QPoint &)), SLOT(showMenuForPreset(const QPoint &)));

    PsiOptions *o        = PsiOptions::instance();
    int         askCount = 6;
    if (!o->getOption("options.ui.menu.status.chat").toBool()) {
        d->ck_askChat->hide();
        askCount--;
    }
    if (!o->getOption("options.ui.menu.status.xa").toBool()) {
        d->ck_askXa->hide();
        askCount--;
    }

    if (askCount != 6) {
        reorderGridLayout(d->gridLayout, askCount == 4 ? 2 : 3); // 4 items in 2 columns look better
    }

    d->pb_spNew->setToolTip(tr("Press this button to create a new status message preset."));
    d->pb_spDelete->setToolTip(tr("Press this button to delete a status message preset."));
    /*TODO d->cb_preset->setToolTip(
        tr("Use this list to select a status message preset"
        " to view or edit in the box to the right. You can"
        " also sort them manually with drag and drop."));*/
    d->te_sp->setToolTip(tr("You may edit the message here for the currently selected"
                            " status message preset in the list to the above."));
    d->cb_sp_status->setToolTip(tr("Use this to choose the status that will be assigned to this preset"));
    d->le_sp_priority->setToolTip(tr("Fill in the priority that will be assigned to this preset."
                                     " If no priority is given, the default account priority will be used."));

    d->ck_askOnline->setToolTip(tr("XMPP allows you to put extended status messages on"
                                   " all status types.  Normally, Psi does not prompt you for"
                                   " an extended message when you set your status to \"online\"."
                                   "  Check this option if you want to have this prompt."));
    // TODO write tooltips for other widgets
    return w;
}

void OptionsTabStatusGeneral::applyOptions()
{
    if (!w)
        return;

    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);
    OptionsTree        *o = PsiOptions::instance();

    if (d->gbSelectedPreset->isEnabled()) {
        statusPresetRejected();
        switchPresetMode(false);
    }
    // Unselect items
    d->lw_presets->setCurrentRow(-1);

    // Delete all presets
    const auto &keys = o->getChildOptionNames("options.status.presets", true, true);
    for (const QString &key : keys) {
        o->removeOption(key, true);
    }

    // Recreate presets considering order from list widget
    for (int i = 0; i < d->lw_presets->count(); i++) {
        QListWidgetItem *item = d->lw_presets->item(i);
        StatusPreset     sp   = presets[item->text()];
        sp.filterStatus();
        sp.toOptions(o);
    }

    o->setOption("options.status.ask-for-message-on-online", d->ck_askOnline->isChecked());
    o->setOption("options.status.ask-for-message-on-chat", d->ck_askChat->isChecked());
    o->setOption("options.status.ask-for-message-on-away", d->ck_askAway->isChecked());
    o->setOption("options.status.ask-for-message-on-xa", d->ck_askXa->isChecked());
    o->setOption("options.status.ask-for-message-on-dnd", d->ck_askDnd->isChecked());
    o->setOption("options.status.ask-for-message-on-offline", d->ck_askOffline->isChecked());

    o->setOption("options.status.presets-in-status-menus",
                 d->cb_presetsMenus->itemData(d->cb_presetsMenus->currentIndex()).toString());

    o->setOption("options.status.show-only-online-offline", d->ck_onlyOnOff->isChecked());
    o->setOption("options.status.show-choose", d->ck_showChoose->isChecked());
    o->setOption("options.status.show-reconnect", d->ck_showReconnect->isChecked());
    o->setOption("options.status.show-edit-presets", d->ck_showEditPresets->isChecked());
}

void OptionsTabStatusGeneral::restoreOptions()
{
    if (!w)
        return;

    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);
    OptionsTree        *o = PsiOptions::instance();

    // TODO: Restore function is calling 3 times! Do something with it! Or maybe it's normal?
    d->lw_presets->clear();
    presets.clear();

    const auto &names = o->mapKeyList("options.status.presets", true);
    for (const QVariant &name : names) {
        StatusPreset sp;
        sp.fromOptions(o, name.toString());
        sp.filterStatus();

        presets[name.toString()] = sp;
#ifdef Q_OS_MAC
        QListWidgetItem *item = new QListWidgetItem(sp.name(), d->lw_presets);
#else
        QListWidgetItem *item
            = new QListWidgetItem(PsiIconset::instance()->status(sp.status()).icon(), sp.name(), d->lw_presets);
#endif
        d->lw_presets->addItem(item);
    }

    cleanupSelectedPresetGroup();
    switchPresetMode(false);

    d->ck_askOnline->setChecked(o->getOption("options.status.ask-for-message-on-online").toBool());
    d->ck_askChat->setChecked(o->getOption("options.status.ask-for-message-on-chat").toBool());
    d->ck_askAway->setChecked(o->getOption("options.status.ask-for-message-on-away").toBool());
    d->ck_askXa->setChecked(o->getOption("options.status.ask-for-message-on-xa").toBool());
    d->ck_askDnd->setChecked(o->getOption("options.status.ask-for-message-on-dnd").toBool());
    d->ck_askOffline->setChecked(o->getOption("options.status.ask-for-message-on-offline").toBool());

    d->cb_presetsMenus->setItemData(0, "submenu");
    d->cb_presetsMenus->setItemData(1, "yes");
    d->cb_presetsMenus->setItemData(2, "no");
    int mode = d->cb_presetsMenus->findData(o->getOption("options.status.presets-in-status-menus").toString());
    d->cb_presetsMenus->setCurrentIndex(mode == -1 ? 0 : mode);

    d->ck_onlyOnOff->setChecked(o->getOption("options.status.show-only-online-offline").toBool());
    d->ck_showChoose->setChecked(o->getOption("options.status.show-choose").toBool());
    d->ck_showReconnect->setChecked(o->getOption("options.status.show-reconnect").toBool());
    d->ck_showEditPresets->setChecked(o->getOption("options.status.show-edit-presets").toBool());
}

void OptionsTabStatusGeneral::setData(PsiCon *, QWidget *parentDialog) { parentWidget = parentDialog; }

void OptionsTabStatusGeneral::currentItemChanged(QListWidgetItem *current, QListWidgetItem * /*previous*/)
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);
    d->pb_spEdit->setEnabled(current != nullptr);
    d->pb_spDelete->setEnabled(current != nullptr);
    if (current)
        loadStatusPreset();
    else
        cleanupSelectedPresetGroup();
}

void OptionsTabStatusGeneral::loadStatusPreset()
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    QListWidgetItem *item = d->lw_presets->currentItem();
    if (!item)
        return;

    StatusPreset preset = presets[item->text()];
    preset.filterStatus();
    d->le_spName->setText(preset.name());
    d->le_spName->home(false);
    d->te_sp->setPlainText(preset.message());
    if (preset.priority().hasValue())
        d->le_sp_priority->setText(QString::number(preset.priority().value()));
    else
        d->le_sp_priority->clear();
    d->cb_sp_status->setStatus(preset.status());
}

void OptionsTabStatusGeneral::saveStatusPreset()
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    QListWidgetItem *item = d->lw_presets->currentItem();
    if (!item)
        return;

    QString oldName = item->text();

    StatusPreset sp;
    sp.setName(d->le_spName->text());
    sp.setMessage(d->te_sp->toPlainText());
    sp.setPriority(d->le_sp_priority->text());
    sp.setStatus(d->cb_sp_status->status());
    sp.filterStatus();
    if (oldName != sp.name()) {
        presets.remove(oldName);
        item->setText(sp.name());
    }
#ifndef Q_OS_MAC
    item->setIcon(PsiIconset::instance()->status(sp.status()).icon());
#endif
    presets[sp.name()] = sp;
}

void OptionsTabStatusGeneral::newStatusPreset()
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    presets[""] = StatusPreset();
    d->lw_presets->addItem("");
    d->lw_presets->setCurrentRow(d->lw_presets->count() - 1);

    loadStatusPreset();

    switchPresetMode(true);
}

void OptionsTabStatusGeneral::deleteStatusPreset()
{
    OptStatusGeneralUI *d       = static_cast<OptStatusGeneralUI *>(w);
    int                 current = d->lw_presets->currentRow();
    if (current == -1)
        return;

    QListWidgetItem *item = d->lw_presets->takeItem(current);
    presets.remove(item->text());

    // select a new entry if possible
    if (d->lw_presets->count() > 0) {
        d->lw_presets->setCurrentRow(d->lw_presets->count() - 1);
    } else
        cleanupSelectedPresetGroup();

    // Emit dataChanged only if we delete existing item and not cancelling creating of new one
    if (!item->text().isEmpty())
        emit dataChanged();

    delete item;
}

void OptionsTabStatusGeneral::statusMenusIndexChanged(int index)
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);
    if (index == -1 || d->cb_presetsMenus->itemData(d->cb_presetsMenus->currentIndex()).toString() == "no")
        d->ck_showEditPresets->setEnabled(false);
    else
        d->ck_showEditPresets->setEnabled(true);
}

void OptionsTabStatusGeneral::showMenuForPreset(const QPoint &point)
{
    OptStatusGeneralUI *d    = static_cast<OptStatusGeneralUI *>(w);
    QListWidgetItem    *item = d->lw_presets->itemAt(point);
    if (item) {
        // paranoia: we must be sure item is selected
        d->lw_presets->setCurrentItem(item);

        // Show menu
        spContextMenu->exec(d->lw_presets->mapToGlobal(point));
    }
}

void OptionsTabStatusGeneral::editStatusPreset()
{
    OptStatusGeneralUI *d       = static_cast<OptStatusGeneralUI *>(w);
    int                 current = d->lw_presets->currentRow();
    if (current == -1)
        return;

    loadStatusPreset();

    switchPresetMode(true);
}

void OptionsTabStatusGeneral::cleanupSelectedPresetGroup()
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);
    d->le_spName->setText("");
    d->te_sp->setPlainText("");
    d->le_sp_priority->setText("");
    d->cb_sp_status->setStatus(XMPP::Status::Online);
}

void OptionsTabStatusGeneral::statusPresetAccepted()
{
    OptStatusGeneralUI *d    = static_cast<OptStatusGeneralUI *>(w);
    QListWidgetItem    *item = d->lw_presets->currentItem();
    if (!item)
        return;

    QString newName = d->le_spName->text();
    if (newName.trimmed().isEmpty()) {
        QMessageBox::critical(parentWidget, tr("Error"), tr("Can't create a blank preset!"));
    } else if (newName != item->text() && presets.contains(newName)) {
        QMessageBox::critical(parentWidget, tr("Error"), tr("You already have a preset with that name!"));
    } else {
        saveStatusPreset();
        switchPresetMode(false);
        loadStatusPreset();
        emit dataChanged();
    }
}

void OptionsTabStatusGeneral::statusPresetRejected()
{
    // TODO almost all functions have this line, GET RID OF IT
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    switchPresetMode(false);
    QListWidgetItem *item = d->lw_presets->currentItem();
    if (!item)
        return;

    if (item->text().isEmpty()) {
        // Cancel creating new item
        deleteStatusPreset();
    } else {
        // Cancel editing, just refresh Selected Preset group for viewing
        loadStatusPreset();
    }
}

void OptionsTabStatusGeneral::switchPresetMode(bool toEdit)
{
    OptStatusGeneralUI *d = static_cast<OptStatusGeneralUI *>(w);

    d->gbSelectedPreset->setEnabled(toEdit);
    d->gbPresets->setEnabled(!toEdit);
    d->gbStatusMenus->setEnabled(!toEdit);
    d->gbPrompts->setEnabled(!toEdit);

    emit enableDlgCommonWidgets(!toEdit);
}

void OptionsTabStatusGeneral::presetDoubleClicked(const QModelIndex & /*index*/) { editStatusPreset(); }
