/* ============================================================
 *
 * This file is a part of kipi-plugins project
 * http://www.kipi-plugins.org
 *
 * Date        : 2006-10-15
 * Description : common subjects settings page.
 *
 * Copyright (C) 2006-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
 * Copyright (C) 2009      by Andi Clemens <andi dot clemens at gmx dot net>
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation;
 * either version 2, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * ============================================================ */

#include "subjects.h"
#include "subjects.moc"

// Qt includes.

#include <QCheckBox>
#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QGridLayout>
#include <QLabel>
#include <QPushButton>
#include <QRadioButton>
#include <QValidator>

// KDE includes.

#include <kcombobox.h>
#include <kdebug.h>
#include <kdialog.h>
#include <kglobal.h>
#include <khbox.h>
#include <kiconloader.h>
#include <klineedit.h>
#include <klistwidget.h>
#include <klocale.h>
#include <kstandarddirs.h>

// LibKExiv2 includes.

#include <libkexiv2/kexiv2.h>

namespace KIPIMetadataEditPlugin
{

Subjects::Subjects(QWidget* parent)
            : QWidget(parent), d(new SubjectsPriv)
{
    // Load subject codes provided by IPTC/NAA as xml file.
    // See http://www.iptc.org/NewsCodes/nc_ts-table01.php for details.

    KGlobal::dirs()->addResourceDir("iptcschema", KStandardDirs::installPath("data") +
                                                  QString("kipiplugin_metadataedit/data"));
    QString path = KGlobal::dirs()->findResource("iptcschema", "topicset.iptc-subjectcode.xml");

    if (!loadSubjectCodesFromXML(KUrl(path)))
        kDebug( 51001 ) << "Cannot load IPTC/NAA subject codes XML database";

    // --------------------------------------------------------

    // Subject Reference Number only accept digit.
    QRegExp refDigitRx("^[0-9]{8}$");
    QValidator *refValidator = new QRegExpValidator(refDigitRx, this);

    // --------------------------------------------------------

    d->subjectsCheck = new QCheckBox(i18n("Use structured definition of the subject matter:"), this);
    d->optionsBox = new QWidget;
    d->btnGroup   = new QButtonGroup(this);
    d->stdBtn     = new QRadioButton;
    d->customBtn  = new QRadioButton;
    d->refCB      = new KComboBox;

    QLabel *codeLink = new QLabel(i18n("Use standard "
                                          "<b><a href='http://www.iptc.org/NewsCodes'>"
                                              "reference code"
                                          "</a></b>"));
    codeLink->setOpenExternalLinks(true);
    codeLink->setWordWrap(false);

    QLabel *customLabel = new QLabel(i18n("Use custom definition"));

    d->btnGroup->addButton(d->stdBtn,    SubjectsPriv::STANDARD);
    d->btnGroup->addButton(d->customBtn, SubjectsPriv::CUSTOM);
    d->btnGroup->setExclusive(true);
    d->stdBtn->setChecked(true);

    for (SubjectsPriv::SubjectCodesMap::Iterator it = d->subMap.begin();
         it != d->subMap.end(); ++it)
        d->refCB->addItem(it.key());

    // --------------------------------------------------------

    d->iprEdit = new KLineEdit;
    d->iprEdit->setClearButtonShown(true);
    d->iprEdit->setMaxLength(32);

    // --------------------------------------------------------

    d->refEdit = new KLineEdit;
    d->refEdit->setClearButtonShown(true);
    d->refEdit->setValidator(refValidator);
    d->refEdit->setMaxLength(8);

    // --------------------------------------------------------

    d->nameEdit = new KLineEdit;
    d->nameEdit->setClearButtonShown(true);
    d->nameEdit->setMaxLength(64);

    // --------------------------------------------------------

    d->matterEdit = new KLineEdit;
    d->matterEdit->setClearButtonShown(true);
    d->matterEdit->setMaxLength(64);

    // --------------------------------------------------------

    d->detailEdit = new KLineEdit;
    d->detailEdit->setClearButtonShown(true);
    d->detailEdit->setMaxLength(64);

    // --------------------------------------------------------

    d->iprLabel    = new QLabel(i18n("I.P.R:"));
    d->refLabel    = new QLabel(i18n("Reference:"));
    d->nameLabel   = new QLabel(i18n("Name:"));
    d->matterLabel = new QLabel(i18n("Matter:"));
    d->detailLabel = new QLabel(i18n("Detail:"));

    // --------------------------------------------------------

    d->subjectsBox = new KListWidget;
    d->subjectsBox->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);

    d->addSubjectButton = new QPushButton(i18n("&Add"));
    d->delSubjectButton = new QPushButton(i18n("&Delete"));
    d->repSubjectButton = new QPushButton(i18n("&Replace"));
    d->addSubjectButton->setIcon(SmallIcon("list-add"));
    d->delSubjectButton->setIcon(SmallIcon("edit-delete"));
    d->repSubjectButton->setIcon(SmallIcon("view-refresh"));
    d->delSubjectButton->setEnabled(false);
    d->repSubjectButton->setEnabled(false);

    // --------------------------------------------------------

    d->note = new QLabel;
    d->note->setMaximumWidth(150);
    d->note->setOpenExternalLinks(true);
    d->note->setWordWrap(true);
    d->note->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);

    // --------------------------------------------------------

    QGridLayout *optionsBoxLayout = new QGridLayout;
    optionsBoxLayout->addWidget(d->stdBtn,      0, 0, 1, 1);
    optionsBoxLayout->addWidget(codeLink,       0, 1, 1, 2);
    optionsBoxLayout->addWidget(d->refCB,       0, 3, 1, 1);
    optionsBoxLayout->addWidget(d->customBtn,   1, 0, 1, 4);
    optionsBoxLayout->addWidget(customLabel,    1, 1, 1, 4);
    optionsBoxLayout->addWidget(d->iprLabel,    2, 0, 1, 1);
    optionsBoxLayout->addWidget(d->iprEdit,     2, 1, 1, 4);
    optionsBoxLayout->addWidget(d->refLabel,    3, 0, 1, 1);
    optionsBoxLayout->addWidget(d->refEdit,     3, 1, 1, 1);
    optionsBoxLayout->addWidget(d->nameLabel,   4, 0, 1, 1);
    optionsBoxLayout->addWidget(d->nameEdit,    4, 1, 1, 4);
    optionsBoxLayout->addWidget(d->matterLabel, 5, 0, 1, 1);
    optionsBoxLayout->addWidget(d->matterEdit,  5, 1, 1, 4);
    optionsBoxLayout->addWidget(d->detailLabel, 6, 0, 1, 1);
    optionsBoxLayout->addWidget(d->detailEdit,  6, 1, 1, 4);
    optionsBoxLayout->setColumnStretch(4, 10);
    optionsBoxLayout->setMargin(0);
    optionsBoxLayout->setSpacing(KDialog::spacingHint());
    d->optionsBox->setLayout(optionsBoxLayout);

    // --------------------------------------------------------

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setAlignment( Qt::AlignTop );
    mainLayout->addWidget(d->subjectsCheck,    0, 0, 1, 4);
    mainLayout->addWidget(d->optionsBox,       1, 0, 1, 4);
    mainLayout->addWidget(d->subjectsBox,      2, 0, 5, 3);
    mainLayout->addWidget(d->addSubjectButton, 2, 3, 1, 1);
    mainLayout->addWidget(d->delSubjectButton, 3, 3, 1, 1);
    mainLayout->addWidget(d->repSubjectButton, 4, 3, 1, 1);
    mainLayout->addWidget(d->note,             5, 3, 1, 1);
    mainLayout->setRowStretch(6, 10);
    mainLayout->setColumnStretch(2, 1);
    mainLayout->setMargin(0);
    mainLayout->setSpacing(KDialog::spacingHint());
    setLayout(mainLayout);

    // --------------------------------------------------------

    connect(d->subjectsBox, SIGNAL(itemSelectionChanged()),
            this, SLOT(slotSubjectSelectionChanged()));

    connect(d->addSubjectButton, SIGNAL(clicked()),
            this, SLOT(slotAddSubject()));

    connect(d->delSubjectButton, SIGNAL(clicked()),
            this, SLOT(slotDelSubject()));

    connect(d->repSubjectButton, SIGNAL(clicked()),
            this, SLOT(slotRepSubject()));

    connect(d->btnGroup, SIGNAL(buttonReleased(int)),
            this, SLOT(slotEditOptionChanged(int)));

    connect(d->refCB, SIGNAL(activated(int)),
            this, SLOT(slotRefChanged()));

    // --------------------------------------------------------

    connect(d->subjectsCheck, SIGNAL(toggled(bool)),
            this, SLOT(slotSubjectsToggled(bool)));

    // --------------------------------------------------------

    connect(d->subjectsCheck, SIGNAL(toggled(bool)),
            this, SIGNAL(signalModified()));

    connect(d->addSubjectButton, SIGNAL(clicked()),
            this, SIGNAL(signalModified()));

    connect(d->delSubjectButton, SIGNAL(clicked()),
            this, SIGNAL(signalModified()));

    connect(d->repSubjectButton, SIGNAL(clicked()),
            this, SIGNAL(signalModified()));
}

Subjects::~Subjects()
{
    delete d;
}

void Subjects::slotSubjectsToggled(bool b)
{
    d->optionsBox->setEnabled(b);
    d->subjectsBox->setEnabled(b);
    d->addSubjectButton->setEnabled(b);
    d->delSubjectButton->setEnabled(b);
    d->repSubjectButton->setEnabled(b);
    slotEditOptionChanged(d->btnGroup->id(d->btnGroup->checkedButton()));
}

void Subjects::slotEditOptionChanged(int b)
{
    if (b == SubjectsPriv::CUSTOM)
    {
        d->refCB->setEnabled(false);
        d->iprLabel->setEnabled(true);
        d->refLabel->setEnabled(true);
        d->nameLabel->setEnabled(true);
        d->matterLabel->setEnabled(true);
        d->detailLabel->setEnabled(true);
        d->iprEdit->setEnabled(true);
        d->refEdit->setEnabled(true);
        d->nameEdit->setEnabled(true);
        d->matterEdit->setEnabled(true);
        d->detailEdit->setEnabled(true);
    }
    else
    {
        d->refCB->setEnabled(true);
        d->iprLabel->setEnabled(false);
        d->refLabel->setEnabled(false);
        d->nameLabel->setEnabled(false);
        d->matterLabel->setEnabled(false);
        d->detailLabel->setEnabled(false);
        d->iprEdit->setEnabled(false);
        d->refEdit->setEnabled(false);
        d->nameEdit->setEnabled(false);
        d->matterEdit->setEnabled(false);
        d->detailEdit->setEnabled(false);
        slotRefChanged();
    }
}

void Subjects::slotRefChanged()
{
    QString key = d->refCB->currentText();
    QString name, matter, detail;

    for (SubjectsPriv::SubjectCodesMap::Iterator it = d->subMap.begin();
         it != d->subMap.end(); ++it)
    {
        if (key == it.key())
        {
            name   = it.value().name;
            matter = it.value().matter;
            detail = it.value().detail;
        }
    }

    d->refEdit->setText(key);
    d->nameEdit->setText(name);
    d->matterEdit->setText(matter);
    d->detailEdit->setText(detail);
}

QString Subjects::buildSubject() const
{
    QString subject = d->iprEdit->text();
    subject.append(":");
    subject.append(d->refEdit->text());
    subject.append(":");
    subject.append(d->nameEdit->text());
    subject.append(":");
    subject.append(d->matterEdit->text());
    subject.append(":");
    subject.append(d->detailEdit->text());
    return subject;
}

void Subjects::slotDelSubject()
{
    QListWidgetItem *item = d->subjectsBox->currentItem();
    if (!item) return;
    d->subjectsBox->takeItem(d->subjectsBox->row(item));
    delete item;
}

void Subjects::slotRepSubject()
{
    QString newSubject = buildSubject();
    if (newSubject.isEmpty()) return;

    if (!d->subjectsBox->selectedItems().isEmpty())
    {
        d->subjectsBox->selectedItems()[0]->setText(newSubject);
        d->iprEdit->clear();
        d->refEdit->clear();
        d->nameEdit->clear();
        d->matterEdit->clear();
        d->detailEdit->clear();
    }
}

void Subjects::slotSubjectSelectionChanged()
{
    if (!d->subjectsBox->selectedItems().isEmpty())
    {
        QString subject = d->subjectsBox->selectedItems()[0]->text();
        d->iprEdit->setText(subject.section(':', 0, 0));
        d->refEdit->setText(subject.section(':', 1, 1));
        d->nameEdit->setText(subject.section(':', 2, 2));
        d->matterEdit->setText(subject.section(':', 3, 3));
        d->detailEdit->setText(subject.section(':', 4, 4));
        d->delSubjectButton->setEnabled(true);
        d->repSubjectButton->setEnabled(true);
    }
    else
    {
        d->delSubjectButton->setEnabled(false);
        d->repSubjectButton->setEnabled(false);
    }
}

void Subjects::slotAddSubject()
{
    QString newSubject = buildSubject();
    if (newSubject.isEmpty()) return;

    bool found = false;
    for (int i = 0 ; i < d->subjectsBox->count(); i++)
    {
        QListWidgetItem *item = d->subjectsBox->item(i);
        if (newSubject == item->text())
        {
            found = true;
            break;
        }
    }

    if (!found)
    {
        d->subjectsBox->insertItem(d->subjectsBox->count(), newSubject);
        d->iprEdit->clear();
        d->refEdit->clear();
        d->nameEdit->clear();
        d->matterEdit->clear();
        d->detailEdit->clear();
    }
}

bool Subjects::loadSubjectCodesFromXML(const KUrl& url)
{
    QFile xmlfile(url.path());

    if (!xmlfile.open(QIODevice::ReadOnly))
        return false;

    QDomDocument xmlDoc("NewsML");
    if (!xmlDoc.setContent(&xmlfile))
        return false;

    QDomElement xmlDocElem = xmlDoc.documentElement();
    if (xmlDocElem.tagName()!="NewsML")
        return false;

    for (QDomNode nbE1 = xmlDocElem.firstChild();
         !nbE1.isNull(); nbE1 = nbE1.nextSibling())
    {
        QDomElement newsItemElement = nbE1.toElement();
        if (newsItemElement.isNull()) continue;
        if (newsItemElement.tagName() != "NewsItem") continue;

        for (QDomNode nbE2 = newsItemElement.firstChild();
            !nbE2.isNull(); nbE2 = nbE2.nextSibling())
        {
            QDomElement topicSetElement = nbE2.toElement();
            if (topicSetElement.isNull()) continue;
            if (topicSetElement.tagName() != "TopicSet") continue;

            for (QDomNode nbE3 = topicSetElement.firstChild();
                !nbE3.isNull(); nbE3 = nbE3.nextSibling())
            {
                QDomElement topicElement = nbE3.toElement();
                if (topicElement.isNull()) continue;
                if (topicElement.tagName() != "Topic") continue;

                QString type, name, matter, detail, ref;
                for (QDomNode nbE4 = topicElement.firstChild();
                    !nbE4.isNull(); nbE4 = nbE4.nextSibling())
                {
                    QDomElement topicSubElement = nbE4.toElement();
                    if (topicSubElement.isNull()) continue;

                    if (topicSubElement.tagName() == "TopicType")
                        type = topicSubElement.attribute("FormalName");

                    if (topicSubElement.tagName() == "FormalName")
                        ref = topicSubElement.text();

                    if (topicSubElement.tagName() == "Description" &&
                        topicSubElement.attribute("Variant") == "Name")
                    {
                        if (type == "Subject")
                            name = topicSubElement.text();
                        else if (type == "SubjectMatter")
                            matter = topicSubElement.text();
                        else if (type == "SubjectDetail")
                            detail = topicSubElement.text();
                    }
                }

                d->subMap.insert(ref, SubjectData(name, matter, detail));
            }
        }
    }

    // Set the Subject Name everywhere on the map.

    for (SubjectsPriv::SubjectCodesMap::Iterator it = d->subMap.begin();
         it != d->subMap.end(); ++it)
    {
        QString name, keyPrefix;
        if (it.key().endsWith("00000"))
        {
            keyPrefix = it.key().left(3);
            name      = it.value().name;

            for (SubjectsPriv::SubjectCodesMap::Iterator it2 = d->subMap.begin();
                it2 != d->subMap.end(); ++it2)
            {
                if (it2.key().startsWith(keyPrefix) &&
                    !it2.key().endsWith("00000"))
                {
                    it2.value().name = name;
                }
            }
        }
    }

    // Set the Subject Matter Name everywhere on the map.

    for (SubjectsPriv::SubjectCodesMap::Iterator it = d->subMap.begin();
         it != d->subMap.end(); ++it)
    {
        QString matter, keyPrefix;
        if (it.key().endsWith("000"))
        {
            keyPrefix = it.key().left(5);
            matter    = it.value().matter;

            for (SubjectsPriv::SubjectCodesMap::Iterator it2 = d->subMap.begin();
                it2 != d->subMap.end(); ++it2)
            {
                if (it2.key().startsWith(keyPrefix) &&
                    !it2.key().endsWith("000"))
                {
                    it2.value().matter = matter;
                }
            }
        }
    }

    return true;
}

}  // namespace KIPIMetadataEditPlugin
