// vim: set tabstop=4 shiftwidth=4 expandtab:
/*
Gwenview: an image viewer
Copyright 2012 Aurélien Gâteau <agateau@kde.org>

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
of the License, 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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA.

*/
// Self
#include "cmsprofile.h"

// Local
#include <cms/cmsprofile_png.h>
#include <gvdebug.h>
#include <iodevicejpegsourcemanager.h>
#include <jpegerrormanager.h>

extern "C" {
#include <cms/iccjpeg.h>
}

// KDE
#include <KDebug>

// Qt
#include <QBuffer>

// lcms
#include <lcms2.h>

// X11
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <fixx11h.h>
#include <QX11Info>
#endif

namespace Gwenview
{

#undef ENABLE_LOG
#undef LOG
//#define ENABLE_LOG
#ifdef ENABLE_LOG
#define LOG(x) kDebug() << x
#else
#define LOG(x) ;
#endif

namespace Cms
{

//- JPEG -----------------------------------------------------------------------
static cmsHPROFILE loadFromJpegData(const QByteArray& data)
{
    cmsHPROFILE profile = 0;
    struct jpeg_decompress_struct srcinfo;

    JPEGErrorManager srcErrorManager;
    srcinfo.err = &srcErrorManager;
    jpeg_create_decompress(&srcinfo);
    if (setjmp(srcErrorManager.jmp_buffer)) {
        kError() << "libjpeg error in src\n";
        return 0;
    }

    QBuffer buffer(const_cast<QByteArray*>(&data));
    buffer.open(QIODevice::ReadOnly);
    IODeviceJpegSourceManager::setup(&srcinfo, &buffer);

    setup_read_icc_profile(&srcinfo);
    jpeg_read_header(&srcinfo, true);
    jpeg_start_decompress(&srcinfo);

    uchar* profile_data;
    uint profile_len;
    if (read_icc_profile(&srcinfo, &profile_data, &profile_len)) {
        LOG("Found a profile, length:" << profile_len);
        profile = cmsOpenProfileFromMem(profile_data, profile_len);
    }

    jpeg_destroy_decompress(&srcinfo);

    return profile;
}

//- Profile class --------------------------------------------------------------
struct ProfilePrivate
{
    cmsHPROFILE mProfile;

    void reset()
    {
        if (mProfile) {
            cmsCloseProfile(mProfile);
        }
        mProfile = 0;
    }

    QString readInfo(cmsInfoType info)
    {
        GV_RETURN_VALUE_IF_FAIL(mProfile, QString());
        wchar_t buffer[1024];
        int size = cmsGetProfileInfo(mProfile, info, "en", "US", buffer, 1024);
        return QString::fromWCharArray(buffer, size);
    }
};

Profile::Profile()
: d(new ProfilePrivate)
{
    d->mProfile = 0;
}

Profile::Profile(cmsHPROFILE hProfile)
: d(new ProfilePrivate)
{
    d->mProfile = hProfile;
}

Profile::~Profile()
{
    d->reset();
    delete d;
}

Profile::Ptr Profile::loadFromImageData(const QByteArray& data, const QByteArray& format)
{
    Profile::Ptr ptr;
    cmsHPROFILE hProfile = 0;
    if (format == "png") {
        hProfile = loadFromPngData(data);
    }
    if (format == "jpeg") {
        hProfile = loadFromJpegData(data);
    }
    if (hProfile) {
        ptr = new Profile(hProfile);
    }
    return ptr;
}

Profile::Ptr Profile::loadFromExiv2Image(const Exiv2::Image* image)
{
    Profile::Ptr ptr;
    cmsHPROFILE hProfile = 0;

    const Exiv2::ExifData& exifData = image->exifData();
    Exiv2::ExifKey key("Exif.Image.InterColorProfile");
    Exiv2::ExifData::const_iterator it = exifData.findKey(key);
    if (it == exifData.end()) {
        LOG("No profile found");
        return ptr;
    }
    int size = it->size();
    LOG("size:" << size);

    QByteArray data;
    data.resize(size);
    it->copy(reinterpret_cast<Exiv2::byte*>(data.data()), Exiv2::invalidByteOrder);
    hProfile = cmsOpenProfileFromMem(data, size);

    if (hProfile) {
        ptr = new Profile(hProfile);
    }
    return ptr;
}

cmsHPROFILE Profile::handle() const
{
    return d->mProfile;
}

QString Profile::copyright() const
{
    return d->readInfo(cmsInfoCopyright);
}

QString Profile::description() const
{
    return d->readInfo(cmsInfoDescription);
}

QString Profile::manufacturer() const
{
    return d->readInfo(cmsInfoManufacturer);
}

QString Profile::model() const
{
    return d->readInfo(cmsInfoModel);
}

Profile::Ptr Profile::getMonitorProfile()
{
    cmsHPROFILE hProfile = 0;
    // Get the profile from you config file if the user has set it.
    // if the user allows override through the atom, do this:
#ifdef Q_WS_X11

    // get the current screen...
    int screen = -1;

    Atom type;
    int format;
    unsigned long nitems;
    unsigned long bytes_after;
    quint8 *str;

    static Atom icc_atom = XInternAtom(QX11Info::display(), "_ICC_PROFILE", True);

    if (XGetWindowProperty(QX11Info::display(),
                            QX11Info::appRootWindow(screen),
                            icc_atom,
                            0,
                            INT_MAX,
                            False,
                            XA_CARDINAL,
                            &type,
                            &format,
                            &nitems,
                            &bytes_after,
                            (unsigned char **) &str) == Success
    ) {
        hProfile = cmsOpenProfileFromMem((void*)str, nitems);
    }
#endif
    if (!hProfile) {
        hProfile = cmsCreate_sRGBProfile();
    }
    return Profile::Ptr(new Profile(hProfile));
}

} // namespace Cms

} // namespace Gwenview
