/* ============================================================
 *
 * This file is a part of digiKam project
 * http://www.digikam.org
 *
 * Date        : 2005-05-07
 * Description : A threaded filter plugin dialog with a preview
 *               image guide widget and a settings user area
 *
 * Copyright (C) 2005-2007 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.
 *
 * ============================================================ */

// TQt includes.

#include <tqvgroupbox.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqwhatsthis.h>
#include <tqtooltip.h>
#include <tqlayout.h>
#include <tqframe.h>
#include <tqtimer.h>
#include <tqspinbox.h>
#include <tqsplitter.h>
#include <tqhbox.h>

// KDE includes.

#include <kcursor.h>
#include <tdelocale.h>
#include <tdeaboutdata.h>
#include <khelpmenu.h>
#include <kiconloader.h>
#include <tdeapplication.h>
#include <tdepopupmenu.h>
#include <kstandarddirs.h>
#include <tdeglobalsettings.h>
#include <kprogress.h>
#include <kcolorbutton.h>
#include <tdeconfig.h>
#include <kseparator.h>

// Local includes.

#include "ddebug.h"
#include "sidebar.h"
#include "dimgthreadedfilter.h"
#include "dimginterface.h"
#include "imageguidedlg.h"
#include "imageguidedlg.moc"

namespace Digikam
{

class ImageGuideDlgPriv
{
public:

    enum RunningMode
    {
        NoneRendering=0,
        PreviewRendering,
        FinalRendering
    };
    
    ImageGuideDlgPriv()
    {
        tryAction            = false;
        progress             = true;
        currentRenderingMode = NoneRendering;
        parent               = 0;
        settings             = 0;
        timer                = 0;
        aboutData            = 0;
        guideColorBt         = 0;
        progressBar          = 0;
        guideSize            = 0;
        mainLayout           = 0;
        settingsLayout       = 0;
        hbox                 = 0;
        settingsSideBar      = 0;
        splitter             = 0;
    }

    bool          tryAction;
    bool          progress;
    
    int           currentRenderingMode;

    TQWidget      *parent;
    TQWidget      *settings;
    
    TQTimer       *timer;
    
    TQString       name;

    TQGridLayout  *mainLayout;
    TQGridLayout  *settingsLayout;
    
    TQSpinBox     *guideSize;

    TQHBox        *hbox;

    TQSplitter    *splitter;

    KProgress    *progressBar;
        
    KColorButton *guideColorBt;

    TDEAboutData   *aboutData;

    Sidebar      *settingsSideBar;
};

ImageGuideDlg::ImageGuideDlg(TQWidget* parent, TQString title, TQString name,
                             bool loadFileSettings, bool progress,
                             bool guideVisible, int guideMode, TQFrame* bannerFrame,
                             bool prevModeOptions, bool useImageSelection,
                             bool tryAction)
             : KDialogBase(Plain, 0,
                           Help|Default|User1|User2|User3|Try|Ok|Cancel, Ok,
                           parent, 0, true, true,
                           i18n("&Abort"),
                           i18n("&Save As..."),
                           i18n("&Load..."))
{
    kapp->setOverrideCursor( KCursor::waitCursor() );
    setCaption(DImgInterface::defaultInterface()->getImageFileName() + TQString(" - ") + title);
    
    d = new ImageGuideDlgPriv;
    d->parent        = parent;
    d->name          = name;
    d->progress      = progress;
    d->tryAction     = tryAction;
    m_threadedFilter = 0;
    TQString whatsThis;

    setButtonWhatsThis ( Default, i18n("<p>Reset all filter parameters to their default values.") );
    setButtonWhatsThis ( User1, i18n("<p>Abort the current image rendering.") );
    setButtonWhatsThis ( User3, i18n("<p>Load all filter parameters from settings text file.") );
    setButtonWhatsThis ( User2, i18n("<p>Save all filter parameters to settings text file.") );
    showButton(User2, loadFileSettings);
    showButton(User3, loadFileSettings);
    showButton(Try, tryAction);

    resize(configDialogSize(name + TQString(" Tool Dialog")));

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

    d->mainLayout = new TQGridLayout( plainPage(), 2, 1);

    if (bannerFrame)
    {
        bannerFrame->reparent( plainPage(), TQPoint(0, 0) );
        d->mainLayout->addMultiCellWidget(bannerFrame, 0, 0, 0, 1);
    }

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

    TQString desc;

    if (guideVisible)
        desc = i18n("<p>This is the the image filter effect preview. "
                    "If you move the mouse cursor on this area, "
                    "a vertical and horizontal dashed line will be draw "
                    "to guide you in adjusting the filter settings. "
                    "Press the left mouse button to freeze the dashed "
                    "line's position.");
    else
        desc = i18n("<p>This is the image filter effect preview.");

    d->hbox              = new TQHBox(plainPage());
    d->splitter          = new TQSplitter(d->hbox);
    m_imagePreviewWidget = new ImageWidget(d->name, d->splitter, desc, prevModeOptions, 
                                           guideMode, guideVisible, useImageSelection);
    
    d->splitter->setFrameStyle( TQFrame::NoFrame );
    d->splitter->setFrameShadow( TQFrame::Plain );
    d->splitter->setFrameShape( TQFrame::NoFrame );    
    d->splitter->setOpaqueResize(false);
    
    TQSizePolicy rightSzPolicy(TQSizePolicy::Preferred, TQSizePolicy::Expanding, 2, 1);
    m_imagePreviewWidget->setSizePolicy(rightSzPolicy);

    TQString sbName(d->name + TQString(" Image Plugin Sidebar"));
    d->settingsSideBar = new Sidebar(d->hbox, sbName.ascii(), Sidebar::Right);
    d->settingsSideBar->setSplitter(d->splitter);

    d->mainLayout->addMultiCellWidget(d->hbox, 1, 2, 0, 1);
    d->mainLayout->setColStretch(0, 10);
    d->mainLayout->setRowStretch(2, 10);

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

    d->settings          = new TQWidget(plainPage());
    d->settingsLayout    = new TQGridLayout( d->settings, 1, 0);
    TQVBoxLayout *vLayout = new TQVBoxLayout( spacingHint() );
    
    // -------------------------------------------------------------

    TQWidget *gboxGuideSettings = new TQWidget(d->settings);
    TQGridLayout* grid          = new TQGridLayout( gboxGuideSettings, 2, 2, marginHint(), spacingHint());
    KSeparator *line           = new KSeparator(TQt::Horizontal, gboxGuideSettings);
    grid->addMultiCellWidget(line, 0, 0, 0, 2);

    TQLabel *label5  = new TQLabel(i18n("Guide color:"), gboxGuideSettings);
    d->guideColorBt = new KColorButton( TQColor( TQt::red ), gboxGuideSettings );
    TQWhatsThis::add( d->guideColorBt, i18n("<p>Set here the color used to draw guides dashed-lines."));
    grid->addMultiCellWidget(label5, 1, 1, 0, 0);
    grid->addMultiCellWidget(d->guideColorBt, 1, 1, 2, 2);

    TQLabel *label6 = new TQLabel(i18n("Guide width:"), gboxGuideSettings);
    d->guideSize   = new TQSpinBox( 1, 5, 1, gboxGuideSettings);
    TQWhatsThis::add( d->guideSize, i18n("<p>Set here the width in pixels used to draw guides dashed-lines."));
    grid->addMultiCellWidget(label6, 2, 2, 0, 0);
    grid->addMultiCellWidget(d->guideSize, 2, 2, 2, 2);
    grid->setColStretch(1, 10);

    if (guideVisible) gboxGuideSettings->show();
    else gboxGuideSettings->hide();

    vLayout->addWidget(gboxGuideSettings);

    TQHBox *hbox    = new TQHBox(d->settings);
    TQLabel *space1 = new TQLabel(hbox);
    space1->setFixedWidth(spacingHint());    
    d->progressBar = new KProgress(100, hbox);
    d->progressBar->setMaximumHeight( fontMetrics().height() );
    TQWhatsThis::add(d->progressBar ,i18n("<p>This is the percentage of the task which has been completed up to this point."));
    d->progressBar->setValue(0);
    setProgressVisible(false);
    TQLabel *space2 = new TQLabel(hbox);
    space2->setFixedWidth(spacingHint());
    
    vLayout->addWidget(hbox);
    vLayout->addStretch(10);

    d->settingsLayout->addMultiCellLayout(vLayout, 1, 1, 0, 0);

    d->settingsSideBar->appendTab(d->settings, SmallIcon("configure"), i18n("Settings"));    
    d->settingsSideBar->loadViewState();
    
    // Reading splitter sizes here prevent flicker effect in dialog.
    TDEConfig *config = kapp->config();
    config->setGroup(d->name + TQString(" Tool Dialog"));
    if(config->hasKey("SplitterSizes"))
        d->splitter->setSizes(config->readIntListEntry("SplitterSizes"));

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

    TQTimer::singleShot(0, this, TQ_SLOT(slotInit()));
    kapp->restoreOverrideCursor();
}

ImageGuideDlg::~ImageGuideDlg()
{
    if (d->timer)
       delete d->timer;

    if (m_threadedFilter)
       delete m_threadedFilter;

    if (d->aboutData)
       delete d->aboutData;
    
    delete d->settingsSideBar;            
    delete d;            
}

void ImageGuideDlg::readSettings(void)
{
    TQColor defaultGuideColor(TQt::red);
    TDEConfig *config = kapp->config();
    config->setGroup(d->name + TQString(" Tool Dialog"));
    d->guideColorBt->setColor(config->readColorEntry("Guide Color", &defaultGuideColor));
    d->guideSize->setValue(config->readNumEntry("Guide Width", 1));
    m_imagePreviewWidget->slotChangeGuideSize(d->guideSize->value());
    m_imagePreviewWidget->slotChangeGuideColor(d->guideColorBt->color());
}

void ImageGuideDlg::writeSettings(void)
{
    TDEConfig *config = kapp->config();
    config->setGroup(d->name + TQString(" Tool Dialog"));
    config->writeEntry( "Guide Color", d->guideColorBt->color() );
    config->writeEntry( "Guide Width", d->guideSize->value() );
    config->writeEntry( "SplitterSizes", d->splitter->sizes() );
    config->sync();
    saveDialogSize(d->name + TQString(" Tool Dialog"));
}

void ImageGuideDlg::slotInit()
{
    readSettings();
    // Reset values to defaults.
    TQTimer::singleShot(0, this, TQ_SLOT(readUserSettings()));

    if (!d->tryAction)
    {
        connect(m_imagePreviewWidget, TQ_SIGNAL(signalResized()),
                this, TQ_SLOT(slotResized()));
    }

    connect(d->guideColorBt, TQ_SIGNAL(changed(const TQColor &)),
            m_imagePreviewWidget, TQ_SLOT(slotChangeGuideColor(const TQColor &)));

    connect(d->guideSize, TQ_SIGNAL(valueChanged(int)),
            m_imagePreviewWidget, TQ_SLOT(slotChangeGuideSize(int)));
}

void ImageGuideDlg::setUserAreaWidget(TQWidget *w)
{
    w->reparent( d->settings, TQPoint(0, 0) );
    TQVBoxLayout *vLayout = new TQVBoxLayout( spacingHint() );
    vLayout->addWidget(w);
    d->settingsLayout->addMultiCellLayout(vLayout, 0, 0, 0, 0);
}

void ImageGuideDlg::setAboutData(TDEAboutData *about)
{
    d->aboutData = about;
    TQPushButton *helpButton = actionButton( Help );
    KHelpMenu* helpMenu = new KHelpMenu(this, d->aboutData, false);
    helpMenu->menu()->removeItemAt(0);
    helpMenu->menu()->insertItem(i18n("digiKam Handbook"), this, TQ_SLOT(slotHelp()), 0, -1, 0);
    helpButton->setPopup( helpMenu->menu() );
}

void ImageGuideDlg::setProgressVisible(bool v)
{
    if (v)
        d->progressBar->show();
    else
        d->progressBar->hide();
}

void ImageGuideDlg::abortPreview()
{
    d->currentRenderingMode = ImageGuideDlgPriv::NoneRendering;
    d->progressBar->setValue(0);
    setProgressVisible(false);
    enableButton(Ok,      true);
    enableButton(User1,   false);
    enableButton(User2,   true);
    enableButton(User3,   true);
    enableButton(Try,     true);
    enableButton(Default, true);
    renderingFinished();
}

void ImageGuideDlg::slotTry()
{
    slotEffect();
}

void ImageGuideDlg::slotResized(void)
{
    if (d->currentRenderingMode == ImageGuideDlgPriv::FinalRendering)
    {
       m_imagePreviewWidget->update();
       return;
    }
    else if (d->currentRenderingMode == ImageGuideDlgPriv::PreviewRendering)
    {
       if (m_threadedFilter)
          m_threadedFilter->stopComputation();
    }

    TQTimer::singleShot(0, this, TQ_SLOT(slotEffect()));
}

void ImageGuideDlg::slotUser1()
{
    if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering)
        if (m_threadedFilter)
            m_threadedFilter->stopComputation();
}

void ImageGuideDlg::slotDefault()
{
    resetValues();
    slotEffect();
}

void ImageGuideDlg::slotCancel()
{
    if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering)
    {
       if (m_threadedFilter)
          m_threadedFilter->stopComputation();

       kapp->restoreOverrideCursor();
    }
    
    writeSettings();
    done(Cancel);
}

void ImageGuideDlg::closeEvent(TQCloseEvent *e)
{
    if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering)
    {
       if (m_threadedFilter)
          m_threadedFilter->stopComputation();

       kapp->restoreOverrideCursor();
    }

    writeSettings();
    e->accept();
}

void ImageGuideDlg::slotHelp()
{
    // If setAboutData() is called by plugin, well DigikamImagePlugins help is lauched, 
    // else digiKam help. In this case, setHelp() method must be used to set anchor and handbook name.

    if (d->aboutData)
        TDEApplication::kApplication()->invokeHelp(d->name, "digikam");
    else
        KDialogBase::slotHelp();
}

void ImageGuideDlg::slotTimer()
{
    if (d->timer)
    {
       d->timer->stop();
       delete d->timer;
    }

    d->timer = new TQTimer( this );
    connect( d->timer, TQ_SIGNAL(timeout()),
             this, TQ_SLOT(slotEffect()) );
    d->timer->start(500, true);
}

void ImageGuideDlg::slotEffect()
{
    // Computation already in process.
    if (d->currentRenderingMode != ImageGuideDlgPriv::NoneRendering)
        return;

    d->currentRenderingMode = ImageGuideDlgPriv::PreviewRendering;
    DDebug() << "Preview " << d->name << " started..." << endl;

    enableButton(Ok,      false);
    enableButton(User1,   true);
    enableButton(User2,   false);
    enableButton(User3,   false);
    enableButton(Default, false);
    enableButton(Try,     false);
    d->progressBar->setValue(0);
    if (d->progress) setProgressVisible(true);

    if (m_threadedFilter)
    {
        delete m_threadedFilter;
        m_threadedFilter = 0;
    }

    prepareEffect();
}

void ImageGuideDlg::slotOk()
{
    d->currentRenderingMode = ImageGuideDlgPriv::FinalRendering;
    DDebug() << "Final " << d->name << " started..." << endl;
    writeSettings();
    writeUserSettings();

    enableButton(Ok,      false);
    enableButton(User1,   false);
    enableButton(User2,   false);
    enableButton(User3,   false);
    enableButton(Default, false);
    enableButton(Try,     false);
    kapp->setOverrideCursor( KCursor::waitCursor() );
    d->progressBar->setValue(0);

    if (m_threadedFilter)
    {
        delete m_threadedFilter;
        m_threadedFilter = 0;
    }

    prepareFinal();
}

void ImageGuideDlg::customEvent(TQCustomEvent *event)
{
    if (!event) return;

    DImgThreadedFilter::EventData *ed = (DImgThreadedFilter::EventData*) event->data();

    if (!ed) return;

    if (ed->starting)           // Computation in progress !
    {
        d->progressBar->setValue(ed->progress);
    }
    else
    {
        if (ed->success)        // Computation Completed !
        {
            switch (d->currentRenderingMode)
            {
                case ImageGuideDlgPriv::PreviewRendering:
                {
                    DDebug() << "Preview " << d->name << " completed..." << endl;
                    putPreviewData();
                    abortPreview();
                    break;
                }

                case ImageGuideDlgPriv::FinalRendering:
                {
                    DDebug() << "Final" << d->name << " completed..." << endl;
                    putFinalData();
                    kapp->restoreOverrideCursor();
                    accept();
                    break;
                }
            }
        }
        else                   // Computation Failed !
        {
            switch (d->currentRenderingMode)
            {
                case ImageGuideDlgPriv::PreviewRendering:
                {
                    DDebug() << "Preview " << d->name << " failed..." << endl;
                    // abortPreview() must be call here for set progress bar to 0 properly.
                    abortPreview();
                    break;
                }

                case ImageGuideDlgPriv::FinalRendering:
                    break;
            }
        }
    }

    delete ed;
}

// Backport KDialog::keyPressEvent() implementation from KDELibs to ignore Enter/Return Key events 
// to prevent any conflicts between dialog keys events and SpinBox keys events.

void ImageGuideDlg::keyPressEvent(TQKeyEvent *e)
{
    if ( e->state() == 0 )
    {
        switch ( e->key() )
        {
        case Key_Escape:
            e->accept();
            reject();
        break;
        case Key_Enter:            
        case Key_Return:     
            e->ignore();              
        break;
        default:
            e->ignore();
            return;
        }
    }
    else
    {
        // accept the dialog when Ctrl-Return is pressed
        if ( e->state() == ControlButton &&
            (e->key() == Key_Return || e->key() == Key_Enter) )
        {
            e->accept();
            accept();
        }
        else
        {
            e->ignore();
        }
    }
}

}  // NameSpace Digikam
