/* ============================================================
 *
 * This file is a part of kipi-plugins project
 * http://www.kipi-plugins.org
 *
 * Date        : 2007-10-24
 * Description : XMP workflow status properties settings page.
 *
 * Copyright (C) 2007-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * 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 "xmpproperties.h"
#include "xmpproperties.moc"

// Qt includes

#include <QCheckBox>
#include <QPushButton>
#include <QGridLayout>

// KDE includes

#include <kcombobox.h>
#include <kdialog.h>
#include <kglobal.h>
#include <kiconloader.h>
#include <klineedit.h>
#include <klocale.h>
#include <kseparator.h>
#include <ktextedit.h>
#include <kdebug.h>
#include <kconfiggroup.h>

// LibKExiv2 includes

#include <libkexiv2/kexiv2.h>

// LibKDcraw includes

#include <libkdcraw/squeezedcombobox.h>

// Local includes

#include "altlangstringedit.h"
#include "metadatacheckbox.h"
#include "multivaluesedit.h"
#include "objectattributesedit.h"
#include "pluginsversion.h"

using namespace KDcrawIface;

namespace KIPIMetadataEditPlugin
{

class XMPPropertiesPriv
{
public:

    XMPPropertiesPriv()
    {
        priorityCB           = 0;
        objectTypeCB         = 0;
        priorityCheck        = 0;
        objectAttributeCheck = 0;
        sceneEdit            = 0;
        objectTypeEdit       = 0;
        objectAttributeEdit  = 0;
        objectAttributeCB    = 0;
        languageEdit         = 0;

        sceneCodeMap.insert( "010100", i18n("Headshot") );
        sceneCodeMap.insert( "010200", i18n("Half-length") );
        sceneCodeMap.insert( "010300", i18n("Full-length") );
        sceneCodeMap.insert( "010400", i18nc("Side view of a person", "Profile") );
        sceneCodeMap.insert( "010500", i18n("Rear view") );
        sceneCodeMap.insert( "010600", i18n("Single") );
        sceneCodeMap.insert( "010700", i18n("Couple") );
        sceneCodeMap.insert( "010800", i18n("Two") );
        sceneCodeMap.insert( "010900", i18n("Group") );
        sceneCodeMap.insert( "011000", i18n("General view") );
        sceneCodeMap.insert( "011100", i18n("Panoramic view") );
        sceneCodeMap.insert( "011200", i18n("Aerial view") );
        sceneCodeMap.insert( "011300", i18n("Under-water") );
        sceneCodeMap.insert( "011400", i18n("Night scene") );
        sceneCodeMap.insert( "011500", i18n("Satellite") );
        sceneCodeMap.insert( "011600", i18n("Exterior view") );
        sceneCodeMap.insert( "011700", i18n("Interior view") );
        sceneCodeMap.insert( "011800", i18n("Close-up") );
        sceneCodeMap.insert( "011900", i18n("Action") );
        sceneCodeMap.insert( "012000", i18n("Performing") );
        sceneCodeMap.insert( "012100", i18n("Posing") );
        sceneCodeMap.insert( "012200", i18n("Symbolic") );
        sceneCodeMap.insert( "012300", i18n("Off-beat") );
        sceneCodeMap.insert( "012400", i18n("Movie scene") );

        typeCodeMap.insert( "Advisory",           i18n("Advisory") );
        typeCodeMap.insert( "Alert",              i18n("Alert") );
        typeCodeMap.insert( "Catalog",            i18n("Catalog") );
        typeCodeMap.insert( "Data",               i18n("Data") );
        typeCodeMap.insert( "Document",           i18n("Document") );
        typeCodeMap.insert( "DTD",                i18n("DTD") );
        typeCodeMap.insert( "Maintenance",        i18n("Maintenance") );
        typeCodeMap.insert( "News",               i18n("News") );
        typeCodeMap.insert( "NewsManagementMode", i18n("News Management Mode") );
        typeCodeMap.insert( "Package",            i18n("Package") );
        typeCodeMap.insert( "Schema",             i18n("Schema") );
        typeCodeMap.insert( "Topic",              i18n("Topic") );
        typeCodeMap.insert( "TopicSet",           i18n("Topic Set") );

        // Fill language code map. inspired from KLanguageButton::loadAllLanguages()
        QStringList list = KGlobal::locale()->allLanguagesList();
        list.sort();
        for (QStringList::Iterator it = list.begin(); it != list.end(); ++it)
        {
            QString code = *it;

            // Only get all ISO 639 language code based on 2 characters
            // http://xml.coverpages.org/iso639a.html
            if (code.size() == 2 )
            {
                QString name = KGlobal::locale()->languageCodeToName(code);
                languageCodeMap.insert(code, name);
            }
        }
    }

    typedef QMap<QString, QString>  SceneCodeMap;
    typedef QMap<QString, QString>  TypeCodeMap;
    typedef QMap<QString, QString>  LanguageCodeMap;

    SceneCodeMap                    sceneCodeMap;
    TypeCodeMap                     typeCodeMap;
    LanguageCodeMap                 languageCodeMap;

    KComboBox                      *priorityCB;
    KComboBox                      *objectTypeCB;

    KLineEdit                      *objectAttributeEdit;

    MetadataCheckBox               *priorityCheck;
    MetadataCheckBox               *objectAttributeCheck;

    MultiValuesEdit                *sceneEdit;
    MultiValuesEdit                *objectTypeEdit;
    MultiValuesEdit                *languageEdit;

    SqueezedComboBox               *objectAttributeCB;
};

XMPProperties::XMPProperties(QWidget* parent)
             : QWidget(parent), d(new XMPPropertiesPriv)
{
    QGridLayout* grid = new QGridLayout(this);

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

    d->languageEdit = new MultiValuesEdit(this, i18n("Language:"),
                          i18n("Select here the language of content."));

    QStringList list;
    for (XMPPropertiesPriv::LanguageCodeMap::Iterator it = d->languageCodeMap.begin();
         it != d->languageCodeMap.end(); ++it)
        list.append(QString("%1 - %2").arg(it.key()).arg(it.value()));

    d->languageEdit->setData(list);

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

    d->priorityCheck = new MetadataCheckBox(i18n("Priority:"), this);
    d->priorityCB    = new KComboBox(this);
    d->priorityCB->insertItem(0, i18n("0: None"));
    d->priorityCB->insertItem(1, i18n("1: High"));
    d->priorityCB->insertItem(2, "2");
    d->priorityCB->insertItem(3, "3");
    d->priorityCB->insertItem(4, "4");
    d->priorityCB->insertItem(5, i18n("5: Normal"));
    d->priorityCB->insertItem(6, "6");
    d->priorityCB->insertItem(7, "7");
    d->priorityCB->insertItem(8, i18n("8: Low"));
    d->priorityCB->setWhatsThis(i18n("Select here the editorial urgency of content."));

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

    d->sceneEdit = new MultiValuesEdit(this, i18n("Scene:"),
                       i18n("Select here the scene type of the content."));

    QStringList list2;
    for (XMPPropertiesPriv::SceneCodeMap::Iterator it = d->sceneCodeMap.begin();
         it != d->sceneCodeMap.end(); ++it)
        list2.append(QString("%1 - %2").arg(it.key()).arg(it.value()));

    d->sceneEdit->setData(list2);

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

    d->objectTypeEdit = new MultiValuesEdit(this, i18n("Type:"),
                            i18n("Select here the editorial type of the content."));

    QStringList list3;
    for (XMPPropertiesPriv::TypeCodeMap::Iterator it = d->typeCodeMap.begin();
         it != d->typeCodeMap.end(); ++it)
        list3.append(it.value());

    d->objectTypeEdit->setData(list3);

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

    d->objectAttributeCheck = new MetadataCheckBox(i18n("Attribute:"), this);
    d->objectAttributeCB    = new SqueezedComboBox(this);
    d->objectAttributeEdit  = new KLineEdit(this);
    d->objectAttributeEdit->setClearButtonShown(true);
    d->objectAttributeEdit->setWhatsThis(i18n("Set here the editorial attribute description of the content."));

    d->objectAttributeCB->setWhatsThis(i18n("Select here the editorial attribute of the content."));
    d->objectAttributeCB->addSqueezedItem(QString("001 - ") + i18n("Current"));
    d->objectAttributeCB->addSqueezedItem(QString("002 - ") + i18n("Analysis"));
    d->objectAttributeCB->addSqueezedItem(QString("003 - ") + i18n("Archive material"));
    d->objectAttributeCB->addSqueezedItem(QString("004 - ") + i18n("Background"));
    d->objectAttributeCB->addSqueezedItem(QString("005 - ") + i18n("Feature"));
    d->objectAttributeCB->addSqueezedItem(QString("006 - ") + i18n("Forecast"));
    d->objectAttributeCB->addSqueezedItem(QString("007 - ") + i18n("History"));
    d->objectAttributeCB->addSqueezedItem(QString("008 - ") + i18n("Obituary"));
    d->objectAttributeCB->addSqueezedItem(QString("009 - ") + i18n("Opinion"));
    d->objectAttributeCB->addSqueezedItem(QString("010 - ") + i18n("Polls & Surveys"));
    d->objectAttributeCB->addSqueezedItem(QString("011 - ") + i18nc("Content type", "Profile"));
    d->objectAttributeCB->addSqueezedItem(QString("012 - ") + i18n("Results Listings & Table"));
    d->objectAttributeCB->addSqueezedItem(QString("013 - ") + i18n("Side bar & Supporting information"));
    d->objectAttributeCB->addSqueezedItem(QString("014 - ") + i18n("Summary"));
    d->objectAttributeCB->addSqueezedItem(QString("015 - ") + i18n("Transcript & Verbatim"));
    d->objectAttributeCB->addSqueezedItem(QString("016 - ") + i18n("Interview"));
    d->objectAttributeCB->addSqueezedItem(QString("017 - ") + i18n("From the Scene"));
    d->objectAttributeCB->addSqueezedItem(QString("018 - ") + i18n("Retrospective"));
    d->objectAttributeCB->addSqueezedItem(QString("019 - ") + i18n("Statistics"));
    d->objectAttributeCB->addSqueezedItem(QString("020 - ") + i18n("Update"));
    d->objectAttributeCB->addSqueezedItem(QString("021 - ") + i18n("Wrap-up"));
    d->objectAttributeCB->addSqueezedItem(QString("022 - ") + i18n("Press Release"));

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

    grid->addWidget(d->languageEdit,                        0, 0, 1, 5);
    grid->addWidget(d->priorityCheck,                       1, 0, 1, 1);
    grid->addWidget(d->priorityCB,                          1, 1, 1, 1);
    grid->addWidget(d->sceneEdit,                           2, 0, 1, 5);
    grid->addWidget(d->objectTypeEdit,                      3, 0, 1, 5);
    grid->addWidget(new KSeparator(Qt::Horizontal, this),   4, 0, 1, 5);
    grid->addWidget(d->objectAttributeCheck,                5, 0, 1, 1);
    grid->addWidget(d->objectAttributeCB,                   5, 1, 1, 2);
    grid->addWidget(d->objectAttributeEdit,                 5, 3, 1, 2);
    grid->setRowStretch(6, 10);
    grid->setColumnStretch(4, 10);
    grid->setMargin(0);
    grid->setSpacing(KDialog::spacingHint());

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

    connect(d->priorityCheck, SIGNAL(toggled(bool)),
            d->priorityCB, SLOT(setEnabled(bool)));

    connect(d->objectAttributeCheck, SIGNAL(toggled(bool)),
            d->objectAttributeCB, SLOT(setEnabled(bool)));

    connect(d->objectAttributeCheck, SIGNAL(toggled(bool)),
            d->objectAttributeEdit, SLOT(setEnabled(bool)));

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

    connect(d->languageEdit, SIGNAL(signalModified()),
            this, SIGNAL(signalModified()));

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

    connect(d->sceneEdit, SIGNAL(signalModified()),
            this, SIGNAL(signalModified()));

    connect(d->objectTypeEdit, SIGNAL(signalModified()),
            this, SIGNAL(signalModified()));

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

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

    connect(d->priorityCB, SIGNAL(activated(int)),
            this, SIGNAL(signalModified()));

    connect(d->objectAttributeCB, SIGNAL(activated(int)),
            this, SIGNAL(signalModified()));

    connect(d->objectAttributeEdit, SIGNAL(textChanged(const QString &)),
            this, SIGNAL(signalModified()));
}

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

void XMPProperties::readMetadata(QByteArray& xmpData)
{
    blockSignals(true);
    KExiv2Iface::KExiv2 exiv2Iface;
    exiv2Iface.setXmp(xmpData);

    int         val;
    QString     data;
    QStringList code, list, list2;
    QString     dateStr, timeStr;
    KExiv2Iface::KExiv2::AltLangMap map;

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

    code = exiv2Iface.getXmpTagStringBag("Xmp.dc.language", false);
    for (QStringList::Iterator it = code.begin(); it != code.end(); ++it)
    {
        QStringList data = d->languageEdit->getData();
        QStringList::Iterator it2;
        for (it2 = data.begin(); it2 != data.end(); ++it2)
        {
            if ((*it2).left(2) == (*it))
            {
                list.append(*it2);
                break;
            }
        }
        if (it2 == data.end())
            d->languageEdit->setValid(false);
    }
    d->languageEdit->setValues(list);

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

    d->priorityCB->setCurrentIndex(0);
    d->priorityCheck->setChecked(false);
    data = exiv2Iface.getXmpTagString("Xmp.photoshop.Urgency", false);
    if (!data.isNull())
    {
        val = data.toInt();
        if (val >= 0 && val <= 8)
        {
            d->priorityCB->setCurrentIndex(val);
            d->priorityCheck->setChecked(true);
        }
        else
            d->priorityCheck->setValid(false);
    }
    d->priorityCB->setEnabled(d->priorityCheck->isChecked());

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

    code = exiv2Iface.getXmpTagStringBag("Xmp.iptc.Scene", false);
    for (QStringList::Iterator it = code.begin(); it != code.end(); ++it)
    {
        QStringList data = d->sceneEdit->getData();
        QStringList::Iterator it2;
        for (it2 = data.begin(); it2 != data.end(); ++it2)
        {
            if ((*it2).left(6) == (*it))
            {
                list.append(*it2);
                break;
            }
        }
        if (it2 == data.end())
            d->sceneEdit->setValid(false);
    }
    d->sceneEdit->setValues(list);

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

    code = exiv2Iface.getXmpTagStringBag("Xmp.dc.Type", false);
    for (QStringList::Iterator it3 = code.begin(); it3 != code.end(); ++it3)
    {
        QStringList data = d->objectTypeEdit->getData();
        QStringList::Iterator it4;
        for (it4 = data.begin(); it4 != data.end(); ++it4)
        {
            if ((*it4) == (*it3))
            {
                list2.append(*it4);
                break;
            }
        }
        if (it4 == data.end())
            d->objectTypeEdit->setValid(false);
    }
    d->objectTypeEdit->setValues(list2);

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

    d->objectAttributeCB->setCurrentIndex(0);
    d->objectAttributeEdit->clear();
    d->objectAttributeCheck->setChecked(false);
    data = exiv2Iface.getXmpTagString("Xmp.iptc.IntellectualGenre", false);
    if (!data.isNull())
    {
        QString attrSec = data.section(":", 0, 0);
        if (!attrSec.isEmpty())
        {
            int attr = attrSec.toInt()-1;
            if (attr >= 0 && attr < 23)
            {
                d->objectAttributeCB->setCurrentIndex(attr);
                d->objectAttributeEdit->setText(data.section(":", -1));
                d->objectAttributeCheck->setChecked(true);
            }
            else
                d->objectAttributeCheck->setValid(false);
        }
    }
    d->objectAttributeCB->setEnabled(d->objectAttributeCheck->isChecked());
    d->objectAttributeEdit->setEnabled(d->objectAttributeCheck->isChecked());

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

    blockSignals(false);
}

void XMPProperties::applyMetadata(QByteArray& xmpData)
{
    QStringList oldList, newList;
    KExiv2Iface::KExiv2 exiv2Iface;
    exiv2Iface.setXmp(xmpData);

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

    if (d->languageEdit->getValues(oldList, newList))
    {
        QStringList newCode;

        for (QStringList::Iterator it2 = newList.begin(); it2 != newList.end(); ++it2)
            newCode.append((*it2).left(2));

        exiv2Iface.setXmpTagStringBag("Xmp.dc.language", newCode, false);
    }
    else
    {
        exiv2Iface.removeXmpTag("Xmp.dc.language");
    }

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

    if (d->priorityCheck->isChecked())
        exiv2Iface.setXmpTagString("Xmp.photoshop.Urgency", QString::number(d->priorityCB->currentIndex()));
    else if (d->priorityCheck->isValid())
        exiv2Iface.removeXmpTag("Xmp.photoshop.Urgency");

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

    if (d->sceneEdit->getValues(oldList, newList))
    {
        QStringList newCode;

        for (QStringList::Iterator it2 = newList.begin(); it2 != newList.end(); ++it2)
            newCode.append((*it2).left(6));

        exiv2Iface.setXmpTagStringBag("Xmp.iptc.Scene", newCode, false);
    }
    else
    {
        exiv2Iface.removeXmpTag("Xmp.iptc.Scene");
    }

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

    if (d->objectTypeEdit->getValues(oldList, newList))
        exiv2Iface.setXmpTagStringBag("Xmp.dc.Type", newList, false);
    else
        exiv2Iface.removeXmpTag("Xmp.dc.Type");

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

    if (d->objectAttributeCheck->isChecked())
    {
        QString objectAttribute;
        objectAttribute.sprintf("%3d", d->objectAttributeCB->currentIndex()+1);
        objectAttribute.append(QString(":%1").arg(d->objectAttributeEdit->text()));
        exiv2Iface.setXmpTagString("Xmp.iptc.IntellectualGenre", objectAttribute);
    }
    else if (d->objectAttributeCheck->isValid())
    {
        exiv2Iface.removeXmpTag("Xmp.iptc.IntellectualGenre");
    }

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

    exiv2Iface.setImageProgramId(QString("Kipi-plugins"), QString(kipiplugins_version));

    xmpData = exiv2Iface.getXmp();
}

}  // namespace KIPIMetadataEditPlugin
