/* 
 *
 * $Id: sourceheader 511311 2006-02-19 14:51:05Z trueg $
 * Copyright (C) 2006 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 "k3bvideodvdrippingjob.h"

#include <k3bvideodvdtitletranscodingjob.h>
#include <k3bvideodvdtitledetectclippingjob.h>

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


K3bVideoDVDRippingJob::TitleRipInfo::TitleRipInfo()
  : title(1),
    audioStream(0),
    width(0),
    height(0),
    videoBitrate(0),
    clipTop(0),
    clipLeft(0),
    clipBottom(0),
    clipRight(0)
{
}


K3bVideoDVDRippingJob::TitleRipInfo::TitleRipInfo( int _title,
						   int _audioStream,
						   const TQString& fn,
						   int _width,
						   int _height,
						   int _videoBitrate,
						   int _clipTop,
						   int _clipLeft,
						   int _clipBottom,
						   int _clipRight )
  : title(_title),
    audioStream(_audioStream),
    filename(fn),
    width(_width),
    height(_height),
    videoBitrate(_videoBitrate),
    clipTop(_clipTop),
    clipLeft(_clipLeft),
    clipBottom(_clipBottom),
    clipRight(_clipRight)
{
}



class K3bVideoDVDRippingJob::Private {
public:
  Private()
    : autoClipping( true ) {
  }

  unsigned int currentTitleInfoIndex;
  bool autoClipping;

  bool canceled;

  int videoBitrate;

  int failedTitles;

  TQValueVector<double> titleProgressParts;
  TQValueVector<double> titleClippingProgressParts;
};



K3bVideoDVDRippingJob::K3bVideoDVDRippingJob( K3bJobHandler* hdl, TQObject* parent )
  : K3bJob( hdl, parent )
{
  d = new Private();

  m_transcodingJob = new K3bVideoDVDTitleTranscodingJob( this, this );
  connectSubJob( m_transcodingJob,
		 TQ_SLOT(slotTranscodingJobFinished(bool)),
		 TQ_SIGNAL(newTask(const TQString&)),
		 TQ_SIGNAL(newSubTask(const TQString&)),
		 TQ_SLOT(slotTranscodingProgress(int)),
		 TQ_SIGNAL(subPercent(int)),
		 0,
		 0 );
  m_detectClippingJob = 0;
}


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


TQString K3bVideoDVDRippingJob::jobDescription() const
{
  return i18n("Ripping Video DVD Titles");
}


TQString K3bVideoDVDRippingJob::jobDetails() const
{
  return i18n("Transcoding %n title to %1/%2", "Transcoding %n titles to %1/%2", m_titleRipInfos.count() )
    .arg( K3bVideoDVDTitleTranscodingJob::videoCodecString( m_transcodingJob->videoCodec() ) )
    .arg( K3bVideoDVDTitleTranscodingJob::audioCodecString( m_transcodingJob->audioCodec() ) );
}


void K3bVideoDVDRippingJob::start()
{
  jobStarted();
  d->canceled = false;
  d->failedTitles = 0;

  initProgressInfo();

  if( d->autoClipping )
    startDetectClipping( 0 );
  else
    startTranscoding( 0 );
}


void K3bVideoDVDRippingJob::slotTranscodingJobFinished( bool success )
{
  if( d->canceled ) {
    emit canceled();
    jobFinished( false );
  }
  else {
    if( success )
      emit infoMessage( i18n("Successfully ripped title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), SUCCESS );
    else {
      d->failedTitles++;
      emit infoMessage( i18n("Failed to rip title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), ERROR );
    }

    ++d->currentTitleInfoIndex ;
    if( d->currentTitleInfoIndex < m_titleRipInfos.count() ) {
      if( d->autoClipping )
	startDetectClipping( d->currentTitleInfoIndex );
      else
	startTranscoding( d->currentTitleInfoIndex );
    }
    else {
      jobFinished( d->failedTitles == 0 );
    }
  }
}


void K3bVideoDVDRippingJob::slotDetectClippingJobFinished( bool success )
{
  if( d->canceled ) {
    emit canceled();
    jobFinished( false );
  }
  else {
    m_titleRipInfos[d->currentTitleInfoIndex].clipTop = 0;
    m_titleRipInfos[d->currentTitleInfoIndex].clipLeft = 0;
    m_titleRipInfos[d->currentTitleInfoIndex].clipBottom = 0;
    m_titleRipInfos[d->currentTitleInfoIndex].clipRight = 0;

    if( success ) {
      emit infoMessage( i18n("Determined clipping values for title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), SUCCESS );
      emit infoMessage( i18n("Top: %1, Bottom: %2")
			.arg(m_detectClippingJob->clippingTop()).arg(m_detectClippingJob->clippingBottom()), INFO );
      emit infoMessage( i18n("Left: %1, Right: %2")
			.arg(m_detectClippingJob->clippingLeft()).arg(m_detectClippingJob->clippingRight()), INFO );

      // let's see if the clipping values make sense
      if( m_detectClippingJob->clippingTop() + m_detectClippingJob->clippingBottom() 
	  >= (int)m_dvd[d->currentTitleInfoIndex].videoStream().pictureHeight() ||
	  m_detectClippingJob->clippingLeft() + m_detectClippingJob->clippingRight()
	  >= (int)m_dvd[d->currentTitleInfoIndex].videoStream().pictureWidth() ) {
	emit infoMessage( i18n("Insane clipping values. No clipping will be done at all."), WARNING );
      }
      else {
	m_titleRipInfos[d->currentTitleInfoIndex].clipTop = m_detectClippingJob->clippingTop();
	m_titleRipInfos[d->currentTitleInfoIndex].clipLeft = m_detectClippingJob->clippingLeft();
	m_titleRipInfos[d->currentTitleInfoIndex].clipBottom = m_detectClippingJob->clippingBottom();
	m_titleRipInfos[d->currentTitleInfoIndex].clipRight = m_detectClippingJob->clippingRight();
      }
    }
    else
      emit infoMessage( i18n("Failed to determine clipping values for title %1").arg(m_titleRipInfos[d->currentTitleInfoIndex].title), ERROR );

    startTranscoding( d->currentTitleInfoIndex );
  }
}


void K3bVideoDVDRippingJob::startTranscoding( int ripInfoIndex )
{
  d->currentTitleInfoIndex = ripInfoIndex;

  m_transcodingJob->setVideoDVD( m_dvd );
  m_transcodingJob->setTitle( m_titleRipInfos[ripInfoIndex].title );
  m_transcodingJob->setAudioStream( m_titleRipInfos[ripInfoIndex].audioStream );
  m_transcodingJob->setClipping( m_titleRipInfos[ripInfoIndex].clipTop,
				 m_titleRipInfos[ripInfoIndex].clipLeft,
				 m_titleRipInfos[ripInfoIndex].clipBottom,
				 m_titleRipInfos[ripInfoIndex].clipRight );
  m_transcodingJob->setSize( m_titleRipInfos[ripInfoIndex].width, m_titleRipInfos[ripInfoIndex].height );
  m_transcodingJob->setFilename( m_titleRipInfos[ripInfoIndex].filename );

  if( m_titleRipInfos[ripInfoIndex].videoBitrate > 0 )
    m_transcodingJob->setVideoBitrate( m_titleRipInfos[ripInfoIndex].videoBitrate );
  else
    m_transcodingJob->setVideoBitrate( d->videoBitrate );

  m_transcodingJob->start();
}


void K3bVideoDVDRippingJob::startDetectClipping( int ripInfoIndex )
{
  d->currentTitleInfoIndex = ripInfoIndex;

  if( !m_detectClippingJob ) {
    m_detectClippingJob = new K3bVideoDVDTitleDetectClippingJob( this, this );
    connectSubJob( m_detectClippingJob,
		   TQ_SLOT(slotDetectClippingJobFinished(bool)),
		   TQ_SIGNAL(newTask(const TQString&)),
		   TQ_SIGNAL(newSubTask(const TQString&)),
		   TQ_SLOT(slotDetectClippingProgress(int)),
		   TQ_SIGNAL(subPercent(int)),
		   0,
		   0 );
  }

  m_detectClippingJob->setVideoDVD( m_dvd );
  m_detectClippingJob->setTitle( m_titleRipInfos[ripInfoIndex].title );
  m_detectClippingJob->setLowPriority( m_transcodingJob->lowPriority() );

  m_detectClippingJob->start();
}


void K3bVideoDVDRippingJob::slotTranscodingProgress( int p )
{
  // calculate the part already done
  double doneParts = 0.0;
  for( unsigned int i = 0; i < d->currentTitleInfoIndex; ++i ) {
    doneParts += d->titleProgressParts[i];
    if( d->autoClipping )
      doneParts += d->titleClippingProgressParts[i];
  }
  if( d->autoClipping )
    doneParts += d->titleClippingProgressParts[d->currentTitleInfoIndex];

  // and the current thing
  doneParts += (double)p/100.0*d->titleProgressParts[d->currentTitleInfoIndex];

  emit percent( (int)( 100.0*doneParts ) );
}


void K3bVideoDVDRippingJob::slotDetectClippingProgress( int p )
{
  // calculate the part already done
  double doneParts = 0.0;
  for( unsigned int i = 0; i < d->currentTitleInfoIndex; ++i ) {
    doneParts += d->titleProgressParts[i];
    doneParts += d->titleClippingProgressParts[i];
  }

  // and the current thing
  doneParts += (double)p/100.0*d->titleClippingProgressParts[d->currentTitleInfoIndex];

  emit percent( (int)( 100.0*doneParts ) );
}


void K3bVideoDVDRippingJob::cancel()
{
  d->canceled = true;
  if( m_transcodingJob->active() )
    m_transcodingJob->cancel();
  else if( m_detectClippingJob && m_detectClippingJob->active() )
    m_detectClippingJob->cancel();
}


void K3bVideoDVDRippingJob::setVideoCodec( K3bVideoDVDTitleTranscodingJob::VideoCodec codec )
{
  m_transcodingJob->setVideoCodec( codec );
}


void K3bVideoDVDRippingJob::setVideoBitrate( int bitrate )
{
  d->videoBitrate = bitrate;
}


void K3bVideoDVDRippingJob::setTwoPassEncoding( bool b )
{
  m_transcodingJob->setTwoPassEncoding( b );
}


void K3bVideoDVDRippingJob::setAudioCodec( K3bVideoDVDTitleTranscodingJob::AudioCodec codec )
{
  m_transcodingJob->setAudioCodec( codec );
}


void K3bVideoDVDRippingJob::setAudioBitrate( int bitrate )
{
  m_transcodingJob->setAudioBitrate( bitrate );
}


void K3bVideoDVDRippingJob::setAudioVBR( bool vbr )
{
  m_transcodingJob->setAudioVBR( vbr );
}


void K3bVideoDVDRippingJob::setResampleAudioTo44100( bool b )
{
  m_transcodingJob->setResampleAudioTo44100( b );
}


void K3bVideoDVDRippingJob::setLowPriority( bool b )
{
  m_transcodingJob->setLowPriority( b );
}


void K3bVideoDVDRippingJob::setAutoClipping( bool b )
{
  d->autoClipping = b;
}


void K3bVideoDVDRippingJob::initProgressInfo()
{
  d->titleProgressParts.resize( m_titleRipInfos.count() );
  d->titleClippingProgressParts.resize( m_titleRipInfos.count() );

  unsigned long long totalFrames = 0ULL;
  for( unsigned int i = 0; i < m_titleRipInfos.count(); ++i ) {
    if( m_transcodingJob->twoPassEncoding() )
      totalFrames += m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames() * 2;
    else
      totalFrames += m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames();

    // using my knowledge of the internals of the clipping detection job: it decodes 200 frames
    // of every chapter
    if( d->autoClipping )
      totalFrames += m_dvd[m_titleRipInfos[i].title-1].numChapters() * 200;
  }

  for( unsigned int i = 0; i < m_titleRipInfos.count(); ++i ) {
    unsigned long long titleFrames = m_dvd[m_titleRipInfos[i].title-1].playbackTime().totalFrames();
    if( m_transcodingJob->twoPassEncoding() )
      titleFrames *= 2;

    // using my knowledge of the internals of the clipping detection job: it decodes 200 frames
    // of every chapter
    unsigned long long titleClippingFrames = m_dvd[m_titleRipInfos[i].title-1].numChapters() * 200;

    d->titleProgressParts[i] = (double)titleFrames/(double)totalFrames;
    d->titleClippingProgressParts[i] = (double)titleClippingFrames/(double)totalFrames;
  }
}

#include "k3bvideodvdrippingjob.moc"
