/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2001-2002 by Andreas Zehender
    email                : zehender@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.                                   *
*                                                                        *
**************************************************************************/

#include "pmpovraywidget.h"
#include "pmpovrayrenderwidget.h"
#include "pmpovrayoutputwidget.h"
#include "pmshell.h"

#include <klocale.h>
#include <kmessagebox.h>
#include <kimageio.h>
#include <kfiledialog.h>
#include <ktempfile.h>
#include <kio/netaccess.h>

#include <qlayout.h>
#include <qprogressbar.h>
#include <qpushbutton.h>
#include <qlabel.h>
#include <qscrollview.h>
#include <qtimer.h>
#include <qapplication.h>

const int timerIntervall = 1000;
bool PMPovrayWidget::s_imageFormatsRegistered = false;

PMPovrayWidget::PMPovrayWidget( QWidget* parent, const char* name )
      : KDialog( parent, name )
{
   QVBoxLayout* topLayout = new QVBoxLayout( this, KDialog::marginHint( ), KDialog::spacingHint( ) );
   topLayout->addStretch( );

   QHBoxLayout* renderLayout = new QHBoxLayout( );
   topLayout->addLayout( renderLayout, 2 );
   m_pScrollView = new QScrollView( this );
   m_pScrollView->setBackgroundMode( PaletteBase );
   renderLayout->addWidget( m_pScrollView, 2 );
   m_pRenderWidget = new PMPovrayRenderWidget( m_pScrollView->viewport( ) );
   m_pRenderWidget->setFixedSize( 200, 200 );
   m_pScrollView->addChild( m_pRenderWidget );
   topLayout->addStretch( );
   
   QHBoxLayout* progressLayout = new QHBoxLayout( topLayout );
   m_pProgressBar = new QProgressBar( this );
   m_pProgressBar->hide( );
   progressLayout->addWidget( m_pProgressBar, 1 );
   m_pProgressLabel = new QLabel( this );
   progressLayout->addWidget( m_pProgressLabel, 2 );

   QHBoxLayout* buttonLayout = new QHBoxLayout( topLayout );
   m_pStopButton = new QPushButton( i18n( "Stop" ), this );
   m_pStopButton->setEnabled( false );
   buttonLayout->addWidget( m_pStopButton );
   m_pSuspendButton = new QPushButton( i18n( "Suspend" ), this );
   m_pSuspendButton->setEnabled( false );
   buttonLayout->addWidget( m_pSuspendButton );
   m_pResumeButton = new QPushButton( i18n( "Resume" ), this );
   m_pResumeButton->setEnabled( false );
   buttonLayout->addWidget( m_pResumeButton );
   buttonLayout->addStretch( 1 );
   m_pPovrayOutputButton = new QPushButton( i18n( "Povray Output..." ), this );
   buttonLayout->addWidget( m_pPovrayOutputButton );
   
   buttonLayout = new QHBoxLayout( topLayout );
   m_pSaveButton = new QPushButton( i18n( "Save..." ), this );
   m_pSaveButton->setEnabled( false );
   buttonLayout->addWidget( m_pSaveButton );
   buttonLayout->addStretch( 1 );
   QPushButton* closeButton = new QPushButton( i18n( "Close" ), this );
   buttonLayout->addWidget( closeButton );

   connect( m_pRenderWidget, SIGNAL( finished( int ) ),
            SLOT( slotRenderingFinished( int ) ) );
   connect( m_pRenderWidget, SIGNAL( progress( int ) ),
            SLOT( slotProgress( int ) ) );
   connect( m_pRenderWidget, SIGNAL( lineFinished( int ) ),
            SLOT( slotLineFinished( int ) ) );

   connect( m_pStopButton, SIGNAL( clicked( ) ), SLOT( slotStop( ) ) );
   connect( m_pSuspendButton, SIGNAL( clicked( ) ), SLOT( slotSuspend( ) ) );
   connect( m_pResumeButton, SIGNAL( clicked( ) ), SLOT( slotResume( ) ) );
   connect( m_pSaveButton, SIGNAL( clicked( ) ), SLOT( slotSave( ) ) );
   connect( closeButton, SIGNAL( clicked( ) ), SLOT( slotClose( ) ) );
   connect( m_pPovrayOutputButton, SIGNAL( clicked( ) ),
            SLOT( slotPovrayOutput( ) ) );

   m_bRunning = false;
   m_pProgressTimer = new QTimer( this );
   connect( m_pProgressTimer, SIGNAL( timeout( ) ),
            SLOT( slotUpdateSpeed( ) ) );

   setCaption( i18n( "Render Window" ) );
   
   m_height = m_width = 0;
   m_stopped = false;

   m_pPovrayOutputWidget = new PMPovrayOutputWidget( );
   connect( m_pRenderWidget, SIGNAL( povrayMessage( const QString& ) ),
            m_pPovrayOutputWidget, SLOT( slotText( const QString& ) ) );
}

PMPovrayWidget::~PMPovrayWidget( )
{
   delete m_pPovrayOutputWidget;
}

bool PMPovrayWidget::render( const QByteArray& scene, const PMRenderMode& m,
                             const KURL& documentURL )
{
   bool updateSize = ( m_height != m.height( ) ) || ( m_width != m.width( ) );
   m_height = m.height( );
   m_width = m.width( );
   m_bRunning = false;
   m_pPovrayOutputWidget->slotClear( );
   m_stopped = false;
   
   m_pRenderWidget->setFixedSize( m_width, m_height );
   QSize maxSize( m_width + m_pScrollView->frameWidth( ) * 2,
                  m_height + m_pScrollView->frameWidth( ) * 2 );
   m_pScrollView->setMaximumSize( maxSize );
      
   if( updateSize )
   {
      int w, h;

      w = maxSize.width( ) + KDialog::spacingHint( ) * 2;
      h = maxSize.height( ) + m_pSaveButton->sizeHint( ).height( ) * 2
         + KDialog::spacingHint( ) * 6;
      if( m_pProgressLabel->sizeHint( ).height( )
          > m_pProgressBar->sizeHint( ).height( ) )
         h += m_pProgressLabel->sizeHint( ).height( );
      else
         h += m_pProgressBar->sizeHint( ).height( );

      w += 16;
      h += 16;

      QWidget* dw = QApplication::desktop( );
      if( w > dw->width( ) )
         w = dw->width( );
      if( h > dw->height( ) )
         h = dw->height( );
      resize( w, h );
   }
   
   if( m_pRenderWidget->render( scene, m, documentURL ) )
   {
      m_bRunning = true;
      m_pProgressBar->setProgress( 0 );
      m_pProgressBar->show( );
      m_pProgressLabel->setText( i18n( "running" ) );
      m_pStopButton->setEnabled( true );
      m_pSuspendButton->setEnabled( true );
      m_pResumeButton->setEnabled( false );
      m_pSaveButton->setEnabled( false );

      m_lastSpeedTime = QTime( );
      m_pProgressTimer->start( timerIntervall, true );
      m_speedInfo = false;
      m_speed = 0;
      m_line = 0;
      m_immediateUpdate = false;
      showSpeed( 0 );
   }
   
   return m_bRunning;
}

void PMPovrayWidget::slotStop( )
{
   m_stopped = true;
   m_pRenderWidget->killRendering( );
}

void PMPovrayWidget::slotSuspend( )
{
   m_pRenderWidget->suspendRendering( );
   m_pSuspendButton->setEnabled( false );
   m_pResumeButton->setEnabled( true );
   
   m_pProgressTimer->stop( );
   m_lastSpeedTime = QTime( );
   m_speedInfo = false;
   m_immediateUpdate = false;
   m_pProgressLabel->setText( i18n( "suspended" ) );
}

void PMPovrayWidget::slotResume( )
{
   m_pRenderWidget->resumeRendering( );
   m_pSuspendButton->setEnabled( true );
   m_pResumeButton->setEnabled( false );
   
   m_pProgressTimer->start( timerIntervall, true );
   showSpeed( m_speed );
}

void PMPovrayWidget::slotClose( )
{
   hide( );
}

void PMPovrayWidget::slotSave( )
{
   KTempFile* tempFile = 0;
   QFile* file = 0;
   bool ok = true;

   if( !s_imageFormatsRegistered )
   {
      KImageIO::registerFormats( );
      s_imageFormatsRegistered = true;
   }
   
   KURL url = KFileDialog::getSaveURL( QString::null, KImageIO::pattern( KImageIO::Writing ) );
   if( url.isEmpty( ) )
      return;
   if( !PMShell::overwriteURL( url ) )
      return;
   
   if( url.isMalformed( ) )
   {
      KMessageBox::error( this, i18n( "Malformed URL" ) );
      return;
   }

   QString format = KImageIO::type( url.fileName( ) );
   if( format.isEmpty( ) )
   {
      KMessageBox::error( this, i18n( "Unknown image format.\n"
                                      "Please enter a valid suffix." ) );
      return;
   }
   
   if( !KImageIO::canWrite( format ) )
   {
      KMessageBox::error( this, i18n( "Format is not supported for writing." ) );
      return;
   }

   if( url.isLocalFile( ) )
   {
      // Local file
      file = new QFile( url.path( ) );
      if( !file->open( IO_WriteOnly ) )
         ok = false;
   }
   else
   {
      // Remote file
      // provide a temp file
      tempFile = new KTempFile( );
      if( tempFile->status( ) != 0 )
         ok = false;
      else
         file = tempFile->file( );
   }

   if( ok )
   {
      QImageIO iio( file, format.latin1( ) );
      iio.setImage( m_pRenderWidget->image( ) );
      ok = iio.write( );

      if( ok )
      {
         if( tempFile )
         {
            tempFile->close( );
            ok = KIO::NetAccess::upload( tempFile->name( ), url );
            tempFile->unlink( );
            file = 0;
         }
         else
            file->close( );
      }
      else
         KMessageBox::error( this, i18n( "Couldn't correctly write the image.\n"
                                         "Wrong image format?" ) );
   }
   else
      KMessageBox::error( this, i18n( "Couldn't write the image.\n"
                                      "Permission denied." ) );


   if( file )
      delete file;
   if( tempFile )
      delete tempFile;
}

void PMPovrayWidget::slotPovrayOutput( )
{
   m_pPovrayOutputWidget->show( );
}

void PMPovrayWidget::slotRenderingFinished( int returnStatus )
{
   m_bRunning = false;
   if( returnStatus == 0 )
      m_pSaveButton->setEnabled( true );
   m_pStopButton->setEnabled( false );
   m_pSuspendButton->setEnabled( false );
   m_pResumeButton->setEnabled( false );
   m_pProgressLabel->setText( i18n( "finished" ) );
   m_pProgressTimer->stop( );

   if( ( returnStatus != 0 ) && !m_stopped )
   {
      KMessageBox::error( this, i18n( "Povray exited abnormally with "
                                      "exit code %1.\n"
                                      "See the povray output for details." )
                          .arg( returnStatus ) );
   }
}

void PMPovrayWidget::slotProgress( int i )
{
   m_pProgressBar->setProgress( i );
}

void PMPovrayWidget::slotLineFinished( int line )
{
   m_speedInfo = true;
   QTime ct = QTime::currentTime( );

   if( !m_lastSpeedTime.isNull( ) )
   {
      int msecs = m_lastSpeedTime.msecsTo( ct );
      if( msecs < 1 )
         msecs = 1;

      double g = 1.0 / ( ( double ) msecs / 500.0 + 1.0 );
      m_speed = g * m_speed +
                1000 * ( 1.0 - g ) * m_width * ( line - m_line ) / msecs;
   }

   if( m_immediateUpdate )
   {
      m_immediateUpdate = false;
      showSpeed( m_speed );
      m_pProgressTimer->start( timerIntervall, true );
      m_speedInfo = false;
   }
   
   m_lastSpeedTime = ct;
   m_line = line;
}

void PMPovrayWidget::slotUpdateSpeed( )
{
   if( m_speedInfo )
   {
      showSpeed( m_speed );
      m_pProgressTimer->start( timerIntervall, true );
      m_speedInfo = false;
   }
   else
      m_immediateUpdate = true;
}

void PMPovrayWidget::showSpeed( double pps )
{
   QString num;
   if( pps > 1000000 )
   {
      num.setNum( pps / 1000000, 'g', 4 );
      num += 'M';
   }
   else if( pps > 1000 )
   {
      num.setNum( pps / 1000, 'g', 4 );
      num += 'K';
   }
   else
      num.setNum( pps, 'g', 4 );
   
   m_pProgressLabel->setText( i18n( "running, %1 pixels/second" ).arg( num ) );
}

#include "pmpovraywidget.moc"
