/*
 *
 * $Id: k3baudiojob.cpp 690212 2007-07-20 11:02:13Z trueg $
 * Copyright (C) 2003 Sebastian Trueg <trueg@k3b.org>
 *
 * This file is part of the K3b project.
 * Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.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.
 * See the file "COPYING" for the exact licensing terms.
 */


#include "k3baudiojob.h"

#include "k3baudioimager.h"
#include <k3baudiodoc.h>
#include "k3baudiotrack.h"
#include "k3baudiodatasource.h"
#include "k3baudionormalizejob.h"
#include "k3baudiojobtempdata.h"
#include "k3baudiomaxspeedjob.h"
#include "k3baudiocdtracksource.h"
#include "k3baudiofile.h"
#include <k3bdevicemanager.h>
#include <k3bdevicehandler.h>
#include <k3bdevice.h>
#include <k3bcdtext.h>
#include <k3bmsf.h>
#include <k3bglobals.h>
#include <k3bexternalbinmanager.h>
#include <k3bcore.h>
#include <k3bcdrecordwriter.h>
#include <k3bcdrdaowriter.h>
#include <k3btocfilewriter.h>
#include <k3binffilewriter.h>

#include <tqfile.h>
#include <tqvaluevector.h>

#include <kdebug.h>
#include <tdelocale.h>
#include <tdetempfile.h>
#include <kstringhandler.h>



static TQString createNonExistingFilesString( const TQValueList<K3bAudioFile*>& items, unsigned int max )
{
  TQString s;
  unsigned int cnt = 0;
  for( TQValueList<K3bAudioFile*>::const_iterator it = items.begin();
       it != items.end(); ++it ) {

    s += KStringHandler::csqueeze( (*it)->filename(), 60 );

    ++cnt;
    if( cnt >= max || it == items.end() )
      break;

    s += "<br>";
  }

  if( items.count() > max )
    s += "...";

  return s;
}



class K3bAudioJob::Private
{
  public:
  Private()
    : copies(1),
      copiesDone(0) {
  }

  int copies;
  int copiesDone;
  int usedSpeed;

  bool useCdText;
  bool maxSpeed;

  bool zeroPregap;
  bool less4Sec;
};


K3bAudioJob::K3bAudioJob( K3bAudioDoc* doc, K3bJobHandler* hdl, TQObject* parent )
  : K3bBurnJob( hdl, parent ),
    m_doc( doc ),
    m_normalizeJob(0),
    m_maxSpeedJob(0)
{
  d = new Private;

  m_audioImager = new K3bAudioImager( m_doc, this, this );
  connect( m_audioImager, TQ_SIGNAL(infoMessage(const TQString&, int)), 
	   this, TQ_SIGNAL(infoMessage(const TQString&, int)) );
  connect( m_audioImager, TQ_SIGNAL(percent(int)), 
	   this, TQ_SLOT(slotAudioDecoderPercent(int)) );
  connect( m_audioImager, TQ_SIGNAL(subPercent(int)), 
	   this, TQ_SLOT(slotAudioDecoderSubPercent(int)) );
  connect( m_audioImager, TQ_SIGNAL(finished(bool)), 
	   this, TQ_SLOT(slotAudioDecoderFinished(bool)) );
  connect( m_audioImager, TQ_SIGNAL(nextTrack(int, int)), 
	   this, TQ_SLOT(slotAudioDecoderNextTrack(int, int)) );

  m_writer = 0;
  m_tempData = new K3bAudioJobTempData( m_doc, this );
}


K3bAudioJob::~K3bAudioJob()
{
  delete d;
}


K3bDevice::Device* K3bAudioJob::writer() const
{
  if( m_doc->onlyCreateImages() )
    return 0; // no writer needed -> no blocking on K3bBurnJob
  else
    return m_doc->burner();
}


K3bDoc* K3bAudioJob::doc() const
{
  return m_doc;
}


void K3bAudioJob::start()
{
  jobStarted();

  m_written = true;
  m_canceled = false;
  m_errorOccuredAndAlreadyReported = false;
  d->copies = m_doc->copies();
  d->copiesDone = 0;
  d->useCdText = m_doc->cdText();
  d->usedSpeed = m_doc->speed();
  d->maxSpeed = false;

  if( m_doc->dummy() )
    d->copies = 1;

  emit newTask( i18n("Preparing data") );

  //
  // Check if all files exist
  //
  TQValueList<K3bAudioFile*> nonExistingFiles;
  K3bAudioTrack* track = m_doc->firstTrack();
  while( track ) {
    K3bAudioDataSource* source = track->firstSource();
    while( source ) {
      if( K3bAudioFile* file = dynamic_cast<K3bAudioFile*>( source ) ) {
	if( !TQFile::exists( file->filename() ) )
	  nonExistingFiles.append( file );
      }
      source = source->next();
    }
    track = track->next();
  }
  if( !nonExistingFiles.isEmpty() ) {
    if( questionYesNo( "<p>" + i18n("The following files could not be found. Do you want to remove them from the "
				    "project and continue without adding them to the image?") + 
		       "<p>" + createNonExistingFilesString( nonExistingFiles, 10 ),
		       i18n("Warning"),
		       i18n("Remove missing files and continue"),
		       i18n("Cancel and go back") ) ) {
      for( TQValueList<K3bAudioFile*>::const_iterator it = nonExistingFiles.begin();
	   it != nonExistingFiles.end(); ++it ) {
	delete *it;
      }
    }
    else {
      m_canceled = true;
      emit canceled();
      jobFinished(false);
      return;
    }
  }

  //
  // Make sure the project is not empty
  //
  if( m_doc->numOfTracks() == 0 ) {
    emit infoMessage( i18n("Please add files to your project first."), ERROR );
    jobFinished(false);
    return;
  }

  if( m_doc->onTheFly() && !checkAudioSources() ) {
    emit infoMessage( i18n("Unable to write on-the-fly with these audio sources."), WARNING );
    m_doc->setOnTheFly(false);
  }


  // we don't need this when only creating image and it is possible
  // that the burn device is null
  if( !m_doc->onlyCreateImages() ) {

    //
    // there are a lot of writers out there which produce coasters
    // in dao mode if the CD contains pregaps of length 0 (or maybe already != 2 secs?)
    //
    // Also most writers do not accept cuesheets with tracks smaller than 4 seconds (a violation
    // of the red book standard) in DAO mode.
    //
    d->zeroPregap = false;
    d->less4Sec = false;
    track = m_doc->firstTrack();
    while( track ) {
      if( track->postGap() == 0 && track->next() != 0 ) // the last track's postgap is always 0
	d->zeroPregap = true;

      if( track->length() < K3b::Msf( 0, 4, 0 ) )
	d->less4Sec = true;

      track = track->next();
    }

    // determine writing mode
    if( m_doc->writingMode() == K3b::WRITING_MODE_AUTO ) {
      //
      // DAO is always the first choice
      // RAW second and TAO last
      // there are none-DAO writers that are supported by cdrdao
      //
      // older cdrecord versions do not support the -shorttrack option in RAW writing mode
      //
      if( !writer()->dao() && writingApp() == K3b::CDRECORD ) {
	if(!writer()->supportsRawWriting() &&
	   ( !d->less4Sec || k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "short-track-raw" ) ) )
	  m_usedWritingMode = K3b::RAW;
	else
	  m_usedWritingMode = K3b::TAO;
      }
      else {
        if( (d->zeroPregap||d->less4Sec) && writer()->supportsRawWriting() ) {
          m_usedWritingMode = K3b::RAW;
	  if( d->less4Sec )
	    emit infoMessage( i18n("Tracklengths below 4 seconds violate the Red Book standard."), WARNING );
	}
        else
          m_usedWritingMode = K3b::DAO;
      }
    }
    else
      m_usedWritingMode = m_doc->writingMode();

    bool cdrecordOnTheFly = false;
    bool cdrecordCdText = false;
    if( k3bcore->externalBinManager()->binObject("cdrecord") ) {
      cdrecordOnTheFly = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "audio-stdin" );
      cdrecordCdText = k3bcore->externalBinManager()->binObject("cdrecord")->hasFeature( "cdtext" );
    }    

    // determine writing app
    if( writingApp() == K3b::DEFAULT ) {
      if( m_usedWritingMode == K3b::DAO ) {
	// there are none-DAO writers that are supported by cdrdao
	if( !writer()->dao() ||
	    ( !cdrecordOnTheFly && m_doc->onTheFly() ) ||
	    ( d->useCdText && !cdrecordCdText ) ||
	    m_doc->hideFirstTrack() )
	  m_usedWritingApp = K3b::CDRDAO;
	else
	  m_usedWritingApp = K3b::CDRECORD;
      }
      else
	m_usedWritingApp = K3b::CDRECORD;
    }
    else
      m_usedWritingApp = writingApp();

    // on-the-fly writing with cdrecord >= 2.01a13
    if( m_usedWritingApp == K3b::CDRECORD &&
	m_doc->onTheFly() &&
	!cdrecordOnTheFly ) {
      emit infoMessage( i18n("On-the-fly writing with cdrecord < 2.01a13 not supported."), ERROR );
      m_doc->setOnTheFly(false);
    }

    if( m_usedWritingApp == K3b::CDRECORD &&
	d->useCdText ) {
      if( !cdrecordCdText ) {
	emit infoMessage( i18n("Cdrecord %1 does not support CD-Text writing.")
			  .arg(k3bcore->externalBinManager()->binObject("cdrecord")->version), ERROR );
	d->useCdText = false;
      }
      else if( m_usedWritingMode == K3b::TAO ) {
	emit infoMessage( i18n("It is not possible to write CD-Text in TAO mode."), WARNING );
	d->useCdText = false;
      }
    }
  }


  if( !m_doc->onlyCreateImages() && m_doc->onTheFly() ) {
    if( m_doc->speed() == 0 ) {
      // try to determine the max possible speed
      emit newSubTask( i18n("Determining maximum writing speed") );
      if( !m_maxSpeedJob ) {
	m_maxSpeedJob = new K3bAudioMaxSpeedJob( m_doc, this, this );
	connect( m_maxSpeedJob, TQ_SIGNAL(percent(int)), 
		 this, TQ_SIGNAL(subPercent(int)) );
	connect( m_maxSpeedJob, TQ_SIGNAL(finished(bool)), 
		 this, TQ_SLOT(slotMaxSpeedJobFinished(bool)) );
      }
      m_maxSpeedJob->start();
      return;
    }
    else {
      if( !prepareWriter() ) {
	cleanupAfterError();
	jobFinished(false);
	return;
      }
      
      if( startWriting() ) {
	
	// now the writer is running and we can get it's stdin
	// we only use this method when writing on-the-fly since
	// we cannot easily change the audioDecode fd while it's working
	// which we would need to do since we write into several
	// image files.
	m_audioImager->writeToFd( m_writer->fd() );
      }
      else {
	// startWriting() already did the cleanup
	return;
      }
    }
  }
  else {
    emit burning(false);
    emit infoMessage( i18n("Creating image files in %1").arg(m_doc->tempDir()), INFO );
    emit newTask( i18n("Creating image files") );
    m_tempData->prepareTempFileNames( doc()->tempDir() );
    TQStringList filenames;
    for( int i = 1; i <= m_doc->numOfTracks(); ++i )
      filenames += m_tempData->bufferFileName( i );
    m_audioImager->setImageFilenames( filenames );
  }

  m_audioImager->start();
}


void K3bAudioJob::slotMaxSpeedJobFinished( bool success )
{
  d->maxSpeed = success;
  if( !success )
    emit infoMessage( i18n("Unable to determine maximum speed for some reason. Ignoring."), WARNING );

  // now start the writing
  // same code as above. See the commecnts there
  if( !prepareWriter() ) {
    cleanupAfterError();
    jobFinished(false);
    return;
  }
  
  if( startWriting() )
    m_audioImager->writeToFd( m_writer->fd() );

  m_audioImager->start();
}


void K3bAudioJob::cancel()
{
  m_canceled = true;

  if( m_maxSpeedJob )
    m_maxSpeedJob->cancel();

  if( m_writer )
    m_writer->cancel();

  m_audioImager->cancel();
  emit infoMessage( i18n("Writing canceled."), K3bJob::ERROR );
  removeBufferFiles();
  emit canceled();
  jobFinished(false);
}


void K3bAudioJob::slotWriterFinished( bool success )
{
  if( m_canceled || m_errorOccuredAndAlreadyReported )
    return;

  if( !success ) {
    cleanupAfterError();
    jobFinished(false);
    return;
  }
  else {
    d->copiesDone++;

    if( d->copiesDone == d->copies ) {
      if( m_doc->onTheFly() || m_doc->removeImages() )
	removeBufferFiles();

      jobFinished(true);
    }
    else {
      K3bDevice::eject( m_doc->burner() );

      if( startWriting() ) {
	if( m_doc->onTheFly() ) {
	  // now the writer is running and we can get it's stdin
	  // we only use this method when writing on-the-fly since
	  // we cannot easily change the audioDecode fd while it's working
	  // which we would need to do since we write into several
	  // image files.
	  m_audioImager->writeToFd( m_writer->fd() );
	  m_audioImager->start();
	}
      }
    }
  }
}


void K3bAudioJob::slotAudioDecoderFinished( bool success )
{
  if( m_canceled || m_errorOccuredAndAlreadyReported )
    return;

  if( !success ) {
    if( m_audioImager->lastErrorType() == K3bAudioImager::ERROR_FD_WRITE ) {
      // this means that the writer job failed so let's use the error handling there.
      return;
    }

    emit infoMessage( i18n("Error while decoding audio tracks."), ERROR );
    cleanupAfterError();
    jobFinished(false);
    return;
  }

  if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {

    emit infoMessage( i18n("Successfully decoded all tracks."), SUCCESS );

    if( m_doc->normalize() ) {
      normalizeFiles();
    }
    else if( !m_doc->onlyCreateImages() ) {
      if( !prepareWriter() ) {
	cleanupAfterError();
	jobFinished(false);
      }
      else
	startWriting();
    }
    else {
      jobFinished(true);
    }
  }
}


void K3bAudioJob::slotAudioDecoderNextTrack( int t, int tt )
{
  if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
    K3bAudioTrack* track = m_doc->getTrack(t);
    emit newSubTask( i18n("Decoding audio track %1 of %2%3")
		     .arg(t)
		     .arg(tt)
		     .arg( track->title().isEmpty() || track->artist().isEmpty() 
			   ? TQString()
			   : " (" + track->artist() + " - " + track->title() + ")" ) );
  }
}


bool K3bAudioJob::prepareWriter()
{
  delete m_writer;

  if( m_usedWritingApp == K3b::CDRECORD ) {

    if( !writeInfFiles() ) {
      kdDebug() << "(K3bAudioJob) could not write inf-files." << endl;
      emit infoMessage( i18n("IO Error. Most likely no space left on harddisk."), ERROR );

      return false;
    }

    K3bCdrecordWriter* writer = new K3bCdrecordWriter( m_doc->burner(), this, this );

    writer->setWritingMode( m_usedWritingMode );
    writer->setSimulate( m_doc->dummy() );
    writer->setBurnSpeed( d->usedSpeed );

    writer->addArgument( "-useinfo" );

    if( d->useCdText ) {
      writer->setRawCdText( m_doc->cdTextData().rawPackData() );
    }

    // add all the audio tracks
    writer->addArgument( "-audio" );

    // we only need to pad in one case. cdrecord < 2.01.01a03 cannot handle shorttrack + raw
    if( d->less4Sec ) {
      if( m_usedWritingMode == K3b::RAW &&
	  !k3bcore->externalBinManager()->binObject( "cdrecord" )->hasFeature( "short-track-raw" ) ) {
	writer->addArgument( "-pad" );
      }
      else {
	// Allow tracks shorter than 4 seconds
	writer->addArgument( "-shorttrack" );
      }
    }

    K3bAudioTrack* track = m_doc->firstTrack();
    while( track ) {
      if( m_doc->onTheFly() ) {
	// this is only supported by cdrecord versions >= 2.01a13
	writer->addArgument( TQFile::encodeName( m_tempData->infFileName( track ) ) );
      }
      else {
	writer->addArgument( TQFile::encodeName( m_tempData->bufferFileName( track ) ) );
      }
      track = track->next();
    }

    m_writer = writer;
  }
  else {
    if( !writeTocFile() ) {
      kdDebug() << "(K3bDataJob) could not write tocfile." << endl;
      emit infoMessage( i18n("IO Error"), ERROR );

      return false;
    }

    // create the writer
    // create cdrdao job
    K3bCdrdaoWriter* writer = new K3bCdrdaoWriter( m_doc->burner(), this, this );
    writer->setCommand( K3bCdrdaoWriter::WRITE );
    writer->setSimulate( m_doc->dummy() );
    writer->setBurnSpeed( d->usedSpeed );
    writer->setTocFile( m_tempData->tocFileName() );

    m_writer = writer;
  }

  connect( m_writer, TQ_SIGNAL(infoMessage(const TQString&, int)), this, TQ_SIGNAL(infoMessage(const TQString&, int)) );
  connect( m_writer, TQ_SIGNAL(percent(int)), this, TQ_SLOT(slotWriterJobPercent(int)) );
  connect( m_writer, TQ_SIGNAL(processedSize(int, int)), this, TQ_SIGNAL(processedSize(int, int)) );
  connect( m_writer, TQ_SIGNAL(subPercent(int)), this, TQ_SIGNAL(subPercent(int)) );
  connect( m_writer, TQ_SIGNAL(processedSubSize(int, int)), this, TQ_SIGNAL(processedSubSize(int, int)) );
  connect( m_writer, TQ_SIGNAL(nextTrack(int, int)), this, TQ_SLOT(slotWriterNextTrack(int, int)) );
  connect( m_writer, TQ_SIGNAL(buffer(int)), this, TQ_SIGNAL(bufferStatus(int)) );
  connect( m_writer, TQ_SIGNAL(deviceBuffer(int)), this, TQ_SIGNAL(deviceBuffer(int)) );
  connect( m_writer, TQ_SIGNAL(writeSpeed(int, int)), this, TQ_SIGNAL(writeSpeed(int, int)) );
  connect( m_writer, TQ_SIGNAL(finished(bool)), this, TQ_SLOT(slotWriterFinished(bool)) );
  //  connect( m_writer, TQ_SIGNAL(newTask(const TQString&)), this, TQ_SIGNAL(newTask(const TQString&)) );
  connect( m_writer, TQ_SIGNAL(newSubTask(const TQString&)), this, TQ_SIGNAL(newSubTask(const TQString&)) );
  connect( m_writer, TQ_SIGNAL(debuggingOutput(const TQString&, const TQString&)),
	   this, TQ_SIGNAL(debuggingOutput(const TQString&, const TQString&)) );

  return true;
}


void K3bAudioJob::slotWriterNextTrack( int t, int tt )
{
  K3bAudioTrack* track = m_doc->getTrack(t);
  // t is in range 1..tt
  if( m_doc->hideFirstTrack() )
    track = m_doc->getTrack(t+1);
  emit newSubTask( i18n("Writing track %1 of %2%3")
		   .arg(t)
		   .arg(tt)
		   .arg( track->title().isEmpty() || track->artist().isEmpty() 
			 ? TQString()
			 : " (" + track->artist() + " - " + track->title() + ")" ) );
}


void K3bAudioJob::slotWriterJobPercent( int p )
{
  double totalTasks = d->copies;
  double tasksDone = d->copiesDone;
  if( m_doc->normalize() ) {
    totalTasks+=1.0;
    tasksDone+=1.0;
  }
  if( !m_doc->onTheFly() ) {
    totalTasks+=1.0;
    tasksDone+=1.0;
  }

  emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
}


void K3bAudioJob::slotAudioDecoderPercent( int p )
{
  if( m_doc->onlyCreateImages() ) {
    if( m_doc->normalize() )
      emit percent( p/2 );
    else
      emit percent( p );
  }
  else if( !m_doc->onTheFly() ) {
    double totalTasks = d->copies;
    double tasksDone = d->copiesDone; // =0 when creating an image
    if( m_doc->normalize() ) {
      totalTasks+=1.0;
    }
    if( !m_doc->onTheFly() ) {
      totalTasks+=1.0;
    }

    emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
  }
}


void K3bAudioJob::slotAudioDecoderSubPercent( int p )
{
  // when writing on the fly the writer produces the subPercent
  if( m_doc->onlyCreateImages() || !m_doc->onTheFly() ) {
    emit subPercent( p );
  }
}


bool K3bAudioJob::startWriting()
{
  if( m_doc->dummy() )
    emit newTask( i18n("Simulating") );
  else if( d->copies > 1 )
    emit newTask( i18n("Writing Copy %1").arg(d->copiesDone+1) );
  else
    emit newTask( i18n("Writing") );


  emit newSubTask( i18n("Waiting for media") );
  if( waitForMedia( m_doc->burner() ) < 0 ) {
    cancel();
    return false;
  }

  // just to be sure we did not get canceled during the async discWaiting
  if( m_canceled )
    return false;

  // in case we determined the max possible writing speed we have to reset the speed on the writer job
  // here since an inserted media is necessary
  // the Max speed job will compare the max speed value with the supported values of the writer
  if( d->maxSpeed )
    m_writer->setBurnSpeed( m_maxSpeedJob->maxSpeed() );

  emit burning(true);
  m_writer->start();
  return true;
}


void K3bAudioJob::cleanupAfterError()
{
  m_errorOccuredAndAlreadyReported = true;
  m_audioImager->cancel();

  if( m_writer )
    m_writer->cancel();

  // remove the temp files
  removeBufferFiles();
}


void K3bAudioJob::removeBufferFiles()
{
    if ( !m_doc->onTheFly() ) {
        emit infoMessage( i18n("Removing temporary files."), INFO );
    }

    // removes buffer images and temp toc or inf files
    m_tempData->cleanup();
}


void K3bAudioJob::normalizeFiles()
{
  if( !m_normalizeJob ) {
    m_normalizeJob = new K3bAudioNormalizeJob( this, this );

    connect( m_normalizeJob, TQ_SIGNAL(infoMessage(const TQString&, int)),
	     this, TQ_SIGNAL(infoMessage(const TQString&, int)) );
    connect( m_normalizeJob, TQ_SIGNAL(percent(int)), this, TQ_SLOT(slotNormalizeProgress(int)) );
    connect( m_normalizeJob, TQ_SIGNAL(subPercent(int)), this, TQ_SLOT(slotNormalizeSubProgress(int)) );
    connect( m_normalizeJob, TQ_SIGNAL(finished(bool)), this, TQ_SLOT(slotNormalizeJobFinished(bool)) );
    connect( m_normalizeJob, TQ_SIGNAL(newTask(const TQString&)), this, TQ_SIGNAL(newSubTask(const TQString&)) );
    connect( m_normalizeJob, TQ_SIGNAL(debuggingOutput(const TQString&, const TQString&)),
	     this, TQ_SIGNAL(debuggingOutput(const TQString&, const TQString&)) );
  }

  // add all the files
  // TODO: we may need to split the wave files and put them back together!
  TQValueVector<TQString> files;
  K3bAudioTrack* track = m_doc->firstTrack();
  while( track ) {
    files.append( m_tempData->bufferFileName(track) );
    track = track->next();
  }

  m_normalizeJob->setFilesToNormalize( files );

  emit newTask( i18n("Normalizing volume levels") );
  m_normalizeJob->start();
}

void K3bAudioJob::slotNormalizeJobFinished( bool success )
{
  if( m_canceled || m_errorOccuredAndAlreadyReported )
    return;

  if( success ) {
    if( m_doc->onlyCreateImages() ) {
      jobFinished(true);
    }
    else {
      // start the writing
      if( !prepareWriter() ) {
	cleanupAfterError();
	jobFinished(false);
      }
      else
	startWriting();
    }
  }
  else {
    cleanupAfterError();
    jobFinished(false);
  }
}

void K3bAudioJob::slotNormalizeProgress( int p )
{
  double totalTasks = d->copies+2.0;
  double tasksDone = 1; // the decoding has been finished
  
  emit percent( (int)((100.0*tasksDone + (double)p) / totalTasks) );
}


void K3bAudioJob::slotNormalizeSubProgress( int p )
{
  emit subPercent( p );
}


bool K3bAudioJob::writeTocFile()
{
  K3bTocFileWriter tocWriter;
  tocWriter.setData( m_doc->toToc() );
  tocWriter.setHideFirstTrack( m_doc->hideFirstTrack() );
  if( d->useCdText )
    tocWriter.setCdText( m_doc->cdTextData() );
  if( !m_doc->onTheFly() ) {
    TQStringList filenames;
    for( int i = 1; i <= m_doc->numOfTracks(); ++i )
      filenames += m_tempData->bufferFileName( i );
    tocWriter.setFilenames( filenames );
  }
  return tocWriter.save( m_tempData->tocFileName() );
}


bool K3bAudioJob::writeInfFiles()
{
  K3bInfFileWriter infFileWriter;
  K3bAudioTrack* track = m_doc->firstTrack();
  while( track ) {

    infFileWriter.setTrack( track->toCdTrack() );
    infFileWriter.setTrackNumber( track->trackNumber() );
    if( !m_doc->onTheFly() )
      infFileWriter.setBigEndian( false );

    if( !infFileWriter.save( m_tempData->infFileName(track) ) )
      return false;

    track = track->next();
  }
  return true;
}


// checks if the doc contains sources from an audio cd which cannot be read on-the-fly
bool K3bAudioJob::checkAudioSources()
{
  K3bAudioTrack* track = m_doc->firstTrack();
  K3bAudioDataSource* source = track->firstSource();
  
  while( source ) {

    if( K3bAudioCdTrackSource* cdSource = dynamic_cast<K3bAudioCdTrackSource*>(source) ) {
      //
      // If which cases we cannot wite on-the-fly:
      // 1. the writing device contains one of the audio cds
      // 2. Well, one of the cds is missing
      //
      K3bDevice::Device* dev = cdSource->searchForAudioCD();
      if( !dev || dev == writer() )
	return false;
      else
	cdSource->setDevice( dev );
    }

    // next source
    source = source->next();
    if( !source ) {
      track = track->next();
      if( track )
	source = track->firstSource();
    }
  }

  return true;
}


TQString K3bAudioJob::jobDescription() const
{
  return i18n("Writing Audio CD")
    + ( m_doc->title().isEmpty() 
	? TQString()
	: TQString( " (%1)" ).arg(m_doc->title()) );
}


TQString K3bAudioJob::jobDetails() const
{
  return ( i18n( "1 track (%1 minutes)", 
		 "%n tracks (%1 minutes)", 
		 m_doc->numOfTracks() ).arg(m_doc->length().toString())
	   + ( m_doc->copies() > 1 && !m_doc->dummy()
	       ? i18n(" - %n copy", " - %n copies", m_doc->copies()) 
	       : TQString() ) );
}

#include "k3baudiojob.moc"
