/***************************************************************************
    smb4kprint  -  The printing core class.
                             -------------------
    begin                : Tue Mar 30 2004
    copyright            : (C) 2004 by Alexander Reinholdt
    email                : dustpuppy@mail.berlios.de
 ***************************************************************************/

/***************************************************************************
 *   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., 59 Temple Place, Suite 330, Boston,   *
 *   MA  02111-1307 USA                                                    *
 ***************************************************************************/

// Qt includes
#include <QTimerEvent>
#include <QFile>

// KDE includes
#include <kurl.h>
#include <kfileitem.h>
#include <kdebug.h>
#include <kshell.h>

// system includes
#include <sys/types.h>
#include <pwd.h>

// application specific includes
#include "smb4kprint.h"
#include "smb4kdefs.h"
#include "smb4kcoremessage.h"
#include "smb4kglobal.h"
#include "smb4kauthinfo.h"
#include "smb4ksettings.h"
#include "smb4kwalletmanager.h"

using namespace Smb4KGlobal;



Smb4KPrint::Smb4KPrint( QObject *parent ) : QObject( parent )
{
  m_proc = new KProcess( this );

  m_working = false;
  m_aborted = false;
  m_process_error = (QProcess::ProcessError)(-1);

  connect( m_proc, SIGNAL( finished( int, QProcess::ExitStatus ) ),
           this,   SLOT( slotProcessFinished( int, QProcess::ExitStatus ) ) );

  connect( m_proc, SIGNAL( error( QProcess::ProcessError ) ),
           this,   SLOT( slotProcessError( QProcess::ProcessError ) ) );
}


Smb4KPrint::~Smb4KPrint()
{
  abort();
}


void Smb4KPrint::abort()
{
  if ( m_proc->state() == KProcess::Running )
  {
    m_proc->kill();
  }
  else
  {
    // Do nothing
  }

  m_aborted = true;
}


/****************************************************************************
   Start the printing.
****************************************************************************/

bool Smb4KPrint::print( Smb4KPrintInfo *info )
{
  bool success = false;

  if ( info )
  {
    // Check that the file exists.
    if ( !QFile::exists( info->path() ) )
    {
      Smb4KCoreMessage::error( ERROR_FILE_NOT_FOUND, info->path() );

      return success;  // false
    }
    else
    {
      // Do nothing
    }

    startTimer( TIMER_INTERVAL );

    // Enqueue the item.
    m_queue.enqueue( *info );

    success = true;
  }
  else
  {
    return success;  // false
  }

  return success;
}


/****************************************************************************
   Sets the device URI
****************************************************************************/

void Smb4KPrint::setDeviceURI()
{
  Smb4KAuthInfo authInfo( m_info.shareItem() );
  Smb4KWalletManager::self()->readAuthInfo( &authInfo );

  QString uri;

  // It seems that we must not quote the entries for the DEVICE_URI
  // environment variable. Printing will fail if you do it.

  if ( !m_info.shareItem()->workgroup().isEmpty() )
  {
    if ( !authInfo.login().isEmpty() )
    {
      uri = QString( "smb://%1:%2@%3/%4/%5" ).arg( QString::fromLocal8Bit( authInfo.login(), -1 ) )
                                             .arg( QString::fromLocal8Bit( authInfo.password(), -1 ) )
                                             .arg( m_info.shareItem()->workgroup(),
                                                   m_info.shareItem()->host(),
                                                   m_info.shareItem()->name() );
    }
    else
    {
      uri = QString( "smb://%1/%2/%3" ).arg( m_info.shareItem()->workgroup(),
                                             m_info.shareItem()->host(),
                                             m_info.shareItem()->name() );
    }
  }
  else
  {
    if ( !authInfo.login().isEmpty() )
    {
      uri = QString( "smb://%1:%2@%3/%4" ).arg( QString::fromLocal8Bit( authInfo.login(), -1 ) )
                                          .arg( QString::fromLocal8Bit( authInfo.password(), -1 ) )
                                          .arg( m_info.shareItem()->host(),
                                                m_info.shareItem()->name() );
    }
    else
    {
      uri = QString( "smb://%1/%2" ).arg( m_info.shareItem()->host(), m_info.shareItem()->name() );
    }
  }

  m_proc->setEnv( "DEVICE_URI", uri );
}


/****************************************************************************
   Do normal printing.
****************************************************************************/

void Smb4KPrint::printNormal()
{
  QString command;

  command.append( "smbspool 111 "+QString( getpwuid( getuid() )->pw_name ) );
  command.append( " \"Smb4K print job\" "+QString( "%1" ).arg( m_info.copies() ) );
  command.append( " \"\" "+KShell::quoteArg( m_info.path() ) );

  m_proc->setShellCommand( command );
  m_proc->setOutputChannelMode( KProcess::SeparateChannels );
  m_proc->start();
}


/****************************************************************************
   Print DVI files.
****************************************************************************/

void Smb4KPrint::printDVI()
{
  // The temporary file.
  QString temp_file = tempDir()+"/smb4k_print.ps";

  QString command;

  // First we need the conversion:
  command.append( "cd "+KShell::quoteArg( m_info.path().section( "/", 0, -2 ) )+" && " );
  command.append( "dvips -P pdf -o "+temp_file+" "+KShell::quoteArg( m_info.path().section( "/", -1, -1 ) )+" && " );

  // The actual print command:
  command.append( "smbspool 111 "+QString( getpwuid( getuid() )->pw_name ) );
  command.append( " \"Smb4K print job\" "+QString( "%1" ).arg( m_info.copies() ) );
  command.append( " \"\" "+KShell::quoteArg( temp_file )+" && " );

  // Clean up:
  command.append( "rm -f "+temp_file );

  m_proc->setShellCommand( command );
  m_proc->setOutputChannelMode( KProcess::SeparateChannels );
  m_proc->start();
}


/****************************************************************************
   Print text files.
****************************************************************************/

void Smb4KPrint::printText()
{
  // The temporary file.
  QString temp_file = tempDir()+"/smb4k_print.ps";

  QString command;

  // Conversion:
  command.append( "enscript --quiet --columns=1 --no-header --ps-level=2 " );
  command.append( "-o "+KShell::quoteArg( temp_file )+" " );
  command.append( KShell::quoteArg( m_info.path() )+ " && " );

  // The actual print command:
  command.append( "smbspool 111 "+QString( getpwuid( getuid() )->pw_name ) );
  command.append( " \"Smb4K print job\" "+QString( "%1" ).arg( m_info.copies() ) );
  command.append( " \"\" "+KShell::quoteArg( temp_file )+" && " );

  // Clean up:
  command.append( "rm -f "+temp_file );

  m_proc->setShellCommand( command );
  m_proc->setOutputChannelMode( KProcess::SeparateChannels );
  m_proc->start();
}


void Smb4KPrint::timerEvent( QTimerEvent *e )
{
  if ( !m_queue.isEmpty() )
  {
    if ( !m_working )
    {
      m_working = true;

      m_aborted = false;

      emit state( PRINT_START );

      m_info = m_queue.dequeue();

      // Since we are queuing the data, we need to check again,
      // if the file that is to be printed still exists.
      if ( QFile::exists( m_info.path() ) )
      {
        // Determine the mimetype and start the print process if the
        // mimetype is supprted.
        KUrl url;
        url.setPath( m_info.path() );

        KFileItem file_item = KFileItem( KFileItem::Unknown, KFileItem::Unknown, url, false );

        if ( QString::compare( file_item.mimetype(), "application/postscript" ) == 0 ||
             QString::compare( file_item.mimetype(), "application/pdf" ) == 0 ||
             file_item.mimetype().startsWith( "image" ) )
        {
          setDeviceURI();
          printNormal();
        }
        else if ( QString::compare( file_item.mimetype(), "application/x-dvi" ) == 0 &&
                  !Smb4KSettings::dvips().isEmpty() )
        {
          setDeviceURI();
          printDVI();
        }
        else if ( (file_item.mimetype().startsWith( "text" ) ||
                  file_item.mimetype().startsWith( "message" ) ||
                  QString::compare( file_item.mimetype(), "application/x-shellscript" ) == 0) &&
                  !Smb4KSettings::enscript().isEmpty() )
        {
          setDeviceURI();
          printText();
        }
        else
        {
          Smb4KCoreMessage::information( INFO_MIMETYPE_NOT_SUPPORTED, file_item.mimetype() );

          m_working = false;
          emit state( PRINT_STOP );
        }
      }
      else
      {
        Smb4KCoreMessage::error( ERROR_FILE_NOT_FOUND, m_info.path() );

        m_working = false;
        emit state( PRINT_STOP );
      }
    }
    else
    {
      // Do nothing
    }
  }
  else
  {
    killTimer( e->timerId() );
  }
}


/////////////////////////////////////////////////////////////////////////////
// SLOT IMPLEMENTATIONS
/////////////////////////////////////////////////////////////////////////////


void Smb4KPrint::slotProcessFinished( int /*exitCode*/, KProcess::ExitStatus exitStatus )
{
  if ( exitStatus == QProcess::NormalExit )
  {
    // Read from stderr and decide what to do:
    QString stderr_output = QString::fromLocal8Bit( m_proc->readAllStandardError(), -1 ).trimmed();

    if ( !stderr_output.isEmpty() )
    {
      // smbspool reports debug info that is no error after the printing to stderr.
      // Avoid showing that output to the user.
      if ( !stderr_output.trimmed().startsWith( "[" ) && !stderr_output.contains( "left in" ) )
      {
        if ( stderr_output.contains( "NT_STATUS_ACCESS_DENIED", Qt::CaseSensitive ) ||
             stderr_output.contains( "NT_STATUS_LOGON_FAILURE", Qt::CaseSensitive ) )
        {
          Smb4KAuthInfo authInfo( m_info.shareItem() );

          if ( Smb4KWalletManager::self()->showPasswordDialog( &authInfo ) )
          {
            m_queue.enqueue( m_info );
          }
        }
        else
        {
          Smb4KCoreMessage::error( ERROR_PRINTING, m_info.path(), stderr_output );

          // Clean up:
          QFile::remove( QString( "%1/smb4k_print.ps" ).arg( tempDir() ) );
        }

        m_working = false;
        emit state( PRINT_STOP );

        return;
      }
      else
      {
        // Only normal debug output was retrieved.
      }
    }
    else
    {
      // If no error occurred, we are done.
    }
  }
  else
  {
    // Something went wrong. Throw an error if the problem was not
    // caused by using the abort() function.
    if ( !m_aborted )
    {
      if ( m_process_error != -1 )
      {
        Smb4KCoreMessage::processError( ERROR_PROCESS_ERROR, m_process_error );
      }
      else
      {
        Smb4KCoreMessage::processError( ERROR_PROCESS_EXIT, m_process_error );
      }
    }
    else
    {
      // Do nothing
    }
  }

  // Clean up:
  QFile::remove( QString( "%1/smb4k_print.ps" ).arg( tempDir() ) );

  m_proc->clearProgram();

  m_process_error = (QProcess::ProcessError)(-1);
  m_working = false;

  emit state( PRINT_STOP );
}


void Smb4KPrint::slotProcessError( KProcess::ProcessError errorCode )
{
  m_process_error = errorCode;
}


#include "smb4kprint.moc"
