/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 * 
 * Date        : 2004-08-17
 * Description : a widget to draw an image clip region.
 * 
 * Copyright (C) 2004-2008 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.
 * 
 * ============================================================ */

// C++ includes.

#include <cmath>

// TQt includes.

#include <tqpainter.h>
#include <tqpixmap.h>
#include <tqtimer.h>
#include <tqpainter.h>
#include <tqpen.h>
#include <tqimage.h>
#include <tqbrush.h>
#include <tqfont.h> 
#include <tqfontmetrics.h> 
#include <tqpointarray.h>

// KDE includes.

#include <kstandarddirs.h>
#include <kcursor.h>
#include <kglobal.h>
#include <kapplication.h>

// Local includes.

#include "ddebug.h"
#include "imageiface.h"
#include "imageregionwidget.h"
#include "imageregionwidget.moc"

namespace Digikam
{

class ImageRegionWidgetPriv
{

public:

    ImageRegionWidgetPriv()
    {
        iface        = 0;
        separateView = ImageRegionWidget::SeparateViewVertical;
    }

    int          separateView;
    int          xpos;
    int          ypos;

    TQPixmap      pixmapRegion;          // Pixmap of current region to render.
    
    TQPointArray  hightlightPoints;
    
    DImg         image;                 // Entire content image to render pixmap.
    
    ImageIface  *iface;
};

ImageRegionWidget::ImageRegionWidget(int wp, int hp, TQWidget *parent, bool scrollBar)
                 : PreviewWidget(parent)
{
    d = new ImageRegionWidgetPriv;
    d->iface = new ImageIface(0, 0);
    d->image = d->iface->getOriginalImg()->copy(); 

    setMinimumSize(wp, hp);
    setBackgroundColor(colorGroup().background());

    if( !scrollBar ) 
    {
       setVScrollBarMode( TQScrollView::AlwaysOff );
       setHScrollBarMode( TQScrollView::AlwaysOff );
    }
    
    connect(this, TQT_SIGNAL(signalZoomFactorChanged(double)),
            this, TQT_SLOT(slotZoomFactorChanged()));
}

ImageRegionWidget::~ImageRegionWidget()
{
    if (d->iface) delete d->iface;
    delete d;
}

void ImageRegionWidget::resizeEvent(TQResizeEvent* e)
{
    if (!e) return;

    TQScrollView::resizeEvent(e);

    // NOTE: We will always adapt the min. zoom factor to the visible size of canvas
 
    double srcWidth  = previewWidth();
    double srcHeight = previewHeight();
    double dstWidth  = contentsRect().width();
    double dstHeight = contentsRect().height();
    double zoom      = TQMAX(dstWidth/srcWidth, dstHeight/srcHeight); 

    setZoomMin(zoom);
    setZoomMax(zoom*12.0);
    setZoomFactor(zoom);
}

int ImageRegionWidget::previewWidth()
{
    return d->image.width();
}

int ImageRegionWidget::previewHeight()
{
    return d->image.height();
}

bool ImageRegionWidget::previewIsNull()
{
    return d->image.isNull();
}

void ImageRegionWidget::resetPreview()
{
    d->image.reset();
}

void ImageRegionWidget::paintPreview(TQPixmap *pix, int sx, int sy, int sw, int sh)
{
    DImg img = d->image.smoothScaleSection(sx, sy, sw, sh, tileSize(), tileSize());    
    TQPixmap pix2 = d->iface->convertToPixmap(img);
    bitBlt(pix, 0, 0, &pix2, 0, 0);
}

void ImageRegionWidget::setHighLightPoints(const TQPointArray& pointsList)
{
    d->hightlightPoints = pointsList;
    repaintContents(false);   
}

void ImageRegionWidget::slotZoomFactorChanged()
{
    emit signalContentsMovedEvent(true);
}

void ImageRegionWidget::slotSeparateViewToggled(int mode)
{
    d->separateView = mode;
    updateContentsSize();
    slotZoomFactorChanged(); 
}

TQRect ImageRegionWidget::getImageRegion()
{
    TQRect region;    

    switch (d->separateView)
    {
        case SeparateViewVertical:
        case SeparateViewHorizontal:
        case SeparateViewNone:
            region = TQRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()); 
            break;
        case SeparateViewDuplicateVert:
            region = TQRect(contentsX(), contentsY(), visibleWidth()/2, visibleHeight());  
            break;
        case SeparateViewDuplicateHorz:
            region = TQRect(contentsX(), contentsY(), visibleWidth(), visibleHeight()/2);  
            break;
    }
        
    return region;   
}

void ImageRegionWidget::viewportPaintExtraData()
{   
    if (!m_movingInProgress && !d->pixmapRegion.isNull())
    {
        TQPainter p(viewport());
        TQRect region = getLocalTargetImageRegion();
        TQRect rt(contentsToViewport(region.topLeft()), contentsToViewport(region.bottomRight())); 

        region = getLocalImageRegionToRender();
        TQRect ro(contentsToViewport(region.topLeft()), contentsToViewport(region.bottomRight())); 

        bitBlt(viewport(), rt.x(), rt.y(), &d->pixmapRegion, 0, 0, rt.width(), rt.height());

        // Drawing separate view.
        
        switch (d->separateView)
        {
            case SeparateViewVertical:
            case SeparateViewDuplicateVert:
            {
                p.setPen(TQPen(TQt::white, 2, TQt::SolidLine));
                p.drawLine(rt.topLeft().x(),    rt.topLeft().y(),
                           rt.bottomLeft().x(), rt.bottomLeft().y());
                p.setPen(TQPen(TQt::red, 2, TQt::DotLine));
                p.drawLine(rt.topLeft().x(),    rt.topLeft().y()+1,
                           rt.bottomLeft().x(), rt.bottomLeft().y()-1);
                            
                p.setPen(TQPen(TQt::red, 1)) ;                    
                TQFontMetrics fontMt = p.fontMetrics();
                
                TQString text(i18n("Target"));
                TQRect textRect;
                TQRect fontRect = fontMt.boundingRect(0, 0, contentsWidth(), contentsHeight(), 0, text); 
                textRect.setTopLeft(TQPoint(rt.topLeft().x()+20, rt.topLeft().y()+20));
                textRect.setSize( TQSize(fontRect.width()+2, fontRect.height()+2) );
                p.fillRect(textRect, TQBrush(TQColor(250, 250, 255)) );
                p.drawRect(textRect);
                p.drawText(textRect, TQt::AlignCenter, text);
                            
                text = i18n("Original");                    
                fontRect = fontMt.boundingRect(0, 0, contentsWidth(), contentsHeight(), 0, text); 

                if (d->separateView == SeparateViewVertical)
                    ro.moveBy(-ro.width(), 0);

                textRect.setTopLeft(TQPoint(ro.topLeft().x()+20, ro.topLeft().y()+20));
                textRect.setSize( TQSize(fontRect.width()+2, fontRect.height()+2 ) );       
                p.fillRect(textRect, TQBrush(TQColor(250, 250, 255)) );
                p.drawRect(textRect);
                p.drawText(textRect, TQt::AlignCenter, text);
                break;
            }
            case SeparateViewHorizontal:
            case SeparateViewDuplicateHorz:
            {
                p.setPen(TQPen(TQt::white, 2, TQt::SolidLine));
                p.drawLine(rt.topLeft().x()+1,  rt.topLeft().y(),
                           rt.topRight().x()-1, rt.topRight().y());
                p.setPen(TQPen(TQt::red, 2, TQt::DotLine));
                p.drawLine(rt.topLeft().x(),   rt.topLeft().y(),
                           rt.topRight().x(), rt.topRight().y());
                            
                p.setPen(TQPen(TQt::red, 1)) ;                    
                TQFontMetrics fontMt = p.fontMetrics();
                
                TQString text(i18n("Target"));
                TQRect textRect;
                TQRect fontRect = fontMt.boundingRect(0, 0, contentsWidth(), contentsHeight(), 0, text); 
                textRect.setTopLeft(TQPoint(rt.topLeft().x()+20, rt.topLeft().y()+20));
                textRect.setSize( TQSize(fontRect.width()+2, fontRect.height()+2) );
                p.fillRect(textRect, TQBrush(TQColor(250, 250, 255)) );
                p.drawRect(textRect);
                p.drawText(textRect, TQt::AlignCenter, text);
                            
                text = i18n("Original");                    
                fontRect = fontMt.boundingRect(0, 0, contentsWidth(), contentsHeight(), 0, text);

                if (d->separateView == SeparateViewHorizontal)
                    ro.moveBy(0, -ro.height());

                textRect.setTopLeft(TQPoint(ro.topLeft().x()+20, ro.topLeft().y()+20));
                textRect.setSize( TQSize(fontRect.width()+2, fontRect.height()+2 ) );       
                p.fillRect(textRect, TQBrush(TQColor(250, 250, 255)) );
                p.drawRect(textRect);
                p.drawText(textRect, TQt::AlignCenter, text);
                break;
            }
        }
        
        // Drawing HighLighted points.
        
        if (!d->hightlightPoints.isEmpty())
        {
            TQPoint pt;
            TQRect  hpArea;
            
            for (int i = 0 ; i < d->hightlightPoints.count() ; i++)
            {
                pt = d->hightlightPoints.point(i);
                
                if ( getImageRegionToRender().contains(pt) )
                {
                    int x = (int)(((double)pt.x() * tileSize()) / floor(tileSize() / zoomFactor()));
                    int y = (int)(((double)pt.y() * tileSize()) / floor(tileSize() / zoomFactor()));

                    TQPoint hp(contentsToViewport(TQPoint(x, y)));
                    hpArea.setSize(TQSize((int)(16*zoomFactor()), (int)(16*zoomFactor())));
                    hpArea.moveCenter(hp);

                    p.setPen(TQPen(TQt::white, 2, TQt::SolidLine));
                    p.drawLine(hp.x(), hpArea.y(), 
                               hp.x(), hp.y()-(int)(3*zoomFactor()));
                    p.drawLine(hp.x(), hp.y()+(int)(3*zoomFactor()), 
                               hp.x(), hpArea.bottom());
                    p.drawLine(hpArea.x(),                   hp.y(), 
                               hp.x()-(int)(3*zoomFactor()), hp.y());
                    p.drawLine(hp.x()+(int)(3*zoomFactor()), hp.y(), 
                               hpArea.right(),                hp.y());

                    p.setPen(TQPen(TQt::red, 2, TQt::DotLine));
                    p.drawLine(hp.x(), hpArea.y(), 
                               hp.x(), hp.y()-(int)(3*zoomFactor()));
                    p.drawLine(hp.x(), hp.y()+(int)(3*zoomFactor()), 
                               hp.x(), hpArea.bottom());
                    p.drawLine(hpArea.x(),                   hp.y(), 
                               hp.x()-(int)(3*zoomFactor()), hp.y());
                    p.drawLine(hp.x()+(int)(3*zoomFactor()), hp.y(), 
                               hpArea.right(),                hp.y());
                }
            }
        }
        p.end();
    }
}

void ImageRegionWidget::setCenterContentsPosition()
{
    center(contentsWidth()/2, contentsHeight()/2);    
    slotZoomFactorChanged();
}

void ImageRegionWidget::setContentsPosition(int x, int y, bool targetDone)
{
    if( targetDone )
        m_movingInProgress = false;

    setContentsPos(x, y);    
    
    if( targetDone )
       slotZoomFactorChanged();
}

void ImageRegionWidget::backupPixmapRegion()
{
    d->pixmapRegion = TQPixmap();
}

void ImageRegionWidget::restorePixmapRegion()
{
    m_movingInProgress = true;
    viewport()->repaint(false);
}

void ImageRegionWidget::updatePreviewImage(DImg *img)
{
    DImg image = img->copy();
    TQRect r    = getLocalImageRegionToRender();
    image.resize(r.width(), r.height());

    // Because image plugins are tool witch only work on image data, the DImg container
    // do not contain metadata from original image. About Color Managed View, we need to 
    // restore the embedded ICC color profile. 
    image.setICCProfil(d->image.getICCProfil());
    d->pixmapRegion = d->iface->convertToPixmap(image);
}

DImg ImageRegionWidget::getImageRegionImage()
{
    return (d->image.copy(getImageRegionToRender()));
}

TQRect ImageRegionWidget::getImageRegionToRender()
{
    TQRect r = getLocalImageRegionToRender();

    int x = (int)(((double)r.x()      / tileSize()) * floor(tileSize() / zoomFactor()));
    int y = (int)(((double)r.y()      / tileSize()) * floor(tileSize() / zoomFactor()));
    int w = (int)(((double)r.width()  / tileSize()) * floor(tileSize() / zoomFactor()));   
    int h = (int)(((double)r.height() / tileSize()) * floor(tileSize() / zoomFactor()));

    TQRect rect(x, y, w, h);
    return (rect);
}

TQRect ImageRegionWidget::getLocalImageRegionToRender()
{
    TQRect region;
    
    if (d->separateView == SeparateViewVertical)
    {
        region = TQRect((int)ceilf(contentsX()+visibleWidth()/2.0), contentsY(), 
                       (int)ceilf(visibleWidth()/2.0),             visibleHeight());
    }
    else if (d->separateView == SeparateViewHorizontal)
    {
        region = TQRect(contentsX(),    (int)ceilf(contentsY()+visibleHeight()/2.0), 
                       visibleWidth(), (int)ceilf(visibleHeight()/2.0));
    }
    else if (d->separateView == SeparateViewDuplicateVert)
    {
        region = TQRect(contentsX(),                    contentsY(), 
                       (int)ceilf(visibleWidth()/2.0), visibleHeight());
    }
    else if (d->separateView == SeparateViewDuplicateHorz)
    {
        region = TQRect(contentsX(),    contentsY(), 
                       visibleWidth(), (int)ceilf(visibleHeight()/2.0));
    }
    else 
    {
        region = TQRect(contentsX(),    contentsY(), 
                       visibleWidth(), visibleHeight());
    }
    
    return (region);
}

TQRect ImageRegionWidget::getLocalTargetImageRegion()
{
    TQRect region = getLocalImageRegionToRender();
    
    if (d->separateView == SeparateViewDuplicateVert)
        region.moveBy(region.width(), 0);
    else if (d->separateView == SeparateViewDuplicateHorz)
        region.moveBy(0, region.height());
    
    return region;
}

void ImageRegionWidget::setContentsSize()
{
    switch (d->separateView)
    {
        case SeparateViewVertical:
        case SeparateViewHorizontal:
        case SeparateViewNone:
        {
            PreviewWidget::setContentsSize();
            break;
        }
        case SeparateViewDuplicateVert:
        {
            resizeContents(zoomWidth()+visibleWidth()/2, zoomHeight());
            break;
        }
        case SeparateViewDuplicateHorz:
        {
            resizeContents(zoomWidth(), zoomHeight()+visibleHeight()/2);
            break;
        }
        default:
            DWarning() << "Unknown separation view specified" << endl;
    }
}

void ImageRegionWidget::contentsWheelEvent(TQWheelEvent *e)
{
    e->accept();

    if (e->state() & TQt::ControlButton)
    {
        if (e->delta() < 0 && !maxZoom())
            slotIncreaseZoom();
        else if (e->delta() > 0 && !minZoom())
            slotDecreaseZoom();
        return;
    }
}

}  // NameSpace Digikam
