/*
    Copyright (C) 2000 - 2001 Kai Heitkamp, kai@kde.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.
    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., 675 Mass Ave, Cambridge, MA 02139, USA.

 */

#include <stdlib.h>
#include <string.h>

#include <qcombobox.h>
#include <qcheckbox.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qprogressbar.h>
#include <qtextview.h>
#include <qmultilineedit.h>
#include <qlabel.h>

#include <kapplication.h>
#include <klocale.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kmessagebox.h>

#include "copycd.h"
#include "copycd.moc"

CopyCD::CopyCD(QWidget *parent, const char *name ) : CopyCD_GUI(parent, name, true) {

	// Read config
	config = kapp->config();
	config->setGroup( "Config - copycd" );

	QCheckBox_isosize->setChecked( config->readBoolEntry("QCheckBox_isosize") );
	QCheckBox_Simulate->setChecked( config->readBoolEntry("QCheckBox_Simulate") );
	QCheckBox_ignoresize->setChecked( config->readBoolEntry("QCheckBox_ignoresize") );
	QCheckBox_Eject->setChecked( config->readBoolEntry("QCheckBox_Eject") );
	QCheckBox_nofix->setChecked( config->readBoolEntry("QCheckBox_nofix") );
	QCheckBox_c2scan->setChecked( config->readBoolEntry("QCheckBox_c2scan") );
	QCheckBox_Force->setChecked( config->readBoolEntry("QCheckBox_Force") );
	QCheckBox_onthefly->setChecked( config->readBoolEntry("QCheckBox_onthefly") );
	QRadioButton_TaO->setChecked( config->readBoolEntry( "QRadioButton_TaO") );
	QRadioButton_DaO->setChecked( config->readBoolEntry( "QRadioButton_DaO") );

	QComboBox_CDRWOptions->setCurrentItem( config->readNumEntry("QComboBox_CDRWOptions") );
	QComboBox_WriterSpeed->setCurrentItem( config->readNumEntry("QComboBox_WriterSpeed") );

	// Connects for 'QRadioButton_DaO' toogle and 'QRadioButton_TaO' toggle
	connect( QRadioButton_DaO, SIGNAL(clicked()), SLOT(slot_toggleTaO()) );
	connect( QRadioButton_TaO, SIGNAL(clicked()), SLOT(slot_toggleDaO()) );

	// Check for RW-Device
	config->setGroup( "CD-Writer" );
	if ( config->readEntry( "isRW" ) == "false" ){
		QComboBox_CDRWOptions->clear();
		QComboBox_CDRWOptions->insertItem(i18n( "Non-RW!" ));
		QComboBox_CDRWOptions->setEnabled( false );
	}

	// One or more devices ?
	int writer = config->readNumEntry("Index");
	config->setGroup( "CD-Reader" );
	int reader = config->readNumEntry("Index");
	if ( writer == reader ){
		oneDrive = true;
	}
	else {
		oneDrive = false;
	}

	//Read program paths into public variables
	config->setGroup("Program paths");
	readcd = config->readEntry("readcd_path");
	cdrecord = config->readEntry("cdrecord_path");
}

CopyCD::~CopyCD(){
}

//'QRadioButton_TaO' clicked , toggle 'QRadioButton_DaO'
void CopyCD::slot_toggleDaO(){
	if ( ! QRadioButton_DaO->isChecked() ){
		QRadioButton_TaO->setChecked( true );
	}
	else{
		QRadioButton_TaO->setChecked( true );
		QRadioButton_DaO->setChecked( false );
	}
}

//'QRadioButton_DaO' clicked , toggle 'QRadioButton_TaO'
void CopyCD::slot_toggleTaO(){
	if ( ! QRadioButton_TaO->isChecked() ){
		QRadioButton_DaO->setChecked( true );
	}
	else{
		QRadioButton_TaO->setChecked( false );
		QRadioButton_DaO->setChecked( true );
	}
}

// 'Start-Button' clicked
void CopyCD::slot_start(){
	//If TaO and DaO RadioButton false, return
	if ( ! QRadioButton_TaO->isChecked() && ! QRadioButton_DaO->isChecked() ){
		KMessageBox::error( 0, i18n("You must select 'Track at Once' or 'Disc at Once'!"), i18n("KOnCD - Copy CD - Error") );
		return;
	}

	if ( QCheckBox_onthefly->isChecked() ){
		slot_writeCDonthefly();
	}
	else {
		slot_createImage();
	}
}

// create cd image
void CopyCD::slot_createImage(){
	if ( ! QCheckBox_c2scan->isChecked() ){
		imgprocoutdlg.StatusLabel->setText( i18n("Creating CD image...") );
	}
	else{
		imgprocoutdlg.StatusLabel->setText( i18n("Scanning for errors...") );
	}

	process.clearArguments();
	process << readcd;

	//Set the CD-Writer first
	config->setGroup("CD-Reader");
	process << config->readEntry("SCSI-Target");

	if ( QCheckBox_c2scan->isChecked() ){
		//Enable c2 scan
		process << "-c2scan";
	}
	else{
		//Set the image file
		process << "-f" << "/tmp/cdimage.raw";
	}

	//Connect the Abort-Button from ImgProOut-Dialog to Abort-Handler
	connect( imgprocoutdlg.Button, SIGNAL(clicked()), this, SLOT(abort_handler()));

	//Connect the 'processExited' Signal to the 'slotProcessFinished' Slot to aktivate the Start- / Quit-Buttons
	connect( &process, SIGNAL(processExited(KProcess *)), this, SLOT(slotImageExited(KProcess *)));

	//Connect with slotRecStderr to update KProgress (Write- / Buffer-Status) and StatusLine
	connect( &process, SIGNAL(receivedStdout (KProcess *, char *, int) ), this, SLOT(slotImageStderr (KProcess *, char *, int) ));
	connect( &process, SIGNAL(receivedStderr (KProcess *, char *, int) ), this, SLOT(slotImageStderr (KProcess *, char *, int) ));

	//Start KProcess
	if( ! process.start( KProcess::NotifyOnExit, KProcess::AllOutput ) ){
		KMessageBox::error( 0, i18n("Could not start readcd!"), i18n("KOnCD - Copy CD - Error") );
		process.disconnect();
		return;
	}

	imgprocoutdlg.Button->setText( i18n( "&Abort" ) );
	if ( ! imgprocoutdlg.isVisible() ) imgprocoutdlg.show();
}

//KProcess outout analysis
void CopyCD::slotImageStderr( KProcess *, char *buffer, int buflen) {
	char buf[1024];
	memset( buf, 0, sizeof( buf ) );
	strncpy(buf,buffer, buflen>(int)sizeof(buf) ? sizeof(buf)-1 : buflen );

	if( !buflen ) {
		return;
	}

	//Add cdrecord messages to the output window
	imgprocoutdlg.Output->append( buf );
	//Go to the end of the output text
	imgprocoutdlg.Output->setCursorPosition( imgprocoutdlg.Output->length(), 0 );

	if( ( strstr( buf, "end:" ) ) ) {
		if( ( strstr( buf, "cnt:" ) ) ) {
			char *tmp = strstr( buf, "end:" );
			strtok( tmp, "\n" );
			if( tmp ) {
				tmp = strchr( tmp, ' ' );
				if( tmp ) {
					int wrote = atoi( tmp );
					imgprocoutdlg.ImgProgress->setTotalSteps( wrote );
				}
			}
		}
	}

	if( ( strstr( buf, "addr:" ) ) ) {
		char *tmp = strchr( buf, ' ' );
		if( tmp ) {
			strtok( tmp, "cnt" );
			if( tmp ) {
				int wrote = atoi( tmp );
				imgprocoutdlg.ImgProgress->setProgress( wrote );
			}
		}
	}
}

//KProcess finished
void CopyCD::slotImageExited(KProcess *rcproc) {
	process.disconnect();

	//Exit KProcess normally ?
	if( rcproc->exitStatus() == 0 ) {
		imgprocoutdlg.Button->setText( i18n( "&OK" ) );
		if ( oneDrive ) KMessageBox::information( 0, i18n( "Please change the disc!" ), i18n("KOnCD - Copy CD - Infos") );
		if ( ! QCheckBox_c2scan->isChecked() ){
			if ( imgprocoutdlg.isVisible() ) imgprocoutdlg.close();
			slot_writeCD();
		}
		else{
			imgprocoutdlg.StatusLabel->setText( i18n("Errorscan complete.") );
		}
	}
  else{
		QString statusout;
		statusout.sprintf( I18N_NOOP( "\nError, exit status: %d\n" ), rcproc->exitStatus() );
		imgprocoutdlg.Output->append( statusout );
		imgprocoutdlg.Output->setCursorPosition( imgprocoutdlg.Output->length(), 0 );
		imgprocoutdlg.Button->setText( i18n( "&OK" ) );
		imgprocoutdlg.StatusLabel->setText( i18n( "An error has occurred!" ) );
		KMessageBox::sorry( 0, i18n("The process was aborted!"), i18n("KOnCD - Copy CD - Infos") );
	}
}

// write CD
void CopyCD::slot_writeCD(){
	procoutdlg.StatusLabel->setText( i18n("Prepare for write...") );

	process.clearArguments();
	process << cdrecord << "-v";

	//Set the CD-Writer first
	config->setGroup("CD-Writer");
	process << config->readEntry("SCSI-Target");

	//Set Burn-Proof
	if( config->readBoolEntry("BurnProof") ) process << "driveropts=burnproof";

	//Set CD-Writer Options to KProcess
	if( QCheckBox_Simulate->isChecked() ) process << "-dummy";
	if( QCheckBox_ignoresize->isChecked() ) process << "-ignsize";
	if( QCheckBox_isosize->isChecked() ) process << "-isosize";
	if( QCheckBox_Eject->isChecked() ) process << "-eject";
	if( QCheckBox_nofix->isChecked() ) process << "-nofix";
	if( QCheckBox_Force->isChecked() ) process << "-force";
	if( QRadioButton_DaO->isChecked() ) process << "-dao";

	//Set fifo size
	switch( config->readNumEntry("Fifosize") )
	{
	 	case 0:	process << "fs=4m";
						break;
	 	case 1: process << "fs=8m";
						break;
	 	case 2: process << "fs=12m";
						break;
	 	case 3: process << "fs=16m";
						break;
	 	case 4: process << "fs=20m";
						break;
	 	case 5: process << "fs=24m";
						break;
		case 6: process << "fs=28m";
						break;
		case 7: process << "fs=32m";
						break;
	}

	//Set CD-RW Options to KProcess
	switch( QComboBox_CDRWOptions->currentItem() )
	{
	 	case 1:	process << "blank=all";
						break;
	 	case 2: process << "blank=fast";
						break;
	 	case 3: process << "blank=track";
						break;
	 	case 4: process << "blank=unreserve";
						break;
	 	case 5: process << "blank=trtrail";
						break;
	 	case 6: process << "blank=unclose";
						break;
		case 7: process << "blank=session";
						break;
	}

	//Set CD-Writer Speed to KProcess
	switch( QComboBox_WriterSpeed->currentItem() )
	{
   	case 0:	process << "speed=1";
						break;
   	case 1:	process << "speed=2";
						break;
   	case 2:	process << "speed=4";
						break;
   	case 3:	process << "speed=6";
						break;
   	case 4:	process << "speed=8";
						break;
   	case 5:	process << "speed=10";
						break;
   	case 6:	process << "speed=12";
						break;
   	case 7:	process << "speed=16";
						break;
   	case 8:	process << "speed=20";
						break;
   	case 9:	process << "speed=24";
						break;
	}

	//Set the cd image to write
	process << "/tmp/cdimage.raw";

	//Connect the Abort-Button from ImgProOut-Dialog to Abort-Handler
	connect( procoutdlg.QPushButton_OK, SIGNAL(clicked()), this, SLOT(abort_handler()));

	//Connect the 'processExited' Signal to the 'slotProcessExited' Slot
	connect( &process, SIGNAL(processExited(KProcess *)), this, SLOT(slotProcessExited(KProcess *)));

	//Connect with slotRecStderr to update KProgress (Write- / Buffer-Status) and StatusLine
	connect( &process, SIGNAL(receivedStdout (KProcess *, char *, int) ), this, SLOT(slotRecStderr (KProcess *, char *, int) ));
	connect( &process, SIGNAL(receivedStderr (KProcess *, char *, int) ), this, SLOT(slotRecStderr (KProcess *, char *, int) ));

	//Start KProcess
	if( ! process.start( KProcess::NotifyOnExit, KProcess::AllOutput ) ){
		KMessageBox::error( 0, i18n("Could not start cdrecord!"), i18n("KOnCD - Copy CD - Error") );
		process.disconnect();
		return;
		}

	procoutdlg.setCaption( i18n( "KOnCD - Writing image..." ) );
	procoutdlg.QPushButton_OK->setText( i18n( "&Abort" ) );
	if ( ! procoutdlg.isVisible() ) procoutdlg.show();
}

// write CD 'on-the-fly'
void CopyCD::slot_writeCDonthefly(){
	procoutdlg.StatusLabel->setText( i18n("Prepare for write...") );

	process.clearArguments();
	process << cdrecord << "-v";

	//Set the CD-Writer first
	config->setGroup("CD-Writer");
	process << config->readEntry("SCSI-Target");

	//Set Burn-Proof
	if( config->readBoolEntry("BurnProof") ) process << "driveropts=burnproof";

	//Set CD-Writer Options to KProcess
	if( QCheckBox_Simulate->isChecked() ) process << "-dummy";
	if( QCheckBox_ignoresize->isChecked() ) process << "-ignsize";
	if( QCheckBox_isosize->isChecked() ) process << "-isosize";
	if( QCheckBox_Eject->isChecked() ) process << "-eject";
	if( QCheckBox_nofix->isChecked() ) process << "-nofix";
	if( QCheckBox_Force->isChecked() ) process << "-force";
	if( QRadioButton_DaO->isChecked() ) process << "-dao";

	//Set fifo size
	switch( config->readNumEntry("Fifosize") )
	{
	 	case 0:	process << "fs=4m";
						break;
	 	case 1: process << "fs=8m";
						break;
	 	case 2: process << "fs=12m";
						break;
	 	case 3: process << "fs=16m";
						break;
	 	case 4: process << "fs=20m";
						break;
	 	case 5: process << "fs=24m";
						break;
		case 6: process << "fs=28m";
						break;
		case 7: process << "fs=32m";
						break;
	}

	//Set CD-RW Options to KProcess
	switch( QComboBox_CDRWOptions->currentItem() )
	{
	 	case 1:	process << "blank=all";
						break;
	 	case 2: process << "blank=fast";
						break;
	 	case 3: process << "blank=track";
						break;
	 	case 4: process << "blank=unreserve";
						break;
	 	case 5: process << "blank=trtrail";
						break;
	 	case 6: process << "blank=unclose";
						break;
		case 7: process << "blank=session";
						break;
	}

	//Set CD-Writer Speed to KProcess
	switch( QComboBox_WriterSpeed->currentItem() )
	{
   	case 0:	process << "speed=1";
						break;
   	case 1:	process << "speed=2";
						break;
   	case 2:	process << "speed=4";
						break;
   	case 3:	process << "speed=6";
						break;
   	case 4:	process << "speed=8";
						break;
   	case 5:	process << "speed=10";
						break;
   	case 6:	process << "speed=12";
						break;
   	case 7:	process << "speed=16";
						break;
   	case 8:	process << "speed=20";
						break;
   	case 9:	process << "speed=24";
						break;
	}

	//Set the CD-Reader
	config->setGroup("CD-Reader");
	process << config->readEntry("Device");

	//Connect the Abort-Button from ImgProOut-Dialog to Abort-Handler
	connect( procoutdlg.QPushButton_OK, SIGNAL(clicked()), this, SLOT(abort_handler()));

	//Connect the 'processExited' Signal to the 'slotProcessExited' Slot
	connect( &process, SIGNAL(processExited(KProcess *)), this, SLOT(slotProcessExited(KProcess *)));

	//Connect with slotRecStderr to update KProgress (Write- / Buffer-Status) and StatusLine
	connect( &process, SIGNAL(receivedStdout (KProcess *, char *, int) ), this, SLOT(slotRecStderr (KProcess *, char *, int) ));
	connect( &process, SIGNAL(receivedStderr (KProcess *, char *, int) ), this, SLOT(slotRecStderr (KProcess *, char *, int) ));

	//Start KProcess
	if( ! process.start( KProcess::NotifyOnExit, KProcess::AllOutput ) ){
		process.disconnect();
		KMessageBox::error( 0, i18n("Could not start cdrecord!"), i18n("KOnCD - Copy CD - Error") );
		return;
		}

	procoutdlg.setCaption( i18n( "KOnCD - Copying CD..." ) );
	procoutdlg.QPushButton_OK->setText( i18n( "&Abort" ) );
	if ( ! procoutdlg.isVisible() ) procoutdlg.show();
}

//KProcess outout analysis
void CopyCD::slotRecStderr( KProcess *, char *buffer, int buflen) {
	char *c, buf[1024];
	int wrote, total, load;

	if( !buflen ) {
		return;
	}

	memset( buf, 0, sizeof( buf ) );
	strncpy( buf, buffer, buflen > (int) sizeof( buf ) ? sizeof(buf) - 1 : buflen );

	//Add cdrecord messages to the output window
	procoutdlg.Output->append( buf );
	//Go to the end of the output text
	procoutdlg.Output->setCursorPosition( procoutdlg.Output->length(), 0 );

	//Blanking a CD-RW
	if( strstr( buf, "Blanking" )) {
		procoutdlg.StatusLabel->setText( i18n("Blanking the CDRW...") );
	}

	//Burn without Fixating
	if( strstr( buf, "Writing  time:" )) {
		procoutdlg.StatusLabel->setText( i18n("Burn-Process complete.") );
	}

	//Fixating the CD-R
	if( strstr( buf, "Fixating..." )) {
		procoutdlg.StatusLabel->setText( i18n("Writing TOC...") );
	}

	//Burn with Fixating
	if( strstr( buf, "Fixating time:" )) {
		procoutdlg.StatusLabel->setText( i18n("Burn-Process complete.") );
   	}

	if( strstr( buf, "MB written" )) {
		c = strchr( buf, ':' );
		if( c && *c ) {
			c = strtok( c, ":\r\t " );
			if( c ) {
				wrote = atoi( c );
				c = strtok( NULL, "\r\t " );
				c = strtok( NULL, "\r\t " );
				if( c ) {
					total = atoi( c );
					c = strtok( NULL, "\r" );
					c = strstr( c, "fifo " );
					if( c ) {
						c = strtok( c + 5, "%\r\t " );
						if( c ) {
							load = atoi( c );
							procoutdlg.WriteProgress->setProgress( total ? wrote * 100 / total : 0 );
							procoutdlg.BufferProgress->setProgress( load );

							if( wrote ) {
								if( QCheckBox_Simulate->isChecked() ) {
									procoutdlg.StatusLabel->setText( i18n("Writing CD in dummy mode...") );
								}
								else {
									procoutdlg.StatusLabel->setText( i18n("Writing CD...") );
								}
							}
							if( ( total == wrote ) && ( QCheckBox_Simulate->isChecked() == false )) {
								procoutdlg.StatusLabel->setText( i18n("Writing TOC...") );
							}
						}
					}
				}
			}
		}
	}
}

//KProcess finished
void CopyCD::slotProcessExited(KProcess *rcproc) {
	process.disconnect();

	//KProcess did not exit normally
	if( rcproc->exitStatus() == 0 ) {
		procoutdlg.QPushButton_OK->setText( i18n( "&OK" ) );
	}
	else {
		QString statusout;
		statusout.sprintf( I18N_NOOP( "\nError, exit status: %d\n" ), rcproc->exitStatus() );
		procoutdlg.Output->append( statusout );
		procoutdlg.Output->setCursorPosition( procoutdlg.Output->length(), 0 );
		procoutdlg.StatusLabel->setText( i18n( "An error has occurred!" ) );
		procoutdlg.QPushButton_OK->setText( i18n( "&OK" ) );
		KMessageBox::sorry( 0, i18n("The process was aborted!"), i18n("KOnCD - Copy CD - Info") );
	}
}

//Abort-Handler
void CopyCD::abort_handler(){
  if ( process.isRunning() ){
    int pid = process.getPid();
		process.kill();
    waitpid( pid, 0, 0 );
  }

	imgprocoutdlg.StatusLabel->setText( i18n("Creating CD image...") );
	imgprocoutdlg.Output->setText( "" );
	imgprocoutdlg.ImgProgress->setProgress( 0 );
	if ( imgprocoutdlg.isVisible() ) imgprocoutdlg.close();

	procoutdlg.setCaption( i18n( "KOnCD - Writing CD..." ) );
	procoutdlg.StatusLabel->setText( i18n( "Prepare for write..." ) );
	procoutdlg.Output->setText( "" );
	procoutdlg.WriteProgress->setProgress( 0 );
	procoutdlg.BufferProgress->setProgress( 0 );
	if ( procoutdlg.isVisible() ) procoutdlg.close();

	process.disconnect();
}

// 'Exit-Button' clicked
void CopyCD::slot_quit(){

	//Write config
	config->setGroup( "Config - copycd" );
	config->writeEntry( "QCheckBox_Simulate", QCheckBox_Simulate->isChecked() );
	config->writeEntry( "QCheckBox_isosize", QCheckBox_isosize->isChecked() );
	config->writeEntry( "QCheckBox_ignoresize", QCheckBox_ignoresize->isChecked() );
	config->writeEntry( "QCheckBox_Eject", QCheckBox_Eject->isChecked() );
	config->writeEntry( "QCheckBox_nofix", QCheckBox_nofix->isChecked() );
	config->writeEntry( "QCheckBox_c2scan", QCheckBox_c2scan->isChecked() );
	config->writeEntry( "QCheckBox_Force", QCheckBox_Force->isChecked() );
	config->writeEntry( "QCheckBox_onthefly", QCheckBox_onthefly->isChecked() );
	config->writeEntry( "QRadioButton_TaO", QRadioButton_TaO->isChecked() );
	config->writeEntry( "QRadioButton_DaO", QRadioButton_DaO->isChecked() );

	config->writeEntry( "QComboBox_CDRWOptions", QComboBox_CDRWOptions->currentItem() );
	config->writeEntry( "QComboBox_WriterSpeed", QComboBox_WriterSpeed->currentItem() );

	close();
}
