/*********
*
* This file is part of BibleTime's source code, http://www.bibletime.info/.
*
* Copyright 1999-2011 by the BibleTime developers.
* The BibleTime source code is licensed under the GNU General Public License version 2.0.
*
**********/

#include "frontend/searchdialog/crangechooserdialog.h"

#include <QDialogButtonBox>
#include <QFrame>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
#include "backend/config/cbtconfig.h"
#include "util/dialogutil.h"

// Sword includes:
#include "versekey.h"
#include "listkey.h"

namespace Search {

CRangeChooserDialog::CRangeChooserDialog(QWidget *parentDialog)
    : QDialog(parentDialog)
{
    initView();
    initConnections();

    retranslateUi();

    // Add the existing scopes
    CBTConfig::StringMap map = CBTConfig::get(CBTConfig::searchScopes);
    CBTConfig::StringMap::Iterator it;
    for (it = map.begin(); it != map.end(); ++it) {
        new RangeItem(it.key(), it.value(), m_rangeList);
    }
    resetEditControls();
}

void CRangeChooserDialog::initView() {
    m_rangeList = new QListWidget(this);
    m_rangeListLabel = new QLabel(this);
    m_rangeListLabel->setBuddy(m_rangeList);

    m_newRangeButton = new QPushButton(this);
    m_deleteRangeButton = new QPushButton(this);

    m_nameEdit = new QLineEdit(this);
    m_nameEditLabel = new QLabel(this);
    m_nameEditLabel->setBuddy(m_nameEdit);

    m_rangeEdit = new QTextEdit(this);
    m_rangeEditLabel = new QLabel(this);
    m_rangeEditLabel->setBuddy(m_rangeEdit);

    m_resultList = new QListWidget(this);
    m_resultListLabel = new QLabel(this);
    m_resultListLabel->setBuddy(m_resultList);

    QFrame *hSeparatorLine = new QFrame(this);
    hSeparatorLine->setFrameShape(QFrame::HLine);
    hSeparatorLine->setFrameShadow(QFrame::Sunken);

    m_buttonBox = new QDialogButtonBox(this);
    m_buttonBox->setOrientation(Qt::Horizontal);
    m_buttonBox->setStandardButtons(QDialogButtonBox::Ok
                                    | QDialogButtonBox::Cancel
                                    | QDialogButtonBox::RestoreDefaults);
    util::prepareDialogBox(m_buttonBox);

    QHBoxLayout *rangeButtonsLayout = new QHBoxLayout();
    rangeButtonsLayout->addWidget(m_newRangeButton);
    rangeButtonsLayout->addWidget(m_deleteRangeButton);

    QVBoxLayout* rangeListLayout = new QVBoxLayout;
    rangeListLayout->addWidget(m_rangeListLabel);
    rangeListLayout->addWidget(m_rangeList);
    rangeListLayout->addLayout(rangeButtonsLayout);

    QHBoxLayout* nameEditLayout = new QHBoxLayout();
    nameEditLayout->addWidget(m_nameEditLabel);
    nameEditLayout->addWidget(m_nameEdit);

    QVBoxLayout* rangeEditLayout = new QVBoxLayout();
    rangeEditLayout->addLayout(nameEditLayout);
    rangeEditLayout->addWidget(m_rangeEditLabel);
    rangeEditLayout->addWidget(m_rangeEdit);
    rangeEditLayout->addWidget(m_resultListLabel);
    rangeEditLayout->addWidget(m_resultList);

    QHBoxLayout *topLayout = new QHBoxLayout;
    topLayout->addLayout(rangeListLayout);
    topLayout->addLayout(rangeEditLayout);

    QVBoxLayout *vboxLayout = new QVBoxLayout(this);
    vboxLayout->addLayout(topLayout);
    vboxLayout->addWidget(hSeparatorLine);
    vboxLayout->addWidget(m_buttonBox);
}

void CRangeChooserDialog::initConnections() {
    connect(m_rangeList, SIGNAL(currentItemChanged(QListWidgetItem*,
                                                   QListWidgetItem*)),
            this,        SLOT(selectedRangeChanged(QListWidgetItem*,
                                                   QListWidgetItem*)));
    connect(m_nameEdit, SIGNAL(textEdited(QString)),
            this,       SLOT(nameEditTextChanged(QString)));
    connect(m_rangeEdit, SIGNAL(textChanged()),
           this,        SLOT(updateResultList()));

    // Connect buttons:
    connect(m_buttonBox, SIGNAL(accepted()),
            this,        SLOT(accept()));
    connect(m_buttonBox, SIGNAL(rejected()),
            this,        SLOT(reject()));
    connect(m_newRangeButton, SIGNAL(clicked()),
            this,             SLOT(addNewRange()));
    connect(m_deleteRangeButton, SIGNAL(clicked()),
            this,                SLOT(deleteCurrentRange()));
    QPushButton * defaultsButton = m_buttonBox->button(QDialogButtonBox::RestoreDefaults);
    connect(defaultsButton, SIGNAL(clicked()),
            this,           SLOT(restoreDefaults()));
}

void CRangeChooserDialog::retranslateUi() {
    setWindowTitle(tr("Setup Search Scopes"));

    m_rangeListLabel->setText(tr("S&earch range:"));
    m_rangeList->setToolTip(tr("Select a scope from the list to edit the search"
                               "ranges"));

    m_newRangeButton->setText(tr("&Add new scope"));
    m_newRangeButton->setToolTip(tr("Add a new search scope. First enter an "
                                    "appropriate name, then edit the search "
                                    "ranges."));

    m_deleteRangeButton->setText(tr("Delete current &scope"));
    m_deleteRangeButton->setToolTip(tr("Delete the selected search scope"));

    m_nameEditLabel->setText(tr("&Name:"));
    m_nameEdit->setToolTip(tr("Change the name of the selected search scope"));

    m_rangeEditLabel->setText(tr("Edi&t current range:"));
    m_rangeEdit->setToolTip(tr("Change the search ranges of the selected search"
                               "scope item. Have a look at the predefined "
                               "search scopes to see how search ranges are "
                               "constructed."));

    m_resultListLabel->setText(tr("Parsed search range:"));
    m_resultList->setToolTip(tr("The search ranges which will be used for the "
                                "search, parsed to the canonical form"));
}

void CRangeChooserDialog::saveCurrentToRange(RangeItem * i) {
    if (!m_nameEdit->text().isEmpty())
        i->setCaption(m_nameEdit->text());

    i->setRange(m_rangeEdit->toPlainText());
}

void CRangeChooserDialog::addNewRange() {
    static const QString nullStr;
    RangeItem * const i = new RangeItem(tr("New range"), nullStr, m_rangeList);
    m_rangeList->setCurrentItem(i);
    resetEditControls();
}

void CRangeChooserDialog::selectedRangeChanged(QListWidgetItem * current,
                                               QListWidgetItem * previous)
{
    if (previous) {
        Q_ASSERT(dynamic_cast<RangeItem*>(previous) != 0);
        saveCurrentToRange(static_cast<RangeItem*>(previous));
    }

    resetEditControls();
}

void CRangeChooserDialog::resetEditControls() {
    const QListWidgetItem * const item = m_rangeList->currentItem();
    Q_ASSERT(item == 0 || dynamic_cast<const RangeItem *>(item) != 0);
    const RangeItem * rangeItem = static_cast<const RangeItem *>(item);

    m_nameEdit->setEnabled(item != 0);
    m_rangeEdit->setEnabled(item != 0);
    m_resultList->setEnabled(item != 0);
    m_deleteRangeButton->setEnabled(item != 0);
    m_nameEdit->setText(item != 0 ? rangeItem->caption() : "");
    m_rangeEdit->setText(item != 0 ? rangeItem->range() : "");

    if (item != 0)
        m_nameEdit->setFocus();

    nameEditTextChanged(item != 0 ? rangeItem->caption() : "");
}

void CRangeChooserDialog::updateResultList() {
    typedef sword::VerseKey VK;

    m_resultList->clear();

    /// \todo remove this hack:
    //HACK: repair range to work with Sword 1.5.6
    const QString range = m_rangeEdit->toPlainText().replace(QRegExp("\\s{0,}-\\s{0,}"), "-");

    sword::ListKey verses = VK().ParseVerseList(range.toUtf8().constData(),
                                                "Genesis 1:1", true);
    for (int i = 0; i < verses.Count(); ++i) {
        new QListWidgetItem(QString::fromUtf8(verses.getElement(i)->getRangeText()),
                            m_resultList);
    }
}

void CRangeChooserDialog::deleteCurrentRange() {
    Q_ASSERT(dynamic_cast<RangeItem*>(m_rangeList->currentItem()) != 0);
    QListWidgetItem *i = m_rangeList->currentItem();
    m_rangeList->removeItemWidget(i);
    delete i;

    resetEditControls();
}

void CRangeChooserDialog::accept() {
    // Update the active item:
    QListWidgetItem *currentItem = m_rangeList->currentItem();
    if (currentItem != 0) {
        Q_ASSERT(dynamic_cast<RangeItem*>(currentItem) != 0);
        saveCurrentToRange(static_cast<RangeItem*>(currentItem));
    }

    // Save the new sorted map of search scopes:
    m_rangeList->sortItems();
    CBTConfig::StringMap map;
    for (int i = 0; i < m_rangeList->count(); i++) {
        Q_ASSERT(dynamic_cast<RangeItem*>(m_rangeList->item(i)) != 0);
        const RangeItem * item = static_cast<RangeItem*>(m_rangeList->item(i));
        map[item->caption()] = item->range();
    }
    CBTConfig::set(CBTConfig::searchScopes, map);

    QDialog::accept();
}

void CRangeChooserDialog::restoreDefaults() {
    typedef CBTConfig::StringMap::ConstIterator SMCI;

    m_rangeList->clear();
    const CBTConfig::StringMap map = CBTConfig::getDefault(CBTConfig::searchScopes);
    for (SMCI it = map.begin(); it != map.end(); ++it) {
        new RangeItem(it.key(), it.value(), m_rangeList);
    };
    m_rangeList->setCurrentItem(0);
    resetEditControls();
}

void CRangeChooserDialog::nameEditTextChanged(const QString &newText) {
    const bool e = !newText.isEmpty() || m_rangeList->count() <= 0;
    m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(e);
}

} //end of namespace Search
