/*
 * Copyright (c) 2012 Maciej Niedzielski
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

/*
 * Copyright (c) 2014-2016 Isode Limited.
 * All rights reserved.
 * See the COPYING file for more information.
 */

#include <Swift/QtUI/QtHighlightEditor.h>

#include <cassert>

#include <boost/lexical_cast.hpp>

#include <QFileDialog>
#include <QScrollBar>
#include <QTreeWidgetItem>

#include <Swift/Controllers/HighlightManager.cpp>

#include <Swift/QtUI/QtSettingsProvider.h>
#include <Swift/QtUI/QtSwiftUtil.h>
#include <Swift/QtUI/UserSearch/QtSuggestingJIDInput.h>

namespace Swift {

QtHighlightEditor::QtHighlightEditor(QtSettingsProvider* settings, QWidget* parent)
    : QWidget(parent), settings_(settings), previousRow_(-1)
{
    ui_.setupUi(this);

    connect(ui_.listWidget, SIGNAL(currentRowChanged(int)), SLOT(onCurrentRowChanged(int)));

    connect(ui_.newButton, SIGNAL(clicked()), SLOT(onNewButtonClicked()));
    connect(ui_.deleteButton, SIGNAL(clicked()), SLOT(onDeleteButtonClicked()));

    connect(ui_.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(onApplyButtonClick()));
    connect(ui_.buttonBox->button(QDialogButtonBox::Cancel), SIGNAL(clicked()), SLOT(onCancelButtonClick()));
    connect(ui_.buttonBox->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(onOkButtonClick()));
    connect(ui_.buttonBox->button(QDialogButtonBox::RestoreDefaults), SIGNAL(clicked()), SLOT(onResetToDefaultRulesClicked()));

    connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(colorOtherSelect()));
    connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(colorCustomSelect()));

    connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect()));
    connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(soundOtherSelect()));
    connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(soundCustomSelect()));

    /* replace the static line-edit control with the roster autocompleter */
    ui_.dummySenderName->setVisible(false);
    jid_ = new QtSuggestingJIDInput(this, settings);
    ui_.senderName->addWidget(jid_);
    jid_->onUserSelected.connect(boost::bind(&QtHighlightEditor::handleOnUserSelected, this, _1));

    /* handle autocomplete */
    connect(jid_, SIGNAL(textEdited(QString)), SLOT(handleContactSuggestionRequested(QString)));

    /* we need to be notified if any of the state changes so that we can update our textual rule description */
    connect(ui_.chatRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.roomRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.nickIsKeyword, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.allMsgRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.senderRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(jid_, SIGNAL(textChanged(const QString&)), SLOT(widgetClick()));
    connect(ui_.keywordRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.keyword, SIGNAL(textChanged(const QString&)), SLOT(widgetClick()));
    connect(ui_.matchPartialWords, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.matchCase, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.noColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.customColorRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.noSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.defaultSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));
    connect(ui_.customSoundRadio, SIGNAL(clicked()), SLOT(widgetClick()));

    /* allow selection of a custom sound file */
    connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(selectSoundFile()));

    /* allowing reordering items */
    connect(ui_.moveUpButton, SIGNAL(clicked()), this, SLOT(onUpButtonClicked()));
    connect(ui_.moveDownButton, SIGNAL(clicked()), this, SLOT(onDownButtonClicked()));

    setWindowTitle(tr("Highlight Rules"));
}

QtHighlightEditor::~QtHighlightEditor()
{
}

QString QtHighlightEditor::formatShortDescription(const HighlightRule &rule)
{
    const QString chatOrRoom = (rule.getMatchChat() ? tr("chat") : tr("room"));

    std::vector<std::string> senders = rule.getSenders();
    std::vector<std::string> keywords = rule.getKeywords();

    if (senders.empty() && keywords.empty() && !rule.getNickIsKeyword()) {
        return tr("All %1 messages.").arg(chatOrRoom);
    }

    if (rule.getNickIsKeyword()) {
        return tr("All %1 messages that mention my name.").arg(chatOrRoom);
    }

    if (!senders.empty()) {
        return tr("All %1 messages from %2.").arg(chatOrRoom, P2QSTRING(senders[0]));
    }

    if (!keywords.empty()) {
        return tr("All %1 messages mentioning the keyword '%2'.").arg(chatOrRoom, P2QSTRING(keywords[0]));
    }

    return tr("Unknown Rule");
}

void QtHighlightEditor::show()
{
    highlightManager_->loadSettings();

    populateList();

    if (ui_.listWidget->count()) {
        selectRow(0);
    }

    updateResetToDefaultRulesVisibility();

    /* prepare default states */
    widgetClick();

    QWidget::show();
    QWidget::activateWindow();
}

void QtHighlightEditor::setHighlightManager(HighlightManager* highlightManager)
{
    highlightManager_ = highlightManager;
}

void QtHighlightEditor::setContactSuggestions(const std::vector<Contact::ref>& suggestions)
{
    jid_->setSuggestions(suggestions);
}

void QtHighlightEditor::colorOtherSelect()
{
    ui_.foregroundColor->setEnabled(false);
    ui_.backgroundColor->setEnabled(false);
}

void QtHighlightEditor::colorCustomSelect()
{
    ui_.foregroundColor->setEnabled(true);
    ui_.backgroundColor->setEnabled(true);
}

void QtHighlightEditor::soundOtherSelect()
{
    ui_.soundFile->setEnabled(false);
    ui_.soundFileButton->setEnabled(false);
}

void QtHighlightEditor::soundCustomSelect()
{
    ui_.soundFile->setEnabled(true);
    ui_.soundFileButton->setEnabled(true);
}

void QtHighlightEditor::onNewButtonClicked()
{
    int row = getSelectedRow() + 1;
    populateList();
    HighlightRule newRule;
    newRule.setMatchMUC(true);
    highlightManager_->insertRule(row, newRule);
    QListWidgetItem *item = new QListWidgetItem();
    item->setText(formatShortDescription(newRule));
    QFont font;
    font.setItalic(true);
    item->setFont(font);
    ui_.listWidget->insertItem(row, item);
    selectRow(row);
}

void QtHighlightEditor::onDeleteButtonClicked()
{
    int selectedRow = getSelectedRow();
    assert(selectedRow>=0 && selectedRow<ui_.listWidget->count());
    delete ui_.listWidget->takeItem(selectedRow);
    highlightManager_->removeRule(selectedRow);

    if (!ui_.listWidget->count()) {
        disableDialog();
        ui_.deleteButton->setEnabled(false);
    } else {
        if (selectedRow == ui_.listWidget->count()) {
            selectRow(ui_.listWidget->count() - 1);
        } else {
            selectRow(selectedRow);
        }
    }
}

void QtHighlightEditor::moveRowFromTo(int fromRow, int toRow) {
    int verticalScrollAreaPosition = ui_.scrollArea->verticalScrollBar()->value();
    highlightManager_->swapRules(fromRow, toRow);
    populateList();
    selectRow(toRow);
    ui_.scrollArea->verticalScrollBar()->setValue(verticalScrollAreaPosition);
}

void QtHighlightEditor::onUpButtonClicked() {
    const size_t moveFrom = ui_.listWidget->currentRow();
    const size_t moveTo = moveFrom - 1;
    moveRowFromTo(moveFrom, moveTo);
}

void QtHighlightEditor::onDownButtonClicked() {
    const size_t moveFrom = ui_.listWidget->currentRow();
    const size_t moveTo = moveFrom + 1;
    moveRowFromTo(moveFrom, moveTo);
}

void QtHighlightEditor::onCurrentRowChanged(int currentRow)
{
    ui_.deleteButton->setEnabled(currentRow != -1);
    ui_.moveUpButton->setEnabled(currentRow != -1 && currentRow != 0);
    ui_.moveDownButton->setEnabled(currentRow != -1 && currentRow != (ui_.listWidget->count()-1));

    if (previousRow_ != -1) {
        if (ui_.listWidget->count() > previousRow_) {
            QFont font;
            font.setItalic(false);
            ui_.listWidget->item(previousRow_)->setFont(font);
            highlightManager_->setRule(previousRow_, ruleFromDialog());
        }
    }

    if (currentRow != -1) {
        HighlightRule rule = highlightManager_->getRule(currentRow);
        ruleToDialog(rule);
        if (ui_.listWidget->currentItem()) {
            QFont font;
            font.setItalic(true);
            ui_.listWidget->currentItem()->setFont(font);
            ui_.listWidget->currentItem()->setText(formatShortDescription(rule));
        }
    }

    /* grey the dialog if we have nothing selected */
    if (currentRow == -1) {
        disableDialog();
    }

    previousRow_ = currentRow;

    updateResetToDefaultRulesVisibility();
}

void QtHighlightEditor::onApplyButtonClick()
{
    selectRow(getSelectedRow()); /* force save */
    highlightManager_->storeSettings();
}

void QtHighlightEditor::onCancelButtonClick()
{
    close();
}

void QtHighlightEditor::onOkButtonClick()
{
    onApplyButtonClick();
    close();
}

void QtHighlightEditor::setChildWidgetStates()
{
    /* disable appropriate radio button child widgets */

    if (ui_.chatRadio->isChecked()) {
        if (ui_.nickIsKeyword->isChecked()) {
            /* switch to another choice before we disable this button */
            ui_.allMsgRadio->setChecked(true);
        }
        ui_.nickIsKeyword->setEnabled(false);
    } else if (ui_.roomRadio->isChecked()) {
        ui_.nickIsKeyword->setEnabled(true);
    } else { /* chats and rooms */
        ui_.nickIsKeyword->setEnabled(true);
    }

    if (ui_.senderRadio->isChecked()) {
        jid_->setEnabled(true);
    } else {
        jid_->setEnabled(false);
    }

    if (ui_.keywordRadio->isChecked()) {
        ui_.keyword->setEnabled(true);
        ui_.matchPartialWords->setEnabled(true);
        ui_.matchCase->setEnabled(true);
    } else {
        ui_.keyword->setEnabled(false);
        ui_.matchPartialWords->setEnabled(false);
        ui_.matchCase->setEnabled(false);
    }

    if (ui_.chatRadio->isChecked()) {
        ui_.allMsgRadio->setText(tr("Apply to all chat messages"));
    } else {
        ui_.allMsgRadio->setText(tr("Apply to all room messages"));
    }
}

void QtHighlightEditor::widgetClick()
{
    setChildWidgetStates();

    HighlightRule rule = ruleFromDialog();

    if (ui_.listWidget->currentItem()) {
        ui_.listWidget->currentItem()->setText(formatShortDescription(rule));
    }
}

void QtHighlightEditor::disableDialog()
{
    ui_.chatRadio->setEnabled(false);
    ui_.roomRadio->setEnabled(false);
    ui_.allMsgRadio->setEnabled(false);
    ui_.nickIsKeyword->setEnabled(false);
    ui_.senderRadio->setEnabled(false);
    ui_.dummySenderName->setEnabled(false);
    ui_.keywordRadio->setEnabled(false);
    ui_.keyword->setEnabled(false);
    ui_.matchPartialWords->setEnabled(false);
    ui_.matchCase->setEnabled(false);
    ui_.noColorRadio->setEnabled(false);
    ui_.customColorRadio->setEnabled(false);
    ui_.foregroundColor->setEnabled(false);
    ui_.backgroundColor->setEnabled(false);
    ui_.noSoundRadio->setEnabled(false);
    ui_.defaultSoundRadio->setEnabled(false);
    ui_.customSoundRadio->setEnabled(false);
    ui_.soundFile->setEnabled(false);
    ui_.soundFileButton->setEnabled(false);
}

void QtHighlightEditor::handleContactSuggestionRequested(const QString& text)
{
    std::string stdText = Q2PSTRING(text);
    onContactSuggestionsRequested(stdText);
}

void QtHighlightEditor::selectSoundFile()
{
    QString path = QFileDialog::getOpenFileName(this, tr("Select sound file..."), QString(), tr("Sounds (*.wav)"));
    ui_.soundFile->setText(path);
}

void QtHighlightEditor::onResetToDefaultRulesClicked() {
    highlightManager_->resetToDefaultRulesList();
    populateList();
    updateResetToDefaultRulesVisibility();
}

void QtHighlightEditor::handleOnUserSelected(const Contact::ref& contact) {
    /* this might seem like it should be standard behaviour for the suggesting input box, but is not desirable in all cases */
    if (contact->jid.isValid()) {
        jid_->setText(P2QSTRING(contact->jid.toString()));
    } else {
        jid_->setText(P2QSTRING(contact->name));
    }
}

void QtHighlightEditor::populateList()
{
    previousRow_ = -1;
    ui_.listWidget->clear();
    HighlightRulesListPtr rules = highlightManager_->getRules();
    for (size_t i = 0; i < rules->getSize(); ++i) {
        const HighlightRule& rule = rules->getRule(i);
        QListWidgetItem *item = new QListWidgetItem();
        item->setText(formatShortDescription(rule));
        ui_.listWidget->addItem(item);
    }
}

void QtHighlightEditor::selectRow(int row)
{
    for (int i = 0; i < ui_.listWidget->count(); ++i) {
        if (i == row) {
            ui_.listWidget->item(i)->setSelected(true);
            onCurrentRowChanged(i);
        } else {
            ui_.listWidget->item(i)->setSelected(false);
        }
    }
    ui_.listWidget->setCurrentRow(row);
}

int QtHighlightEditor::getSelectedRow() const
{
    for (int i = 0; i < ui_.listWidget->count(); ++i) {
        if (ui_.listWidget->item(i)->isSelected()) {
            return i;
        }
    }
    return -1;
}

HighlightRule QtHighlightEditor::ruleFromDialog()
{
    HighlightRule rule;
    HighlightAction& action = rule.getAction();

    if (ui_.chatRadio->isChecked()) {
        rule.setMatchChat(true);
        rule.setMatchMUC(false);
    } else {
        rule.setMatchChat(false);
        rule.setMatchMUC(true);
    }

    if (ui_.allMsgRadio->isChecked()) {
        action.setHighlightWholeMessage(true);
    }

    if (ui_.senderRadio->isChecked()) {
        QString senderName = jid_->text();
        if (!senderName.isEmpty()) {
            std::vector<std::string> senders;
            senders.push_back(Q2PSTRING(senderName));
            rule.setSenders(senders);
            action.setHighlightWholeMessage(true);
        }
    }

    if (ui_.keywordRadio->isChecked()) {
        QString keywordString = ui_.keyword->text();
        if (!keywordString.isEmpty()) {
            std::vector<std::string> keywords;
            keywords.push_back(Q2PSTRING(keywordString));
            rule.setKeywords(keywords);
        }
    }

    if (ui_.nickIsKeyword->isChecked()) {
        rule.setNickIsKeyword(true);
        rule.setMatchWholeWords(true);
        rule.setMatchCase(true);
    } else {
        rule.setMatchWholeWords(!ui_.matchPartialWords->isChecked());
        rule.setMatchCase(ui_.matchCase->isChecked());
    }

    if (ui_.noColorRadio->isChecked()) {
        action.setTextColor("");
        action.setTextBackground("");
    } else {
        action.setTextColor(Q2PSTRING(ui_.foregroundColor->getColor().name()));
        action.setTextBackground(Q2PSTRING(ui_.backgroundColor->getColor().name()));
    }

    if (ui_.noSoundRadio->isChecked()) {
        action.setPlaySound(false);
    } else if (ui_.defaultSoundRadio->isChecked()) {
        action.setPlaySound(true);
        action.setSoundFile("");
    } else {
        action.setPlaySound(true);
        action.setSoundFile(Q2PSTRING(ui_.soundFile->text()));
    }

    return rule;
}

void QtHighlightEditor::ruleToDialog(const HighlightRule& rule)
{
    ui_.chatRadio->setEnabled(true);
    ui_.roomRadio->setEnabled(true);

    if (rule.getMatchMUC()) {
        ui_.chatRadio->setChecked(false);
        ui_.roomRadio->setChecked(true);
    } else {
        ui_.chatRadio->setChecked(true);
        ui_.roomRadio->setChecked(false);
    }

    ui_.allMsgRadio->setEnabled(true);
    ui_.allMsgRadio->setChecked(true); /* this is the default radio button */
    jid_->setText("");
    ui_.keyword->setText("");
    ui_.matchPartialWords->setChecked(false);
    ui_.matchCase->setChecked(false);

    ui_.nickIsKeyword->setEnabled(true);
    if (rule.getNickIsKeyword()) {
        ui_.nickIsKeyword->setChecked(true);
    }

    ui_.senderRadio->setEnabled(true);
    std::vector<std::string> senders = rule.getSenders();
    if (!senders.empty()) {
        ui_.senderRadio->setChecked(true);
        jid_->setText(P2QSTRING(senders[0]));
    }

    ui_.keywordRadio->setEnabled(true);
    std::vector<std::string> keywords = rule.getKeywords();
    if (!keywords.empty()) {
        ui_.keywordRadio->setChecked(true);
        ui_.keyword->setText(P2QSTRING(keywords[0]));
        ui_.matchPartialWords->setChecked(!rule.getMatchWholeWords());
        ui_.matchCase->setChecked(rule.getMatchCase());
    }

    const HighlightAction& action = rule.getAction();

    ui_.noColorRadio->setEnabled(true);
    ui_.customColorRadio->setEnabled(true);
    if (action.getTextColor().empty() && action.getTextBackground().empty()) {
        ui_.noColorRadio->setChecked(true);
        ui_.foregroundColor->setEnabled(false);
        ui_.backgroundColor->setEnabled(false);
    } else {
        ui_.foregroundColor->setEnabled(true);
        ui_.backgroundColor->setEnabled(true);
        QColor foregroundColor(P2QSTRING(action.getTextColor()));
        ui_.foregroundColor->setColor(foregroundColor);
        QColor backgroundColor(P2QSTRING(action.getTextBackground()));
        ui_.backgroundColor->setColor(backgroundColor);
        ui_.customColorRadio->setChecked(true);
    }

    ui_.noSoundRadio->setEnabled(true);
    ui_.defaultSoundRadio->setEnabled(true);
    ui_.customSoundRadio->setEnabled(true);
    ui_.soundFile->setText("");
    ui_.soundFile->setEnabled(false);
    ui_.soundFileButton->setEnabled(false);
    if (action.playSound()) {
        if (action.getSoundFile().empty()) {
            ui_.defaultSoundRadio->setChecked(true);
        } else {
            ui_.customSoundRadio->setChecked(true);
            ui_.soundFile->setText(P2QSTRING(action.getSoundFile()));
            ui_.soundFile->setEnabled(true);
            ui_.soundFileButton->setEnabled(true);
        }
    } else {
        ui_.noSoundRadio->setChecked(true);
    }

    /* set radio button child option states */
    setChildWidgetStates();
}

void QtHighlightEditor::updateResetToDefaultRulesVisibility() {
    ui_.buttonBox->button(QDialogButtonBox::RestoreDefaults)->setVisible(!highlightManager_->isDefaultRulesList());
}

}