//
// C++ Implementation: k9mp4enc
//
// Description:
//
//
// Author: Jean-Michel PETIT <k9copy@free.fr>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "k9mp4enc.h"
#include "k9mp4dlg.h"
#include "k9config.h"
#include <tqcstring.h>
#include <tqapplication.h>
#include <tdelocale.h>
#include <tqstringlist.h>
#include <tqdir.h>
#include <tdefiledialog.h>
#include <tdemessagebox.h>
#include <tqstringlist.h>
#include <tdetempfile.h>
#include <kstandarddirs.h>
#include "k9tools.h"
#include "k9audiocodecs.h"
#include "k9videocodecs.h"
#include <tqcstring.h>

k9MP4Enc::k9MP4Enc ( TQObject *parent, const char *name,const TQStringList& )
		: TQObject ( parent, name )
{
	m_fourcc=m_height=m_width=m_audioBitrate=m_videoBitrate=m_filename="";
	m_codec=0; //lavc_mp4;
	m_audioCodec=0;
	m_cpt=-1;
	m_parts=1;

	TQStringList laudio;
	TQStringList llabels;
	TQStringList lvideo;

	k9Config config;
	m_lstAudio=config.getCodecAudio();
	m_lstCodecs=config.getCodecLabels();
	m_lstVideo=config.getCodecVideo();

	timer = new TQTimer ( this );
	connect ( timer, TQT_SIGNAL ( timeout() ), this, TQT_SLOT ( timerDone() ) );
	m_progress=new k9MP4Dlg ( tqApp->mainWidget(),0 );

}

TQString k9MP4Enc::round16 ( TQString _wh )
{
	if ( _wh !="" )
	{
		int value=_wh.toInt() /16;
		return TQString::number ( value*16 );

	}
	else
		return _wh;


}

TQString k9MP4Enc::getChapterList ( k9DVDTitle *_title )
{
	TQString res="";
	TQPtrList <k9DVDChapter> chapters=_title->getChapters();
	for ( k9DVDChapter *chapter=chapters.first();chapter;chapter=chapters.next() )
	{
		if ( chapter->getSelected() )
		{
			res+=res=="" ? TQString::number ( chapter->getnum() ) : ","+TQString::number ( chapter->getnum() );
		}
	}
	TQPtrList <k9DVDTitle> titles=_title->getTitles();

	for ( k9DVDTitle *title=titles.first();title;title=titles.next() )
	{
		chapters=title->getChapters();
		for ( k9DVDChapter *chapter=chapters.first();chapter;chapter=chapters.next() )
		{
			if ( chapter->getSelected() )
			{
				res+=res=="" ? TQString::number ( chapter->getnum() ) : ","+TQString::number ( chapter->getnum() );
			}
		}
	}
	return res;

}


int k9MP4Enc::getselectedSubp ( k9DVDTitle *_title )
{
	for ( int i=0;i< _title->getsubPictureCount();i++ )
	{
		if ( _title->getsubtitle ( i )->getselected() )
		{
			return _title->getsubtitle ( i )->getID().first()-1;
		}
	}
	//nos subtitle selected
	return -1;
}



void k9MP4Enc::execute ( k9DVDTitle *_title )
{
	bool error=false;

	if ( m_mpeg2 )
	{
		m_parts=1;
		m_2pass=false;
	}

	if ( ! k9Tools::checkProgram ( "mencoder" ) && ! m_mpeg2 )
	{
		KMessageBox::error ( tqApp->mainWidget(),i18n ( "Unable to run %1" ).arg ( "mencoder" ) , i18n ( "Encoding error" ) );
		error = TRUE;
		return;
	}

	time = new TQTime ( 0,0 );
	m_percent=0;
	m_remain="--:--:--";

	m_totalSize=_title->getChaptersSize ( true );

	TQString injectName;
	KTempFile injectFile ( locateLocal ( "tmp", "k9copy/k9v" ), "" );
	injectFile.setAutoDelete ( true );
	injectFile.close();
	injectName=injectFile.name();


	int maxPass=0;
	int pass=0;

	//build the cell list for mpeg2 extraction
	TQMap<TQString,int> chapterCells;
	TQMap<TQString, int>::iterator ichapterCells;
	TQStringList chapters;
	if ( m_mpeg2 && m_mpegChapters )
	{
		m_parts=0;
		chapters=TQStringList::split ( ",", getChapterList ( _title ) );
		for ( unsigned int idxChap = 0; idxChap < chapters.size(); idxChap++ )
		{
			TQString chapter = chapters[idxChap];
			//foreach (TQString chapter,chapters) {
			int iCell=0;
			k9DVDChapter *chap=_title->getChapter ( chapter.toInt()-1 );
			//foreach(k9ChapterCell *cell ,chap->cells) {
			iCell++;
			chapterCells.insert ( chapter,iCell );
			m_parts++;
			//}
		}
		ichapterCells = chapterCells.begin();
	}

	for ( int m_part =1 ; ( m_part <=m_parts ) && !error ;m_part++ )
	{
		if ( m_2pass )
		{
			maxPass=2;
			pass=1;
		}
		KTempFile passLogFile ( locateLocal ( "tmp", "k9copy/k9v" ), "" );
		passLogFile.setAutoDelete ( true );
		passLogFile.close();

		do
		{
			uint32_t nbSectors= m_totalSize / m_parts   ;

			uint32_t startSector= nbSectors* ( m_part-1 );
			uint32_t endSector= startSector+nbSectors;

			//calculer le bitrate en faisant la somme des cells compris entre startSector et endSector
			//FIXME Mettre en place la sélection par chapitres
			m_stderr="";
			m_title=_title;
			if ( m_height=="" || m_height=="0" )
				m_height="-2";
			if ( m_width=="" )
				m_width="640";
			if ( m_audioBitrate=="" )
				m_audioBitrate="128";
			if ( m_size=="" )
				m_size="700";
			if ( m_filename=="" )
				m_filename=KFileDialog::getSaveFileName ( TQDir::homeDirPath(),"*.avi", 0,i18n ( "Save file to disk" ) );
			if ( m_filename =="" )
				return;

			TQDir d=TQDir::root();
			if ( d.exists ( m_filename ) )
				d.remove ( m_filename );

			m_progress->setbitrate ( TQString::number ( getBitRate ( _title ) ) );
			if ( !m_mpeg2 )
				m_progress->setsize ( m_size +i18n ( "MB" ) +" X " +TQString::number ( m_parts ) );
			else
				m_progress->setsize ( m_size +i18n ( "MB" ) +" X " );

			m_process=new k9Process ( this,0 );
			m_process->setUseShell ( true );
			*m_process << "k9copy" << "--play" << "--endsector" << TQString::number ( endSector ) ;
			*m_process << "--inject" << injectName; //"/tmp/tde-jmp/inject";
			*m_process << "--input" << "'"+m_device+"'";
			*m_process << "--dvdtitle" << TQString::number ( _title->getnumTitle() );

			if ( m_mpegChapters && m_mpeg2 )
			{
				*m_process << "--chapter" << ichapterCells.key();//chapters.at(m_part-1);
				//*m_process << "--cell" << TQString::number(ichapterCells.value());
			}
			else
				*m_process << "--chapterlist" << getChapterList ( _title );

			if ( m_part==1 || m_mpeg2 )
				*m_process << "--initstatus";
			else
				*m_process << "--continue";

			if ( pass==1 )
				*m_process << "--firstpass";

			for ( int i=0;i<_title->getaudioStreamCount();i++ )
			{
				if ( _title->getaudioStream ( i )->getselected() )
				{
					*m_process << "--audiofilter" << TQString::number ( _title->getaudioStream ( i )->getID() );
					break;
				}
			}

			if ( getselectedSubp ( _title ) !=-1 )
			{
				*m_process << "--subpicturefilter" ;
				TQString s="";
				for ( int i=1; i<=_title->getsubPictureCount();i++ )
					s+= ( i>1?",":"" ) + TQString::number ( i );
				*m_process << s;
			}

			if ( m_usecache )
				*m_process << "--usecache";

			if ( m_mpeg2 )
			{
				m_progress->setbitrate ( "--" );
				double size;

				if ( _title->getforceFactor() )
				{
					size = _title->getChaptersSize_mb ( true ) /_title->getfactor();
					*m_process << "--vampsfactor" << TQString::number ( _title->getfactor() ) << "--ffactor";
				}
				else
					size = _title->getChaptersSize_mb ( true );
				m_progress->setsize ( TQString::number ( size ) +i18n ( "MB" ) );
				TQString path=m_filename;
				if ( m_parts>1 )
				{
					TQString ext=m_filename.section ( ".",-1 );
					if ( ext!="" )
						ext="."+ext;
					path=m_filename.left ( m_filename.length()-ext.length() );
					//path=TQString("%1-chapter%2-cell%3%4").arg(path).arg(ichapterCells.key()).arg(ichapterCells.value()).arg(ext);
					path=TQString ( "%1-chapter%2%3" ).arg ( path ).arg ( ichapterCells.key() ).arg ( ext );
					++ichapterCells;
				}
				*m_process << "> "+path;
			}
			else
			{
				*m_process << "| mencoder" << "/dev/stdin";
				*m_process << "-passlogfile" << passLogFile.name();

				bool audio=false;
				TQString sPass="";
				TQString sCodec="";

				k9AudioCodecs *audioCodecs=new k9AudioCodecs ( 0,0 );
				k9VideoCodecs *videoCodecs=new k9VideoCodecs ( 0,0 );

				TQString sVOption;
				m_pass=pass;
				switch ( pass )
				{
					case 1:
						sVOption=replaceParams ( videoCodecs->getOptions1 ( m_codec ) );
						break;
					case 2:
						sVOption=replaceParams ( videoCodecs->getOptions2 ( m_codec ) );
						break;
					default:
						sVOption=replaceParams ( videoCodecs->getOptions0 ( m_codec ) );
						break;
				}
				sCodec=videoCodecs->getCodecName ( m_codec );
				sVOption=sVOption.simplifyWhiteSpace();
				int pos;
				//*m_process << "-ovc" << sVOption;
				/* int pos=sVOption.find("-vf");
				if (pos==-1)
				    *m_process <<"-vf" << TQString("scale=%1:%2").arg(m_width).arg(m_height);
				else
				     sVOption=sVOption.insert(pos+4,TQString("scale=%1:%2,").arg(m_width).arg(m_height));
				 */
				*m_process  << sVOption;

				TQString sAOption=replaceParams ( audioCodecs->getOptions ( m_audioCodec ) ).simplifyWhiteSpace();



				if ( pass >0 )
					m_progress->setTitleLabel ( i18n ( "Encoding %1" ).arg ( sCodec ) +" - "+i18n ( "pass %1" ).arg ( pass ) );
				else
					m_progress->setTitleLabel ( i18n ( "Encoding %1" ).arg ( sCodec ) );

				if ( m_fourcc !="" )
					*m_process << "-ffourcc" << m_fourcc;
				else if ( videoCodecs->getFourcc ( m_codec ) !="" )
					*m_process << "-ffourcc" << videoCodecs->getFourcc ( m_codec );

				delete audioCodecs;
				delete videoCodecs;

				//looking for first audio selected
				for ( int i=0;i<_title->getaudioStreamCount();i++ )
				{
					if ( _title->getaudioStream ( i )->getselected() )
					{
						//*m_process << "-oac" << sAOption;
						pos=sAOption.find ( "-af" );
						if ( pos==-1 )
							*m_process << TQString ( "-af volume=%1" ).arg ( m_audioGain );
						else
							sAOption=sAOption.insert ( pos+4,TQString ( "volume=%1," ).arg ( m_audioGain ) );
						*m_process << sAOption;

						audio=true;
						break;
					}
				}

				if ( getselectedSubp ( _title ) !=-1 )
				{
					*m_process << "-sid" << TQString::number ( getselectedSubp ( _title ) );
				}
				if ( !audio )
					*m_process << "-nosound";

				TQString path=m_filename;

				if ( m_parts>1 )
				{
					TQString ext=m_filename.section ( ".",-1 );
					if ( ext!="" )
						ext="."+ext;
					path=m_filename.left ( m_filename.length()-ext.length() );
					path=path+TQString::number ( m_part ) +ext;
				}
				if ( pass==1 )
					*m_process << "-o" << "/dev/null";
				else
					*m_process <<"-o" << "'"+path+"'";
				if ( path.upper().endsWith ( "MPEG" ) || path.upper().endsWith ( "MPG" ) )
					*m_process << "-of" << "mpeg";
				else if ( path.upper().endsWith ( "AVI" ) )
					*m_process << "-of" << "avi";
				else
				{
					*m_process << "-of" << "lavf";
					*m_process << "-lavfopts" << "i_certify_that_my_video_stream_does_not_use_b_frames";
				}
				//*m_process << "-of" << "avi";

			}
			TQString s="";
			for ( uint i=0; i< m_process->args().count();i++ )
			{
				TQCString str=* ( m_process->args().at ( i ) );
				s +=TQString ( str ) +" ";
			}
			tqDebug ( "%s", s.ascii() );
			time->start();
			m_timer3.start();
			connect ( m_process, TQT_SIGNAL ( receivedStdout ( TDEProcess *, char *, int ) ),this, TQT_SLOT ( getStdout ( TDEProcess *, char *, int ) ) );
			connect ( m_process, TQT_SIGNAL ( receivedStderr ( TDEProcess *, char *, int ) ),this, TQT_SLOT ( getStderr ( TDEProcess *, char *, int ) ) );
			//connect(m_process, TQT_SIGNAL(processExited(TDEProcess*)),this,TQT_SLOT(exited(TDEProcess*)));
			connect ( m_progress,TQT_SIGNAL ( sigCancel() ),this,TQT_SLOT ( slotCancel() ) );
			m_canceled=false;
			m_progress->show();
			m_process->start ( TDEProcess::OwnGroup, TDEProcess::All );
			timer->start ( 500, 0 );
			m_process->sync();
			//if application is exiting, kill the encoding process
			if ( m_process->isRunning() )
			{
				m_process->kill();
				return;
			}
			if ( m_canceled )
			{
				KMessageBox::information ( tqApp->mainWidget(),i18n ( "MPEG-4 Encoding cancelled" ), i18n ( "MPEG-4 Encoding" ) );
				error=true;
			}
			else if ( !m_process->normalExit() )
			{
				KMessageBox::error ( tqApp->mainWidget(),"<b>"+i18n ( "Error while running mencoder :" ) +"</b><br>"+m_stderr, i18n ( "Encoding error" ) );
				error=true;
			}
			if ( maxPass >0 )
				pass++;
		}
		while ( pass<=maxPass && !error && m_2pass );

	}
}


void k9MP4Enc::slotCancel()
{
	m_canceled=true;
	m_process->kill();
}


TQString k9MP4Enc::replaceParams ( TQString _value )
{
	TQString str=_value;
	str.replace ( "$PASS",TQString::number ( m_pass ) );
	str.replace ( "$WIDTH",m_width );
	str.replace ( "$HEIGHT",m_height );
	str.replace ( "$VIDBR",TQString::number ( getBitRate ( m_title ) ) );
	str.replace ( "$AUDBR",m_audioBitrate );
	return str;
}


int k9MP4Enc::getBitRate ( k9DVDTitle *_title )
{
	// bitrate video = (MB *8388.608) /SEC    - bitrate audio

	if ( m_videoBitrate!="" )
	{
		return  m_videoBitrate.toInt();
	}
	else
	{
		int size=m_size.toInt();
		float titleSize=_title->getChaptersSize_mb ( true );
		if ( titleSize< ( float ) size )
			size= ( int ) ( titleSize/m_parts ) ;
		m_progress->setsize ( TQString::number ( size ) +i18n ( "MB" ) +" X " +TQString::number ( m_parts ) );
		TQTime t1 ( 0,0 );
		int sec=t1.secsTo ( _title->getSelectedLength() );
		//int bitrate=(int)( ((size*m_parts) * 8388.608)/sec  - m_audioBitrate.toInt());
		int bitrate=8* ( ( ( size*m_parts*1024 )- ( m_audioBitrate.toInt() *sec/8 ) ) /sec );

		return bitrate;
	}
}


void k9MP4Enc::getStdout ( TDEProcess *, char *buffer, int buflen )
{
	TQCString tmp ( buffer,buflen );
	m_cpt++;
	if ( m_cpt==100 )
		m_cpt=0;

	if ( m_cpt!=0 )
		return;

	int pos=tmp.find ( "Pos:" );
	if ( pos!=-1 )
	{
		TQString tmp2=tmp.mid ( pos );
		float t;
		int frame;
		int fps;
		sscanf ( tmp2.latin1(),"Pos: %f%*s%d",&t,&frame );
		tmp2=tmp2.mid ( tmp2.find ( "(" ) +1 );
		tmp2=tmp2.mid ( tmp2.find ( ")" ) +1 );
		sscanf ( tmp2.latin1(),"%d",&fps );

		m_progress->setfps ( TQString::number ( fps ) );
	}


}

void k9MP4Enc::getStderr ( TDEProcess *proc, char *buffer, int buflen )
{
	//m_stderr=TQString::fromLatin1(buffer,buflen);
	TQCString cstderr ( buffer,buflen+1 );

	if ( cstderr.find ( "FATAL:" ) !=-1 )
	{
		proc->kill();
	}

	int pos=cstderr.find ( "INFOPOS:" );
	if ( pos!=-1 )
	{
		if ( m_timer3.elapsed() >500 )
		{
			m_timer3.restart();
			TQString tmp=cstderr.mid ( pos );
			uint32_t totalBytes,totalSize;
			sscanf ( tmp.latin1(),"INFOPOS: %d %d",&totalBytes,&totalSize );
			if ( totalSize !=0 )
				m_percent= ( float ) totalBytes / ( float ) m_totalSize;


			TQTime time2 ( 0,0 );
			time2=time2.addMSecs ( time->elapsed() );
			if ( m_percent>0 )
			{
				TQTime time3 ( 0,0 );
				time3=time3.addMSecs ( ( uint32_t ) ( time->elapsed() * ( 1/m_percent ) ) );
				m_remain=time3.toString ( "hh:mm:ss" );
			}

			m_percent*=100;
			m_progress->setProgress ( ( int ) m_percent );
			m_progress->setremain ( time2.toString ( "hh:mm:ss" ) +" / " +m_remain );
		}
	}
	else
	{
		pos=cstderr.find ( "INFOIMAGE:" );
		if ( pos!=-1 )
		{
			m_progress->setImage ( cstderr.mid ( pos+10 ) );
		}
		else
			tqDebug ( "[%s]",buffer );
	}
	m_stderr=cstderr;
}

void k9MP4Enc::timerDone()
{
	TQTime time2 ( 0,0 );
	time2=time2.addMSecs ( time->elapsed() );
	m_progress->setremain ( time2.toString ( "hh:mm:ss" ) +" / " +m_remain );

}

bool k9MP4Enc::isCanceled()
{
	return m_canceled;
}


k9MP4Enc::~k9MP4Enc() {}


#include "k9mp4enc.moc"
