#include "slideshow.h"
#include "imageutils.h"
#include "menuid.h"
#include <qapplication.h>
#include <qpushbutton.h>
#include <kconfig.h>
#include <kglobal.h>
#include <kimageeffect.h>
#include <qpixmap.h>
#include <qimage.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <stdlib.h>
#include <unistd.h>

// takes two images and makes them the same size, resizing and centering on
// the bg color if needed
void centerImages(QImage &img1, QImage &img2, unsigned int bgColor)
{
    int sx, sy, dx, dy, w, h, xoff, yoff;
    QImage tmpImage;
    unsigned int *srcData, *destData;

    if(img1.width() == img2.width() && img1.height() == img2.height())
        return;

    if(img1.width() > img2.width())
        w = img1.width();
    else
        w = img2.width();
    if(img1.height() > img2.height())
        h = img1.height();
    else
        h = img2.height();

    if(img1.width() != w || img1.height() != h){
        tmpImage.create(w, h, 32);
        tmpImage.fill(bgColor);
        xoff = (w-img1.width())/2;
        yoff = (h-img1.height())/2;
        for(sy=0, dy=yoff; sy < img1.height(); ++sy, ++dy){
            srcData = (unsigned int *)img1.scanLine(sy);
            destData = (unsigned int *)tmpImage.scanLine(dy);
            for(sx=0, dx=xoff; sx < img1.width(); ++sx, ++dx){
                destData[dx] = srcData[sx];
            }
        }
        img1 = tmpImage;
        img1.detach();
        tmpImage.reset();
    }
    if(img2.width() != w || img2.height() != h){
        tmpImage.create(w, h, 32);
        tmpImage.fill(bgColor);
        xoff = (w-img2.width())/2;
        yoff = (h-img2.height())/2;
        for(sy=0, dy=yoff; sy < img2.height(); ++sy, ++dy){
            srcData = (unsigned int *)img2.scanLine(sy);
            destData = (unsigned int *)tmpImage.scanLine(dy);
            for(sx=0, dx=xoff; sx < img2.width(); ++sx, ++dx){
                destData[dx] = srcData[sx];
            }
        }
        img2 = tmpImage;
        img2.detach();
        tmpImage.reset();
    }
}

void removeAlpha(QImage &img, unsigned int bgColor)
{
    int x, y;
    unsigned int *data;
    unsigned int pixel;
    float srcPercent, destPercent;
    unsigned char r = qRed(bgColor);
    unsigned char g = qGreen(bgColor);
    unsigned char b = qBlue(bgColor);
    unsigned char alpha;

    for(y=0; y < img.height(); ++y){
        data = (unsigned int *)img.scanLine(y);
        for(x=0; x < img.width(); ++x){
            alpha = qAlpha(data[x]);
            if(alpha == 0)
                data[x] = qRgba(r, g, b, 255);
            else if(alpha != 255){
                pixel = data[x];
                srcPercent =  ((float)alpha)/255.0;
                destPercent = 1.0-srcPercent;
                data[x] = qRgba((unsigned char)((srcPercent*qRed(pixel))+
                                                (destPercent*r)),
                               (unsigned char)((srcPercent*qGreen(pixel))+
                                               (destPercent*g)),
                                (unsigned char)((srcPercent*qBlue(pixel))+
                                                (destPercent*b)),
                                255);
            }
        }
    }
}

// blends two images by percent into dest
void blendImages(QImage &img1, QImage &img2, QImage &dest, float percent)
{
    int x, y;
    unsigned int *srcData1, *srcData2, *destData;
    float destPercent = 1.0-percent;

    for(y=0; y < dest.height(); ++y){
        srcData1 = (unsigned int *)img1.scanLine(y);
        srcData2 = (unsigned int *)img2.scanLine(y);
        destData = (unsigned int *)dest.scanLine(y);
        for(x=0; x < dest.width(); ++x){
            destData[x] = qRgba((int)((percent*qRed(srcData1[x])) +
                                      (destPercent*qRed(srcData2[x]))),
                                (int)((percent*qGreen(srcData1[x])) +
                                      (destPercent*qGreen(srcData2[x]))),
                                (int)((percent*qBlue(srcData1[x])) +
                                      (destPercent*qBlue(srcData2[x]))),
                                255);
        }
    }
}

KIFSlideShow::KIFSlideShow(const QStringList &fileList, int delay,
                           bool maxpect, bool loop, int effectType,
                           int effectDelay,
                           QWidget *parent, const char *name)
    : QWidget(parent, name, WStyle_StaysOnTop | WType_Popup |
          WDestructiveClose | WX11BypassWM)
{
    //connect(&t, SIGNAL(timeout()), this, SLOT(slotTimer()));
    setBackgroundMode(NoBackground);

    gc = XCreateGC(x11Display(),
                   RootWindow(x11Display(), x11Screen()), 0, 0 );

    KConfig *config = KGlobal::config();
    config->setGroup("UISettings");
    c = config->readColorEntry("FullScreenColor", &Qt::white);
    XSetForeground(x11Display(), gc, c.pixel());

    for(int i=0; i < 4; ++i)
        effectFrame[i] = NULL;
    pix = new QPixmap();
    nextPix = NULL;
    img = new QImage();
    nextImg = new QImage();
    max = maxpect;
    effect = effectType;
    effectTime = effectDelay;
    looping = loop;
    time = delay;
    list = fileList;
    move(0, 0);
    resize(QApplication::desktop()->size());
    it = list.begin();
    show();
    slotTimer();
}

KIFSlideShow::~KIFSlideShow()
{
    t.stop();
    XFreeGC(x11Display(), gc);
    for(int i=0; i < 4; ++i){
        if(effectFrame[i])
            delete effectFrame[i];
    }
    if(pix)
        delete pix;
    if(nextPix)
        delete nextPix;
    delete img;
    delete nextImg;
}

void KIFSlideShow::scaleImage(QImage &img)
{
    if(img.width() > width() || img.height() > height()){
        int w = img.width();
        int h = img.height();
        if(w > width()){
            float percent = ((float)width())/w;
            w = (int)(w*percent);
            h = (int)(h*percent);
        }
        if(h > height()){
            float percent = ((float)height())/h;
            w = (int)(w*percent);
            h = (int)(h*percent);
        }
        img = img.smoothScale(w, h);
    }
    else if(max && img.width() < width() && img.height() < height()){
        int w = img.width();
        int h = img.height();
        float percentW, percentH;

        // decide if to scale horizontally or vertically
        percentW = percentH = 0.0;
        if(img.width() < width())
            percentW = ((float)width())/w;
        if(img.height() < height())
            percentH = ((float)height())/h;
        // can we scale width to screensize w/out making height offscreen
        if(percentW && ((int)(h * percentW)) <= height()){
            w = (int)(w*percentW);
            h = (int)(h*percentW);
        }
        // if not try to scale horizontally w/out making width offscreen
        else if(percentH && ((int)(w * percentH)) <= width()){
            w = (int)(w*percentH);
            h = (int)(h*percentH);
        }
        img = img.smoothScale(w, h);
    }
}

void KIFSlideShow::slotTimer()
{
    t.stop();
    int timeout;
    QTime processTime;
    processTime.start();

    if(it == list.end()){
        if(looping)
            it = list.begin();
        else{
            close();
            return;
        }
    }
    if(effect == NoSlideEffectID){
        // no effect, just load image into pixmap
        loadImage(*img, (*it));
        if(!img->isNull()){
            scaleImage(*img);
            pix->convertFromImage(*img);
        }
        else
            pix->resize(0, 0);
        repaint(false);
        ++it;
    }
    else{
        // Okay, there is an effect. Display this image, load up the next,
        // and calculate the next animation while displaying the current one
        if(nextPix){
            QPixmap *tmp = pix;
            for(int i=0; i < 4; ++i){
                if(effectFrame[i]){
                    pix = effectFrame[i];
                    paint(rect());
                    usleep(effectTime*1000);
                }
            }
            pix = tmp;
            *pix = *nextPix;
        }
        else{
            nextPix = new QPixmap();
            loadImage(*img, (*it));
            if(!img->isNull()){
                scaleImage(*img);
                convertImageToPixmap(*img, *pix);
            }
            else{
                pix->resize(0, 0);
                img->create(100, 100, 32);
                img->fill(c.rgb());
            }
        }
        repaint(false);
        ++it;
        if(it == list.end()){
            if(looping)
                it = list.begin();
            else{
                qWarning("Doing early timeout");
                timeout = time*1000-processTime.elapsed();
                if(timeout < 300) // always keep a little delay
                    timeout = 300;
                t.singleShot(timeout, this, SLOT(slotTimer()));
                return;
            }
        }
        loadImage(*nextImg, (*it));
        if(!nextImg->isNull()){
            scaleImage(*nextImg);
            convertImageToPixmap(*nextImg, *nextPix);
        }
        else{
            nextPix->resize(0, 0);
            nextImg->create(100, 100, 32);
            nextImg->fill(c.rgb());
        }

        for(int i=0; i < 4; ++i){
            if(!effectFrame[i])
                effectFrame[i] = new QPixmap();
        }
        if(img->depth() < 32)
            *img = img->convertDepth(32);
        if(nextImg->depth() < 32)
            *nextImg = nextImg->convertDepth(32);
        if(img->hasAlphaBuffer())
            removeAlpha(*img, c.rgb());
        if(nextImg->hasAlphaBuffer())
            removeAlpha(*nextImg, c.rgb());
        if(effect == BlendSlideEffectID){
            centerImages(*img, *nextImg, c.rgb());
            QImage destImg(img->width(), img->height(), 32);
            blendImages(*img, *nextImg, destImg, 0.90);
            convertImageToPixmap(destImg, *effectFrame[0]);
            blendImages(*img, *nextImg, destImg, 0.75);
            convertImageToPixmap(destImg, *effectFrame[1]);
            blendImages(*img, *nextImg, destImg, 0.50);
            convertImageToPixmap(destImg, *effectFrame[2]);
            blendImages(*img, *nextImg, destImg, 0.25);
            convertImageToPixmap(destImg, *effectFrame[3]);
        }
        else if(effect == FadeSlideEffectID){
            QImage destImg(KImageEffect::blend(c, *nextImg, 0.20));
            convertImageToPixmap(destImg, *effectFrame[0]);
            destImg = KImageEffect::blend(c, *nextImg, 0.40);
            convertImageToPixmap(destImg, *effectFrame[1]);
            destImg = KImageEffect::blend(c, *nextImg, 0.60);
            convertImageToPixmap(destImg, *effectFrame[2]);
            destImg = KImageEffect::blend(c, *nextImg, 0.80);
            convertImageToPixmap(destImg, *effectFrame[3]);
        }
        *img = *nextImg;
        img->detach();
        nextImg->reset();
    }
    timeout = time*1000-processTime.elapsed();
    if(timeout < 300) // always keep a little delay
        timeout = 300;
    t.singleShot(timeout, this, SLOT(slotTimer()));
}

void KIFSlideShow::paint(const QRect &rect)
{
    if(pix->isNull()){
        XFillRectangle(x11Display(), winId(),  gc, 0, 0, width(),
                       height());
        return;
    }
    QRect pixRect(0, 0, pix->width(), pix->height());
    QRect drawRect(rect);
    if(pix->width() < width()){
        pixRect.setLeft(((width()-pix->width())/2)-1);
        pixRect.setWidth(pix->width()); // need to reset
    }
    if(pix->height() < height()){
        pixRect.setTop(((height()-pix->height())/2)-1);
        pixRect.setHeight(pix->height());
    }
    pixRect = pixRect.intersect(drawRect);
    if(pix->mask())
        XFillRectangle(x11Display(), winId(), gc, pixRect.x(),
                       pixRect.y(), drawRect.width(), drawRect.height());
    bitBlt(this, pixRect.topLeft(), pix, drawRect, Qt::CopyROP);
    // fill in the edges
    int rects = 0;
    XRectangle rectangles[4];
    if(drawRect.top() < pixRect.top()){
        rectangles[rects].x = drawRect.x();
        rectangles[rects].y = drawRect.x();
        rectangles[rects].width = drawRect.width();
        rectangles[rects].height =  pixRect.top()-drawRect.top();
        ++rects;
    }
    if(drawRect.left() < pixRect.left()){
        rectangles[rects].x = drawRect.x();
        rectangles[rects].y = pixRect.top()-drawRect.top();
        rectangles[rects].width = pixRect.left()-drawRect.left();
        rectangles[rects].height = drawRect.height()-pixRect.top();
        ++rects;
    }
    if(drawRect.right() > pixRect.right()){
        rectangles[rects].x = pixRect.right()+1;
        rectangles[rects].y = pixRect.top()-drawRect.top();
        rectangles[rects].width = drawRect.right()-pixRect.right();
        rectangles[rects].height = drawRect.height()-pixRect.top();
        ++rects;
    }
    if(drawRect.bottom() > pixRect.bottom()){
        rectangles[rects].x = pixRect.left();
        rectangles[rects].y = pixRect.bottom()+1;
        rectangles[rects].width = pixRect.width();
        rectangles[rects].height = drawRect.bottom()-pixRect.bottom();
        ++rects;
    }
    if(rects)
        XFillRectangles(x11Display(), winId(), gc, rectangles, rects);
}

void KIFSlideShow::paintEvent(QPaintEvent *ev)
{
    paint(ev->rect());
}

void KIFSlideShow::closeEvent(QCloseEvent *ev)
{
    t.stop();
    pix->resize(0, 0);
    ev->accept();
}

void KIFSlideShow::mousePressEvent(QMouseEvent *ev)
{
    if(ev->button() == LeftButton){
        close();
    }
}


#include "slideshow.moc"
