/* 
 *
 * $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 "k3bvideodvdtitledetectclippingjob.h"

#include <k3bexternalbinmanager.h>
#include <k3bprocess.h>
#include <k3bcore.h>
#include <k3bglobals.h>

#include <klocale.h>
#include <kdebug.h>


static const int s_unrealisticHighClippingValue = 100000;


class K3bVideoDVDTitleDetectClippingJob::Private
{
public:
  const K3bExternalBin* usedTranscodeBin;

  K3bProcess* process;

  bool canceled;

  unsigned int currentChapter;
  unsigned int currentFrames;
  unsigned int totalChapters;

  int lastProgress;
  int lastSubProgress;
};



K3bVideoDVDTitleDetectClippingJob::K3bVideoDVDTitleDetectClippingJob( K3bJobHandler* hdl, TQObject* parent )
  : K3bJob( hdl, parent ),
    m_clippingTop( 0 ),
    m_clippingBottom( 0 ),
    m_clippingLeft( 0 ),
    m_clippingRight( 0 ),
    m_lowPriority( true )
{
  d = new Private;
  d->process = 0;
}


K3bVideoDVDTitleDetectClippingJob::~K3bVideoDVDTitleDetectClippingJob()
{
  delete d->process;
  delete d;
}


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

  d->canceled = false;
  d->lastProgress = 0;

  //
  // It seems as if the last chapter is often way too short
  //
  d->totalChapters = m_dvd[m_titleNumber-1].numPTTs();
  if( d->totalChapters > 1 && m_dvd[m_titleNumber-1][d->totalChapters-1].playbackTime().totalFrames() < 200 )
    d->totalChapters--;

  // initial values (some way to big value)
  m_clippingTop = s_unrealisticHighClippingValue;
  m_clippingBottom = s_unrealisticHighClippingValue;
  m_clippingLeft = s_unrealisticHighClippingValue;
  m_clippingRight = s_unrealisticHighClippingValue;

  d->usedTranscodeBin = k3bcore->externalBinManager()->binObject("transcode");
  if( !d->usedTranscodeBin ) {
    emit infoMessage( i18n("%1 executable could not be found.").arg("transcode"), ERROR );
    jobFinished( false );
    return;
  }

  if( d->usedTranscodeBin->version < K3bVersion( 1, 0, 0 ) ){
    emit infoMessage( i18n("%1 version %2 is too old.")
		      .arg("transcode")
		      .arg(d->usedTranscodeBin->version), ERROR );
    jobFinished( false );
    return;
  }

  emit debuggingOutput( "Used versions", "transcode: " + d->usedTranscodeBin->version );

  if( !d->usedTranscodeBin->copyright.isEmpty() )
    emit infoMessage( i18n("Using %1 %2 - Copyright (C) %3")
		      .arg(d->usedTranscodeBin->name())
		      .arg(d->usedTranscodeBin->version)
		      .arg(d->usedTranscodeBin->copyright), INFO );

  emit newTask( i18n("Analysing Title %1 of Video DVD %2").arg(m_titleNumber).arg(m_dvd.volumeIdentifier()) );

  startTranscode( 1 );
}


void K3bVideoDVDTitleDetectClippingJob::startTranscode( int chapter )
{
  d->currentChapter = chapter;
  d->lastSubProgress = 0;

  //
  // If we have only one chapter and it is not longer than 2 minutes (value guessed based on some test DVD)
  // use the whole chapter
  //
  if( d->totalChapters == 1 )
    d->currentFrames = TQMIN( 3000, TQMAX( 1, m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );
  else
    d->currentFrames = TQMIN( 200, TQMAX( 1, m_dvd[m_titleNumber-1][d->currentChapter-1].playbackTime().totalFrames() ) );

  //
  // prepare the process
  //
  delete d->process;
  d->process = new K3bProcess();
  d->process->setSuppressEmptyLines(true);
  d->process->setSplitStdout(true);
  //  connect( d->process, TQT_SIGNAL(stderrLine(const TQString&)), this, TQT_SLOT(slotTranscodeStderr(const TQString&)) );
  connect( d->process, TQT_SIGNAL(stdoutLine(const TQString&)), this, TQT_SLOT(slotTranscodeStderr(const TQString&)) );
  connect( d->process, TQT_SIGNAL(processExited(KProcess*)), this, TQT_SLOT(slotTranscodeExited(KProcess*)) );

  // the executable
  *d->process << d->usedTranscodeBin;

  // low priority
  if( m_lowPriority )
    *d->process << "--nice" << "19";

  // the input
  *d->process << "-i" << m_dvd.device()->blockDeviceName();

  // select the title number and chapter
  *d->process << "-T" << TQString("%1,%2").arg(m_titleNumber).arg(chapter);

  // null output
  *d->process << "-y" << "null,null" << "-o" << "/dev/null";

  // analyze the first 200 frames
  *d->process << "-J" << TQString("detectclipping=range=0-%1/5").arg(d->currentFrames);

  // also only decode the first 200 frames
  *d->process << "-c" << TQString("0-%1").arg(d->currentFrames+1);

  // additional user parameters from config
  const TQStringList& params = d->usedTranscodeBin->userParameters();
  for( TQStringList::const_iterator it = params.begin(); it != params.end(); ++it )
    *d->process << *it;

  // produce some debugging output
  kdDebug() << "***** transcode parameters:\n";
  const TQValueList<TQCString>& args = d->process->args();
  TQString s;
  for( TQValueList<TQCString>::const_iterator it = args.begin(); it != args.end(); ++it ) {
    s += *it + " ";
  }
  kdDebug() << s << flush << endl;
  emit debuggingOutput( d->usedTranscodeBin->name() + " command:", s);

  // start the process
  if( !d->process->start( KProcess::NotifyOnExit, KProcess::All ) ) {
    // something went wrong when starting the program
    // it "should" be the executable
    emit infoMessage( i18n("Could not start %1.").arg(d->usedTranscodeBin->name()), K3bJob::ERROR );
    jobFinished(false);
  }
  else {
    emit newSubTask( i18n("Analysing Chapter %1 of %2").arg(chapter).arg(m_dvd[m_titleNumber-1].numPTTs()) );
    emit subPercent( 0 );
  }
}


void K3bVideoDVDTitleDetectClippingJob::cancel()
{
  d->canceled = true;
  if( d->process && d->process->isRunning() )
    d->process->kill();
}


void K3bVideoDVDTitleDetectClippingJob::slotTranscodeStderr( const TQString& line )
{
  emit debuggingOutput( "transcode", line );

  // parse progress
  // encoding frame [185],  24.02 fps, 93.0%, ETA: 0:00:00, ( 0| 0| 0)
  if( line.startsWith( "encoding frame" ) ) {
    int pos1 = line.find( '[', 15 );
    int pos2 = line.find( ']', pos1+1 );
    if( pos1 > 0 && pos2 > 0 ) {
      bool ok;
      int encodedFrames = line.mid( pos1+1, pos2-pos1-1 ).toInt( &ok );
      if( ok ) {
	int progress = 100 * encodedFrames / d->currentFrames;

	if( progress > d->lastSubProgress ) {
	  d->lastSubProgress = progress;
	  emit subPercent( progress );
	}

	double part = 100.0 / (double)d->totalChapters;

	progress = (int)( ( (double)(d->currentChapter-1) * part )
			  + ( (double)progress / (double)d->totalChapters )
			  + 0.5 );

	if( progress > d->lastProgress ) {
	  d->lastProgress = progress;
	  emit percent( progress );
	}
      }
    }
  }

  // [detectclipping#0] valid area: X: 5..719 Y: 72..507  -> -j 72,6,68,0
  else if( line.startsWith( "[detectclipping" ) ) {
    int pos = line.find( "-j" );
    if( pos > 0 ) {
      TQStringList values = TQStringList::split( ',', line.mid( pos+3 ) );
      m_clippingTop = TQMIN( m_clippingTop, values[0].toInt() );
      m_clippingLeft = TQMIN( m_clippingLeft, values[1].toInt() );
      m_clippingBottom = TQMIN( m_clippingBottom, values[2].toInt() );
      m_clippingRight = TQMIN( m_clippingRight, values[3].toInt() );
    }
    else
      kdDebug() << "(K3bVideoDVDTitleDetectClippingJob) failed to parse line: " << line << endl;
  }
}


void K3bVideoDVDTitleDetectClippingJob::slotTranscodeExited( KProcess* p )
{
  switch( p->exitStatus() ) {
  case 0:
    d->currentChapter++;
    if( d->currentChapter > d->totalChapters ) {
      //
      // check if we did set any values at all
      //
      if( m_clippingTop == s_unrealisticHighClippingValue )
	m_clippingTop = m_clippingLeft = m_clippingBottom = m_clippingRight = 0;
      
      if( d->totalChapters < m_dvd[m_titleNumber-1].numPTTs() )
	emit infoMessage( i18n("Ignoring last chapter due to its short playback time."), INFO );

      jobFinished( true );
    }
    else {
      startTranscode( d->currentChapter );
    }
    break;
    
  default:
    // FIXME: error handling

    if( d->canceled ) {
      emit canceled();
    }
    else {
      emit infoMessage( i18n("%1 returned an unknown error (code %2).")
			.arg(d->usedTranscodeBin->name()).arg(p->exitStatus()), 
			K3bJob::ERROR );
      emit infoMessage( i18n("Please send me an email with the last output."), K3bJob::ERROR );
    }

    jobFinished( false );
  }
}

#include "k3bvideodvdtitledetectclippingjob.moc"