/*
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.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,
 * Boston, MA 02110-1301, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <magick/api.h>

#include <tqfile.h>
#include <tqfileinfo.h>
#include <tqstring.h>

#include <tdeapplication.h>
#include <tdelocale.h>
#include <kurl.h>
#include <tdeio/netaccess.h>

#include <tqcolor.h>

#include "kis_types.h"
#include "kis_global.h"
#include "kis_doc.h"
#include "kis_image.h"
#include "kis_layer.h"
#include "kis_undo_adapter.h"
#include "kis_image_magick_converter.h"
#include "kis_meta_registry.h"
#include "kis_colorspace_factory_registry.h"
#include "kis_iterators_pixel.h"
#include "kis_colorspace.h"
#include "kis_profile.h"
#include "kis_annotation.h"
#include "kis_paint_layer.h"
#include "kis_group_layer.h"
#include "kis_paint_device.h"

#include "../../../config.h"

namespace {

    const TQ_UINT8 PIXEL_BLUE = 0;
    const TQ_UINT8 PIXEL_GREEN = 1;
    const TQ_UINT8 PIXEL_RED = 2;
    const TQ_UINT8 PIXEL_ALPHA = 3;

    static const TQ_UINT8 PIXEL_CYAN = 0;
    static const TQ_UINT8 PIXEL_MAGENTA = 1;
    static const TQ_UINT8 PIXEL_YELLOW = 2;
    static const TQ_UINT8 PIXEL_BLACK = 3;
    static const TQ_UINT8 PIXEL_CMYK_ALPHA = 4;

    static const TQ_UINT8 PIXEL_GRAY = 0;
    static const TQ_UINT8 PIXEL_GRAY_ALPHA = 1;

    /**
     * Make this more flexible -- although... ImageMagick
     * isn't that flexible either.
     */
    TQString getColorSpaceName(ColorspaceType type, unsigned long imageDepth = 8)
    {

        if (type == GRAYColorspace) {
            if (imageDepth == 8)
                return "GRAYA";
            else if ( imageDepth == 16 )
                return "GRAYA16" ;
        }
        else if (type == CMYKColorspace) {
            if (imageDepth == 8)
                return "CMYK";
            else if ( imageDepth == 16 ) {
                return "CMYK16";
            }
        }
        else if (type == LABColorspace) {
            kdDebug(41008) << "Lab!\n";
            return "LABA";
        }
        else if (type == RGBColorspace || type == sRGBColorspace || type == TransparentColorspace) {
            if (imageDepth == 8)
                return "RGBA";
            else if (imageDepth == 16)
                return "RGBA16";
        }
        return "";

    }

    ColorspaceType getColorTypeforColorSpace( KisColorSpace * cs )
    {
        if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") ) return GRAYColorspace;
        if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") ) return RGBColorspace;
        if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") ) return CMYKColorspace;
        if ( cs->id() == KisID("LABA") ) return LABColorspace;

        kdDebug(41008) << "Cannot export images in " + cs->id().name() + " yet.\n";
        return RGBColorspace;

    }

    KisProfile * getProfileForProfileInfo(const Image * image)
    {
#ifndef HAVE_MAGICK6
        return 0;
#else

        if (image->profiles == NULL)
            return  0;

        const char *name;
        const StringInfo *profile;

        KisProfile * p = 0;

        ResetImageProfileIterator(image);
        for (name = GetNextImageProfile(image); name != (char *) NULL; )
        {
            profile = GetImageProfile(image, name);
            if (profile == (StringInfo *) NULL)
                continue;

            // XXX: Hardcoded for icc type -- is that correct for us?
            if (TQString::compare(name, "icc") == 0) {
                TQByteArray rawdata;
                rawdata.resize(profile->length);
                memcpy(rawdata.data(), profile->datum, profile->length);

                p = new KisProfile(rawdata);
                if (p == 0)
                    return 0;
            }
            name = GetNextImageProfile(image);
        }
        return p;
#endif
    }

    void setAnnotationsForImage(const Image * src, KisImageSP image)
    {
#ifndef HAVE_MAGICK6
        return;
#else
        if (src->profiles == NULL)
            return;

        const char *name = 0;
        const StringInfo *profile;
        KisAnnotation* annotation = 0;

        // Profiles and so
        ResetImageProfileIterator(src);
        while((name = GetNextImageProfile(src))) {
            profile = GetImageProfile(src, name);
            if (profile == (StringInfo *) NULL)
                continue;

            // XXX: icc will be written seperately?
            if (TQString::compare(name, "icc") == 0)
                continue;

            TQByteArray rawdata;
            rawdata.resize(profile->length);
            memcpy(rawdata.data(), profile->datum, profile->length);

            annotation = new KisAnnotation(TQString(name), "", rawdata);
            TQ_CHECK_PTR(annotation);

            image -> addAnnotation(annotation);
        }

        // Attributes, since we have no hint on if this is an attribute or a profile
        // annotation, we prefix it with 'chalk_attribute:'. XXX This needs to be rethought!
        // The joys of imagemagick. From at version 6.2.1 (dfaure has 6.2.0 and confirms the
        // old way of doing things) they changed the src -> attributes
        // to void* and require us to use the iterator functions. So we #if around that, *sigh*
#if MagickLibVersion >= 0x621
        const ImageAttribute * attr;
        ResetImageAttributeIterator(src);
        while ( (attr = GetNextImageAttribute(src)) ) {
#else
            ImageAttribute * attr = src -> attributes;
            while (attr) {
#endif
                TQByteArray rawdata;
                int len = strlen(attr -> value) + 1;
                rawdata.resize(len);
                memcpy(rawdata.data(), attr -> value, len);

                annotation = new KisAnnotation(
                    TQString("chalk_attribute:%1").arg(TQString(attr -> key)), "", rawdata);
                TQ_CHECK_PTR(annotation);

                image -> addAnnotation(annotation);
#if MagickLibVersion < 0x620
                attr = attr -> next;
#endif
            }

#endif
        }
    }

    void exportAnnotationsForImage(Image * dst, vKisAnnotationSP_it& it, vKisAnnotationSP_it& annotationsEnd)
    {
#ifndef HAVE_MAGICK6
        return;
#else
        while(it != annotationsEnd) {
            if (!(*it) || (*it) -> type() == TQString()) {
                    kdDebug(41008) << "Warning: empty annotation" << endl;
                    ++it;
                    continue;
            }

            kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;

            if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute
                if (!SetImageAttribute(dst,
                                        (*it) -> type().mid(strlen("chalk_attribute:")).ascii(),
                                        (*it) -> annotation() . data()) ) {
                        kdDebug(41008) << "Storing of attribute " << (*it) -> type() << "failed!\n";
                    }
            } else { // Profile
                    if (!ProfileImage(dst, (*it) -> type().ascii(),
                                    (unsigned char*)(*it) -> annotation() . data(),
                                    (*it) -> annotation() . size(), MagickFalse)) {
                        kdDebug(41008) << "Storing failed!" << endl;
                    }
            }
            ++it;
        }
#endif
    }


    void InitGlobalMagick()
    {
        static bool init = false;

        if (!init) {
            TDEApplication *app = TDEApplication::kApplication();

            InitializeMagick(*app -> argv());
            atexit(DestroyMagick);
            init = true;
        }
    }

    /*
     * ImageMagick progress monitor callback.  Unfortunately it doesn't support passing in some user
     * data which complicates things quite a bit.  The plan was to allow the user start multiple
     * import/scans if he/she so wished.  However, without passing user data it's not possible to tell
     * on which task we have made progress on.
     *
     * Additionally, ImageMagick is thread-safe, not re-entrant... i.e. IM does not relinquish held
     * locks when calling user defined callbacks, this means that the same thread going back into IM
     * would deadlock since it would try to acquire locks it already holds.
     */
#ifdef HAVE_MAGICK6
    MagickBooleanType monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
    {
        TDEApplication *app = TDEApplication::kApplication();

        Q_ASSERT(app);

        if (app -> hasPendingEvents())
            app -> processEvents();

        printf("%s\n", text);
        return MagickTrue;
    }
#else
    unsigned int monitor(const char *text, const ExtendedSignedIntegralType, const ExtendedUnsignedIntegralType, ExceptionInfo *)
    {
        TDEApplication *app = TDEApplication::kApplication();

        Q_ASSERT(app);

        if (app -> hasPendingEvents())
            app -> processEvents();

        printf("%s\n", text);
        return true;
    }
#endif



KisImageMagickConverter::KisImageMagickConverter(KisDoc *doc, KisUndoAdapter *adapter)
{
    InitGlobalMagick();
    init(doc, adapter);
    SetMonitorHandler(monitor);
    m_stop = false;
}

KisImageMagickConverter::~KisImageMagickConverter()
{
}

KisImageBuilder_Result KisImageMagickConverter::decode(const KURL& uri, bool isBlob)
{
    Image *image;
    Image *images;
    ExceptionInfo ei;
    ImageInfo *ii;

    if (m_stop) {
        m_img = 0;
        return KisImageBuilder_RESULT_INTR;
    }

    GetExceptionInfo(&ei);
    ii = CloneImageInfo(0);

    if (isBlob) {

        // TODO : Test.  Does BlobToImage even work?
        Q_ASSERT(uri.isEmpty());
        images = BlobToImage(ii, &m_data[0], m_data.size(), &ei);
    } else {

        tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);

        if (ii -> filename[MaxTextExtent - 1]) {
            emit notifyProgressError();
            return KisImageBuilder_RESULT_PATH;
        }

        images = ReadImage(ii, &ei);

    }

    if (ei.severity != UndefinedException)
        CatchException(&ei);

    if (images == 0) {
        DestroyImageInfo(ii);
        DestroyExceptionInfo(&ei);
        emit notifyProgressError();
        return KisImageBuilder_RESULT_FAILURE;
    }

    emit notifyProgressStage(i18n("Importing..."), 0);

    m_img = 0;

    while ((image = RemoveFirstImageFromList(&images))) {
        ViewInfo *vi = OpenCacheView(image);

        // Determine image depth -- for now, all channels of an imported image are of the same depth
        unsigned long imageDepth = image->depth;
        kdDebug(41008) << "Image depth: " << imageDepth << "\n";

        TQString csName;
        KisColorSpace * cs = 0;
        ColorspaceType colorspaceType;

        // Determine image type -- rgb, grayscale or cmyk
        if (GetImageType(image, &ei) == GrayscaleType || GetImageType(image, &ei) == GrayscaleMatteType) {
            if (imageDepth == 8)
                csName = "GRAYA";
            else if ( imageDepth == 16 )
                csName = "GRAYA16" ;
            colorspaceType = GRAYColorspace;
        }
        else {
            colorspaceType = image->colorspace;
            csName = getColorSpaceName(image -> colorspace, imageDepth);
        }

        kdDebug(41008) << "image has " << csName << " colorspace\n";
        
        KisProfile * profile = getProfileForProfileInfo(image);
        if (profile)
        {
            kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
            cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
        }
        else
            cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");

        if (!cs) {
            kdDebug(41008) << "Chalk does not support colorspace " << image -> colorspace << "\n";
            CloseCacheView(vi);
            DestroyImage(image);
            DestroyExceptionInfo(&ei);
            DestroyImageList(images);
            DestroyImageInfo(ii);
            emit notifyProgressError();
            return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
        }

        if( ! m_img) {
            m_img = new KisImage(m_doc->undoAdapter(), image -> columns, image -> rows, cs, "built image");
            TQ_CHECK_PTR(m_img);
            m_img->blockSignals(true); // Don't send out signals while we're building the image
            
            // XXX I'm assuming seperate layers won't have other profile things like EXIF
            setAnnotationsForImage(image, m_img);
        }

        if (image -> columns && image -> rows) {

            // Opacity (set by the photoshop import filter)
            TQ_UINT8 opacity = OPACITY_OPAQUE;
            const ImageAttribute * attr = GetImageAttribute(image, "[layer-opacity]");
            if (attr != 0) {
                opacity = TQ_UINT8_MAX - Downscale(TQString(attr->value).toInt());
            }

            KisPaintLayerSP layer = 0;

            attr = GetImageAttribute(image, "[layer-name]");
            if (attr != 0) {
                layer = new KisPaintLayer(m_img, attr->value, opacity);
            }
            else {
                layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), opacity);
            }

            Q_ASSERT(layer);

            // Layerlocation  (set by the photoshop import filter)
            TQ_INT32 x_offset = 0;
            TQ_INT32 y_offset = 0;

            attr = GetImageAttribute(image, "[layer-xpos]");
            if (attr != 0) {
                x_offset = TQString(attr->value).toInt();
            }

            attr = GetImageAttribute(image, "[layer-ypos]");
            if (attr != 0) {
                y_offset = TQString(attr->value).toInt();
            }


            for (TQ_UINT32 y = 0; y < image->rows; y ++)
            {
                const PixelPacket *pp = AcquireCacheView(vi, 0, y, image->columns, 1, &ei);

                if(!pp)
                {
                    CloseCacheView(vi);
                    DestroyImageList(images);
                    DestroyImageInfo(ii);
                    DestroyExceptionInfo(&ei);
                    emit notifyProgressError();
                    return KisImageBuilder_RESULT_FAILURE;
                }

                IndexPacket * indexes = GetCacheViewIndexes(vi);

                KisHLineIteratorPixel hiter = layer->paintDevice()->createHLineIterator(0, y, image->columns, true);

                if (colorspaceType== CMYKColorspace) {
                    if (imageDepth == 8) {
                        int x = 0;
                        while (!hiter.isDone())
                        {
                            TQ_UINT8 *ptr= hiter.rawData();
                            *(ptr++) = Downscale(pp->red); // cyan
                            *(ptr++) = Downscale(pp->green); // magenta
                            *(ptr++) = Downscale(pp->blue); // yellow
                            *(ptr++) = Downscale(indexes[x]); // Black
// XXX: Warning! This ifdef messes up the paren matching big-time!
#ifdef HAVE_MAGICK6
                            if (image->matte != MagickFalse) {
#else
                            if (image->matte == true) {
#endif
                                *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);
                            }
                            else {
                                *(ptr++) = OPACITY_OPAQUE;
                            }
                            ++x;
                            pp++;
                            ++hiter;
                        }
                    }
                }
                else if (colorspaceType == LABColorspace) {
                    while(! hiter.isDone())
                    {
                        TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
                        
                        *(ptr++) = ScaleQuantumToShort(pp->red);
                        *(ptr++) = ScaleQuantumToShort(pp->green);
                        *(ptr++) = ScaleQuantumToShort(pp->blue);
                        *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);

                        pp++;
                        ++hiter;
                    }
                }
                else if (colorspaceType == RGBColorspace ||
                             colorspaceType == sRGBColorspace ||
                             colorspaceType == TransparentColorspace)
                    {
                        if (imageDepth == 8) {
                            while(! hiter.isDone())
                            {
                                TQ_UINT8 *ptr= hiter.rawData();
                                // XXX: not colorstrategy and bitdepth independent
                                *(ptr++) = Downscale(pp->blue);
                                *(ptr++) = Downscale(pp->green);
                                *(ptr++) = Downscale(pp->red);
                                *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);

                                pp++;
                                ++hiter;
                            }
                        }
                        else if (imageDepth == 16) {
                            while(! hiter.isDone())
                            {
                                TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
                                // XXX: not colorstrategy independent
                                *(ptr++) = ScaleQuantumToShort(pp->blue);
                                *(ptr++) = ScaleQuantumToShort(pp->green);
                                *(ptr++) = ScaleQuantumToShort(pp->red);
                                *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);

                                pp++;
                                ++hiter;
                            }
                        }
                    }
                    else if ( colorspaceType == GRAYColorspace) {
                        if (imageDepth == 8) {
                            while(! hiter.isDone())
                            {
                                TQ_UINT8 *ptr= hiter.rawData();
                                // XXX: not colorstrategy and bitdepth independent
                                *(ptr++) = Downscale(pp->blue);
                                *(ptr++) = OPACITY_OPAQUE - Downscale(pp->opacity);

                                pp++;
                                ++hiter;
                            }
                        }
                        else if (imageDepth == 16) {
                            while(! hiter.isDone())
                            {
                                TQ_UINT16 *ptr = reinterpret_cast<TQ_UINT16 *>(hiter.rawData());
                                // XXX: not colorstrategy independent
                                *(ptr++) = ScaleQuantumToShort(pp->blue);
                                *(ptr++) = 65535/*OPACITY_OPAQUE*/ - ScaleQuantumToShort(pp->opacity);

                                pp++;
                                ++hiter;
                            }
                        }
                    }

                    emit notifyProgress(y * 100 / image->rows);

                    if (m_stop) {
                        CloseCacheView(vi);
                        DestroyImage(image);
                        DestroyImageList(images);
                        DestroyImageInfo(ii);
                        DestroyExceptionInfo(&ei);
                        m_img = 0;
                        return KisImageBuilder_RESULT_INTR;
                    }
                }
                m_img->addLayer(layer.data(), m_img->rootLayer());
                layer->paintDevice()->move(x_offset, y_offset);
            }

            emit notifyProgressDone();
            CloseCacheView(vi);
            DestroyImage(image);
        }

        emit notifyProgressDone();
        DestroyImageList(images);
        DestroyImageInfo(ii);
        DestroyExceptionInfo(&ei);
        return KisImageBuilder_RESULT_OK;
    }

    KisImageBuilder_Result KisImageMagickConverter::buildImage(const KURL& uri)
    {
        if (uri.isEmpty())
            return KisImageBuilder_RESULT_NO_URI;

        if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
            return KisImageBuilder_RESULT_NOT_EXIST;
        }

        KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
        TQString tmpFile;

        if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
            KURL uriTF;
            uriTF.setPath( tmpFile );
            result = decode(uriTF, false);
            TDEIO::NetAccess::removeTempFile(tmpFile);
        }

        return result;
    }


    KisImageSP KisImageMagickConverter::image()
    {
        return m_img;
    }

    void KisImageMagickConverter::init(KisDoc *doc, KisUndoAdapter *adapter)
    {
        m_doc = doc;
        m_adapter = adapter;
        m_job = 0;
    }

    KisImageBuilder_Result KisImageMagickConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd)
    {
        Image *image;
        ExceptionInfo ei;
        ImageInfo *ii;

        if (!layer)
            return KisImageBuilder_RESULT_INVALID_ARG;

        KisImageSP img = layer->image();
        if (!img)
            return KisImageBuilder_RESULT_EMPTY;

        if (uri.isEmpty())
            return KisImageBuilder_RESULT_NO_URI;

        if (!uri.isLocalFile())
            return KisImageBuilder_RESULT_NOT_LOCAL;


        TQ_UINT32 layerBytesPerChannel = layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();

        GetExceptionInfo(&ei);

        ii = CloneImageInfo(0);

        tqstrncpy(ii -> filename, TQFile::encodeName(uri.path()), MaxTextExtent - 1);

        if (ii -> filename[MaxTextExtent - 1]) {
            emit notifyProgressError();
            return KisImageBuilder_RESULT_PATH;
        }

        if (!img -> width() || !img -> height())
            return KisImageBuilder_RESULT_EMPTY;

        if (layerBytesPerChannel < 2) {
            ii->depth = 8;
        }
        else {
            ii->depth = 16;
        }

        ii->colorspace = getColorTypeforColorSpace(layer->paintDevice()->colorSpace());

        image = AllocateImage(ii);
        SetImageColorspace(image, ii->colorspace);
        image -> columns = img -> width();
        image -> rows = img -> height();

        kdDebug(41008) << "Saving with colorspace " << image->colorspace << ", (" << layer->paintDevice()->colorSpace()->id().name() << ")\n";
        kdDebug(41008) << "IM Image thinks it has depth: " << image->depth << "\n";

#ifdef HAVE_MAGICK6
        //    if ( layer-> hasAlpha() )
        image -> matte = MagickTrue;
        //    else
        //        image -> matte = MagickFalse;
#else
        //    image -> matte = layer -> hasAlpha();
        image -> matte = true;
#endif

        TQ_INT32 y, height, width;

        height = img -> height();
        width = img -> width();

        bool alpha = true;
        TQString ext = TQFileInfo(TQFile::encodeName(uri.path())).extension(false).upper();
        if (ext == "BMP") {
            alpha = false;
            tqstrncpy(ii->magick, "BMP2", MaxTextExtent - 1);
        }
        else if (ext == "RGB") {
            tqstrncpy(ii->magick, "SGI", MaxTextExtent - 1);
        }

        for (y = 0; y < height; y++) {

            // Allocate pixels for this scanline
            PixelPacket * pp = SetImagePixels(image, 0, y, width, 1);

            if (!pp) {
                DestroyExceptionInfo(&ei);
                DestroyImage(image);
                emit notifyProgressError();
                return KisImageBuilder_RESULT_FAILURE;

            }

            KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, y, width, false);
            if (alpha)
                SetImageType(image, TrueColorMatteType);
            else
                SetImageType(image,  TrueColorType);

            if (image->colorspace== CMYKColorspace) {

                IndexPacket * indexes = GetIndexes(image);
                int x = 0;
                if (layerBytesPerChannel == 2) {
                    while (!it.isDone()) {

                        const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
                        pp -> red = ScaleShortToQuantum(d[PIXEL_CYAN]);
                        pp -> green = ScaleShortToQuantum(d[PIXEL_MAGENTA]);
                        pp -> blue = ScaleShortToQuantum(d[PIXEL_YELLOW]);
                        if (alpha)
                            pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_CMYK_ALPHA]);
                        indexes[x] = ScaleShortToQuantum(d[PIXEL_BLACK]);
                        x++;
                        pp++;
                        ++it;
                    }
                }
                else {
                    while (!it.isDone()) {

                        TQ_UINT8 * d = it.rawData();
                        pp -> red = Upscale(d[PIXEL_CYAN]);
                        pp -> green = Upscale(d[PIXEL_MAGENTA]);
                        pp -> blue = Upscale(d[PIXEL_YELLOW]);
                        if (alpha)
                            pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_CMYK_ALPHA]);

                        indexes[x]= Upscale(d[PIXEL_BLACK]);

                        x++;
                        pp++;
                        ++it;
                    }
                }
            }
            else if (image->colorspace== RGBColorspace ||
                     image->colorspace == sRGBColorspace ||
                     image->colorspace == TransparentColorspace)
            {
                if (layerBytesPerChannel == 2) {
                    while (!it.isDone()) {

                        const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
                        pp -> red = ScaleShortToQuantum(d[PIXEL_RED]);
                        pp -> green = ScaleShortToQuantum(d[PIXEL_GREEN]);
                        pp -> blue = ScaleShortToQuantum(d[PIXEL_BLUE]);
                        if (alpha)
                            pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_ALPHA]);

                        pp++;
                        ++it;
                    }
                }
                else {
                    while (!it.isDone()) {

                        TQ_UINT8 * d = it.rawData();
                        pp -> red = Upscale(d[PIXEL_RED]);
                        pp -> green = Upscale(d[PIXEL_GREEN]);
                        pp -> blue = Upscale(d[PIXEL_BLUE]);
                        if (alpha)
                            pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_ALPHA]);

                        pp++;
                        ++it;
                    }
                }
            }
            else if (image->colorspace == GRAYColorspace)
            {
                SetImageType(image, GrayscaleMatteType);
                if (layerBytesPerChannel == 2) {
                    while (!it.isDone()) {

                        const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
                        pp -> red = ScaleShortToQuantum(d[PIXEL_GRAY]);
                        pp -> green = ScaleShortToQuantum(d[PIXEL_GRAY]);
                        pp -> blue = ScaleShortToQuantum(d[PIXEL_GRAY]);
                        if (alpha)
                            pp -> opacity = ScaleShortToQuantum(65535/*OPACITY_OPAQUE*/ - d[PIXEL_GRAY_ALPHA]);

                        pp++;
                        ++it;
                    }
                }
                else {
                    while (!it.isDone()) {
                        TQ_UINT8 * d = it.rawData();
                        pp -> red = Upscale(d[PIXEL_GRAY]);
                        pp -> green = Upscale(d[PIXEL_GRAY]);
                        pp -> blue = Upscale(d[PIXEL_GRAY]);
                        if (alpha)
                            pp -> opacity = Upscale(OPACITY_OPAQUE - d[PIXEL_GRAY_ALPHA]);

                        pp++;
                        ++it;
                    }
                }
            }
            else {
                kdDebug(41008) << "Unsupported image format\n";
                return KisImageBuilder_RESULT_INVALID_ARG;
            }

            emit notifyProgressStage(i18n("Saving..."), y * 100 / height);

#ifdef HAVE_MAGICK6
            if (SyncImagePixels(image) == MagickFalse)
                kdDebug(41008) << "Syncing pixels failed\n";
#else
            if (!SyncImagePixels(image))
                kdDebug(41008) << "Syncing pixels failed\n";
#endif
        }

        // set the annotations
        exportAnnotationsForImage(image, annotationsStart, annotationsEnd);

        // XXX: Write to a temp file, then have Chalk use TDEIO to copy temp
        // image to remote location.

        WriteImage(ii, image);
        DestroyExceptionInfo(&ei);
        DestroyImage(image);
        emit notifyProgressDone();
        return KisImageBuilder_RESULT_OK;
    }

    void KisImageMagickConverter::ioData(TDEIO::Job *job, const TQByteArray& data)
    {
        if (data.isNull() || data.isEmpty()) {
            emit notifyProgressStage(i18n("Loading..."), 0);
            return;
        }

        if (m_data.empty()) {
            Image *image;
            ImageInfo *ii;
            ExceptionInfo ei;

            ii = CloneImageInfo(0);
            GetExceptionInfo(&ei);
            image = PingBlob(ii, data.data(), data.size(), &ei);

            if (image == 0 || ei.severity == BlobError) {
                DestroyExceptionInfo(&ei);
                DestroyImageInfo(ii);
                job -> kill();
                emit notifyProgressError();
                return;
            }

            DestroyImage(image);
            DestroyExceptionInfo(&ei);
            DestroyImageInfo(ii);
            emit notifyProgressStage(i18n("Loading..."), 0);
        }

        Q_ASSERT(data.size() + m_data.size() <= m_size);
        memcpy(&m_data[m_data.size()], data.data(), data.count());
        m_data.resize(m_data.size() + data.count());
        emit notifyProgressStage(i18n("Loading..."), m_data.size() * 100 / m_size);

        if (m_stop)
            job -> kill();
    }

    void KisImageMagickConverter::ioResult(TDEIO::Job *job)
    {
        m_job = 0;

        if (job -> error())
            emit notifyProgressError();

        decode(KURL(), true);
    }

    void KisImageMagickConverter::ioTotalSize(TDEIO::Job * /*job*/, TDEIO::filesize_t size)
    {
        m_size = size;
        m_data.reserve(size);
        emit notifyProgressStage(i18n("Loading..."), 0);
    }

    void KisImageMagickConverter::cancel()
    {
        m_stop = true;
    }

    /**
     * @name readFilters
     * @return Provide a list of file formats the application can read.
     */
    TQString KisImageMagickConverter::readFilters()
    {
        TQString s;
        TQString all;
        TQString name;
        TQString description;
        unsigned long matches;

#ifdef HAVE_MAGICK6
#ifdef HAVE_OLD_GETMAGICKINFOLIST
        const MagickInfo **mi;
        mi = GetMagickInfoList("*", &matches);
#else // HAVE_OLD_GETMAGICKINFOLIST
        ExceptionInfo ei;
        GetExceptionInfo(&ei);
        const MagickInfo **mi;
        mi = GetMagickInfoList("*", &matches, &ei);
        DestroyExceptionInfo(&ei);
#endif // HAVE_OLD_GETMAGICKINFOLIST
#else // HAVE_MAGICK6
        const MagickInfo *mi;
        ExceptionInfo ei;
        GetExceptionInfo(&ei);
        mi = GetMagickInfo("*", &ei);
        DestroyExceptionInfo(&ei);
#endif // HAVE_MAGICK6

        if (!mi)
            return s;

#ifdef HAVE_MAGICK6
        for (unsigned long i = 0; i < matches; i++) {
            const MagickInfo *info = mi[i];
            if (info -> stealth)
                continue;

            if (info -> decoder) {
                name = info -> name;
                description = info -> description;
                kdDebug(41008) << "Found import filter for: " << name << "\n";

                if (!description.isEmpty() && !description.contains('/')) {
                    all += "*." + name.lower() + " *." + name + " ";
                    s += "*." + name.lower() + " *." + name + "|";
                    s += i18n(description.utf8());
                    s += "\n";
                }
            }
        }
#else
        for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
            if (mi -> stealth)
                continue;
            if (mi -> decoder) {
                name = mi -> name;
                description = mi -> description;
                kdDebug(41008) << "Found import filter for: " << name << "\n";

                if (!description.isEmpty() && !description.contains('/')) {
                    all += "*." + name.lower() + " *." + name + " ";
                    s += "*." + name.lower() + " *." + name + "|";
                    s += i18n(description.utf8());
                    s += "\n";
                }
            }
        }
#endif

        all += "|" + i18n("All Images");
        all += "\n";

        return all + s;
    }

    TQString KisImageMagickConverter::writeFilters()
    {
        TQString s;
        TQString all;
        TQString name;
        TQString description;
        unsigned long matches;

#ifdef HAVE_MAGICK6
#ifdef HAVE_OLD_GETMAGICKINFOLIST
        const MagickInfo **mi;
        mi = GetMagickInfoList("*", &matches);
#else // HAVE_OLD_GETMAGICKINFOLIST
        ExceptionInfo ei;
        GetExceptionInfo(&ei);
        const MagickInfo **mi;
        mi = GetMagickInfoList("*", &matches, &ei);
        DestroyExceptionInfo(&ei);
#endif // HAVE_OLD_GETMAGICKINFOLIST
#else // HAVE_MAGICK6
        const MagickInfo *mi;
        ExceptionInfo ei;
        GetExceptionInfo(&ei);
        mi = GetMagickInfo("*", &ei);
        DestroyExceptionInfo(&ei);
#endif // HAVE_MAGICK6

        if (!mi) {
            kdDebug(41008) << "Eek, no magick info!\n";
            return s;
        }

#ifdef HAVE_MAGICK6
        for (unsigned long i = 0; i < matches; i++) {
            const MagickInfo *info = mi[i];
            kdDebug(41008) << "Found export filter for: " << info -> name << "\n";
            if (info -> stealth)
                continue;

            if (info -> encoder) {
                name = info -> name;

                description = info -> description;

                if (!description.isEmpty() && !description.contains('/')) {
                    all += "*." + name.lower() + " *." + name + " ";
                    s += "*." + name.lower() + " *." + name + "|";
                    s += i18n(description.utf8());
                    s += "\n";
                }
            }
        }
#else
        for (; mi; mi = reinterpret_cast<const MagickInfo*>(mi -> next)) {
            kdDebug(41008) << "Found export filter for: " << mi -> name << "\n";
            if (mi -> stealth)
                continue;

            if (mi -> encoder) {
                name = mi -> name;

                description = mi -> description;

                if (!description.isEmpty() && !description.contains('/')) {
                    all += "*." + name.lower() + " *." + name + " ";
                    s += "*." + name.lower() + " *." + name + "|";
                    s += i18n(description.utf8());
                    s += "\n";
                }
            }
        }
#endif


        all += "|" + i18n("All Images");
        all += "\n";

        return all + s;
    }

#include "kis_image_magick_converter.moc"

