/***************************************************************************
                          CdboAudioActionFactory.cpp  -  description
                             -------------------
    begin                : Sun Feb 3 2002
    copyright            : (C) 2002 by Alex Podolsky
    email                : alexpod@users.sourceforge.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/
#include <iostream.h>


#include <qobject.h>
#include <qstringlist.h>
#include <qlistview.h>
#include <qevent.h>

//#include <kapplication.h>
#include <kconfig.h>
#include <kprocess.h>

#include <kdirlister.h>
#include <kurl.h>
//#include <kglobal.h>
#include <klocale.h>
#include <kdebug.h>

#include "../dialogs/CdboDialogBase.h"
#include "CdboAudioActionFactory.h"
#include "CdboAudioActionFactory.moc"


//=======================================
CdboAudioActionFactory::CdboAudioActionFactory(QObject *parent, const char *name) : QObject(parent, name){

	config = new KConfig("cdbakeovenrc");
	DEBUG_COMMANDS = true;
}
CdboAudioActionFactory::~CdboAudioActionFactory(){
	//killCurrentTask();
}


//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
///////////////////////////////////////////////// SCAN ///////////////////////////////////////////////////////////////////
//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::scanAudioDisk(QString device, QListView *list,
			CdboDialogBase *user, bool uncheck){
	this->uncheck = uncheck;
	this->list = list;
	this->user = user;
	this->device = device;
	outStr = "";
	cdboShellProc = new KShellProcess();
	if(config)
 		delete config;
	config = new KConfig("cdbakeovenrc");
	config->setGroup("Default Settings");
	cdboShellProc->setWorkingDirectory(config->readEntry("Tmp Dir", "/tmp/"));
	*cdboShellProc << "cdda2wav" << "-J";
	config->setGroup("Audio Read Options");
	if(config->readBoolEntry("Perform CDDB Lookup", true))
		*cdboShellProc << "-L";
	*cdboShellProc << "-g" << "-D" << device;
///////////////////////////////////////////////////////
	if(DEBUG_COMMANDS){
		cerr << "\n\n==============DEBUG_COMMANDS==================================\n";
		QValueList<QCString> list = cdboShellProc->args();
		for(int i = 0; i < (int)list.count(); i++)	
			cerr << " " << list[i];	
		cerr << "\n\n===============DEBUG_COMMANDS=================================\n";
	}
	//////////////////////////////////////////////////////////////////
	
	connect(cdboShellProc, SIGNAL(processExited(KProcess *)),
      			this, SLOT(scanAudioDiskProcessDoneSlot(KProcess *)));
 	connect(cdboShellProc, SIGNAL(receivedStdout (KProcess *, char *, int)),
		this, SLOT(receivedScanAudioDiskMessageSlot(KProcess *, char *, int)));
	connect(cdboShellProc, SIGNAL(receivedStderr (KProcess *, char *, int)),
		this, SLOT(receivedScanAudioDiskMessageSlot(KProcess *, char *, int)));
	if(!cdboShellProc->start(KProcess::NotifyOnExit, KProcess::AllOutput)){
		if(user){
			user->output(CdboDialogBase::ErrorMessage, i18n("Failed to load audio tracks from CD! "));
			user->output(CdboDialogBase::ErrorMessage, i18n("Make sure you have enough privileges."));
			user->blockBtns(false);
			user->status(i18n("Ready"));
			user = 0;
		}
		this->list = 0;
		delete cdboShellProc;
		emit failedToLunchProcess();
		currentProc = 0;
		curJob = 0;
		return;
	}
	else if(user){
		user->blockBtns(true);
		user->output(CdboDialogBase::StatusOkMessage, i18n("Loading audio tracks from CD."));
		user->status(i18n("Loading audio tracks..."));
	}
	curJob = 0;
	currentProc = cdboShellProc;
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::receivedScanAudioDiskMessageSlot(KProcess *, char *buffer, int buflen){
	//cerr << "Counting audio tracks...\n";
	if(buflen > 0){
		QString buff(buffer);
		outStr += buff.left(buflen);
		//cerr << buff.left(buflen).latin1() << "\n";

		if(buff.contains("Permission denied")) {
			if(user){
				user->output(CdboDialogBase::ErrorMessage, i18n("Failed to load audio tracks from CD! "));
				user->output(CdboDialogBase::ErrorMessage, i18n("Make sure you have enough privileges."));
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			killCurrentTask();
			return;
		}
		 if(buff.contains("No such")) {
			if(user){
				user->output(CdboDialogBase::ErrorMessage, i18n("This device does not seem to be properly configured!"));
				//user->output(CdboDialogBase::ProcessMessage, buff);
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			killCurrentTask();
			return;
		}
		if(buff.contains("Inquiry command failed") || buff.contains("error") || buff.contains("load cdrom please") ||
				buff.contains("Invalid argument") || buff.contains("No medium found")) {
			if(user){
				user->output(CdboDialogBase::ErrorMessage, i18n("Failed to load audio tracks from CD! "));
				user->output(CdboDialogBase::ErrorMessage, i18n("This device does not seem to contain a valid Audio CD."));
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			killCurrentTask();
			return;
		}
		if(buff.contains("Usage:") || buff.contains("--help")) {
			if(user){
				/*user->output(CdboDialogBase::ErrorMessage, i18n("Ooops! "\
					"I did not understand the combination of options you've given me :-/"));
				user->output(CdboDialogBase::ErrorMessage,
					i18n("Please submit this as a bug to the email located in about dialog."));*/
				user->output(CdboDialogBase::ErrorMessage,
					i18n("Failed to load audio tracks from CD!"));
				user->output(CdboDialogBase::ErrorMessage,
					i18n("Try to disable track names lookup in 'Audio Copy' section of Settings Dialog."));
				user->output(CdboDialogBase::ErrorMessage,
					i18n("Please make sure you have the latest version of cdda2wav installed on your system."));
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			killCurrentTask();
			return;
		}
		
		if(buff.contains("This disk has no audio tracks")) {
			if(user){
				user->output(CdboDialogBase::ErrorMessage, i18n("Failed to load audio tracks from CD! "));
				user->output(CdboDialogBase::ErrorMessage, i18n("This device does not seem to contain a valid Audio CD."));
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			killCurrentTask();
			return;
		}
		//if(buff.contains("Leadout:")) {
		if(buff.contains("catalog number")) {
			killCurrentTask();
			loadTracks();
			if(user){
				//user->output(CdboDialogBase::ErrorMessage, "Done.");
				user->blockBtns(false);
				user->status(i18n("Ready"));
				user = 0;
			}
			list = 0;
			emit processDone(false);
			return;
		}
 	}
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::scanAudioDiskProcessDoneSlot(KProcess *proc){
	delete proc;
	user = 0;
	list = 0;
	currentProc = 0;
	//uncheck = false;
}
//============== This method looks like a waste of time, I should probably use audiocd ioslave!!! ============
bool CdboAudioActionFactory::loadTracks(){
	cerr << "\n\n=++++++++++++++++++++++++++++++++=\n\n";
	cerr << outStr.latin1();
	cerr << "\n\n=++++++++++++++++++++++++++++++++=\n\n";
	bool mode2 = false;
	if(outStr.find("Table of Contents:"))
		mode2 = true;
	QStringList comOutput(QStringList::split("\n", outStr));
	
	//int fix = 0;
	int index = 3;
	if(comOutput[index].find("no cddb entry found:") != -1){
		user->output(CdboDialogBase::ProcessMessage, comOutput[index]);
		//fix = 1;
		index++;
	}
	//index++;
	if(comOutput[index].find("multiple entries") != -1){
		user->output(CdboDialogBase::ProcessMessage, i18n("Multiple CDDB entries found."));
		while(comOutput[index].find("Tracks:") == -1)
			index++;
	}

	QString buff = comOutput[index];
	int totTracks;
	
	int pos = buff.find("Tracks:");
	if(pos != -1){
		pos += 7;
		buff = buff.right(buff.length() - pos);
		buff = buff.left(buff.find("."));
		QString toTrack = buff.left(buff.find(" "));
		totTracks = toTrack.toInt();
		emit countedTracks(totTracks);
		QString totTime = buff.right(buff.length() - buff.find(" "));
		if(user){
			user->output(CdboDialogBase::InfoMessage, comOutput[6]);
			user->output(CdboDialogBase::InfoMessage, comOutput[7]);
			user->output(CdboDialogBase::InfoMessage, comOutput[8]);
			user->output(CdboDialogBase::InfoMessage, i18n("Total Tracks: ") + toTrack + i18n(",  Total Time: ") + totTime);
		}
	}
	/*else if( buff.find("Table of Contents:"){

	}*/
	else{
		if(user){
			user->output(CdboDialogBase::ErrorMessage, i18n("Failed to load audio tracks from CD!"));
			user->output(CdboDialogBase::ErrorMessage, i18n("Make sure you have enough privileges."));
			user->output(CdboDialogBase::ErrorMessage,
				i18n("Try to disable track names lookup in 'Audio Copy' section of Settings Dialog."));
		}
		return false;
	}
	do{
		index++;
	}while(comOutput[index].find("Album") == -1);
	index++;
	totTracks += index;
	QCheckListItem *newItem;
	int y = 1;
	for(; index < totTracks; index++, y++){
		//newItem = new QCheckListItem(list);
		if(y <= 9){
			newItem = new QCheckListItem(list, /*"Track*/" 0" + QString::number(y), QCheckListItem::CheckBox);
		}
		else{
			newItem = new QCheckListItem(list, /*"Track*/" " + QString::number(y), QCheckListItem::CheckBox);
		}
		QStringList line(QStringList::split(" ", comOutput[index]));
		buff = line[2].left(line[2].find("."));
		if(buff.find(":") == 1)
			buff = "0" + buff;
		newItem->setText(1, buff);
		//cerr << " Time " << line[2];
		
		/*buff = line[line.count() - 3];
		if(buff == "''"){
			newItem->setText(1, "<Unknown>" );
			//cerr << " Author " << "<Unknown>" << "\n\n";
		}
		else{
			newItem->setText(1, buff);
			//cerr << " Author " << buff << "\n\n";
		}*/
		buff = comOutput[index].right(comOutput[index].length() - comOutput[index].find("title") - 5);
		buff = buff.left(buff.find("from")).simplifyWhiteSpace();
		if(buff == "''")
			buff = "<Unknown>.";
		else if(buff.find("/") != -1)
			buff =  buff.right(buff.length() - buff.findRev("/") - 1).simplifyWhiteSpace();
		buff.truncate(buff.length() - 1);
		newItem->setText(2, buff);
		if(uncheck)
			newItem->setOn(false);
		else
			newItem->setOn(true);
	}
	return true;
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::killCurrentTask(){		
	if(currentProc){
		if(currentProc->isRunning()){
			currentProc->kill();
			delete currentProc;
		}
	}
	if(curJob){
		curJob->kill(false);
	}
	//uncheck = false;
}




//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
///////////////////////////////////////////////// RIP(P) ///////////////////////////////////////////////////////////////////
//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
//==========================================================
bool CdboAudioActionFactory::ripp(QString device, QStringList* tracks, QString outputDir,
		QString ext, CdboDialogBase *user){
	
	if(!tracks || tracks->count() == 0)
		return false;
		
	this->list = list;
	this->user = user;

	KURL::List urlList;
	
	for(int i = 0; i < (int)tracks->count(); i++){
		
		urlList.append( "audiocd:" + ((*tracks)[i]).stripWhiteSpace() + "." + ext + "?device=" + device );
		//cerr <<  "audiocd:" + ((*tracks)[i]).stripWhiteSpace() + "." + ext + "?device=" + device << endl;
	}
	
	
	if(user){
		user->setProcessType(CdboDialogBase::rippProcess);////////////////////////////////////
		user->blockBtns(true);
		user->output(CdboDialogBase::StatusOkMessage, i18n("Ripping audio CD."));
		user->output(CdboDialogBase::StatusOkMessage, i18n("Outputing files to %1.").arg(outputDir));
		user->status(i18n("Ripping audio CD..."));
		user->setTotalProcesses( tracks->count());
		user->output(CdboDialogBase::StatusOkMessage, "show total");
		user->output(CdboDialogBase::ProcessMessage,i18n("percent done: %1").arg(QString::number(0)));
	}
		
	curJob = KIO::copy( urlList, "file:" + outputDir, false );
	
	connect( curJob, SIGNAL(result( KIO::Job * )), this, SLOT( rippResult( KIO::Job *)) );
	
	connect( curJob, SIGNAL(canceled( KIO::Job * )), this, SLOT( rippCanceled( KIO::Job *)) );
	
	connect( curJob, SIGNAL(infoMessage( KIO::Job *, const QString &)),
			this, SLOT( rippMessage( KIO::Job *,const QString &)) );
	
	connect( curJob, SIGNAL(percent( KIO::Job *,unsigned long)),
			this, SLOT( rippPercent( KIO::Job *,unsigned long)) );
	
	//connect( curJob, SIGNAL(processedFiles( KIO::Job *,unsigned long)),
		//	this, SLOT( rippProcessedFiles( KIO::Job *,unsigned long)) );
	connect( curJob, SIGNAL(copyingDone ( KIO::Job *, const KURL &, const KURL &, bool, bool)),
			this, SLOT( rippedFile( KIO::Job *, const KURL &, const KURL &, bool, bool )) );
	
	timeOutId = startTimer(config->readNumEntry("Default Command Time Out", 60) * 1000);
	watchTime = true;
	
	currentProc = 0;
	return true;
}
//=================================================
void CdboAudioActionFactory::rippResult( KIO::Job * job){
	watchTime = false;
	killTimer(timeOutId);
	if(user){
		if(job->error()){
			user->output(CdboDialogBase::StatusNotOkMessage, i18n("Operation exited with following error: "));
			user->output(CdboDialogBase::StatusNotOkMessage, job->errorText());
			//user->output(CdboDialogBase::StatusNotOkMessage, "Operation exited with following error: ");
			//user->output(CdboDialogBase::StatusNotOkMessage, job->errorString());
		}
		else
			user->output(CdboDialogBase::StatusOkMessage,i18n("Operation completed successfully!"));
		
		user->blockBtns(false);
		user->status(i18n("Ready"));
		user = 0;
	}
	list = 0;
	curJob = 0;
	emit processDone(true);
}
//=================================================
void CdboAudioActionFactory::rippCanceled( KIO::Job * ){
	watchTime = false;
	killTimer(timeOutId);
	if(user){
		user->output(CdboDialogBase::StatusNotOkMessage, i18n("Operation canceled by user!"));
		user->output(CdboDialogBase::InfoMessage, i18n("Files ripped up to this point will remain on your disk."));
		user->blockBtns(false);
		user->status(i18n("Ready"));
		user = 0;
	}
	list = 0;
	curJob = 0;
	emit processDone(false);
}
//=================================================
void CdboAudioActionFactory::rippMessage( KIO::Job *,const QString &msg){
	if(user)
		user->output(CdboDialogBase::ProcessMessage,msg);
}
//=================================================
void CdboAudioActionFactory::rippPercent( KIO::Job *,unsigned long p){
	watchTime = false;
	killTimer(timeOutId);
	if(user)
		user->output(CdboDialogBase::ProcessMessage,i18n("percent done: %1").arg(QString::number(p)));
}
//=================================================
void CdboAudioActionFactory::rippProcessedFiles( KIO::Job *,unsigned long f){
	watchTime = false;
	killTimer(timeOutId);
	if(user)
		user->output(CdboDialogBase::ProcessMessage,i18n("processed files: %1").arg(QString::number(f)));
}
//=================================================
void CdboAudioActionFactory::rippedFile ( KIO::Job *, const KURL &, const KURL &, bool, bool ){
	watchTime = false;
	killTimer(timeOutId);
	if(user)
		user->output(CdboDialogBase::ProcessMessage, i18n("processed files: %1").arg(QString::number(0)));
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::timerEvent(QTimerEvent *event){
	if(event->timerId() == timeOutId && watchTime){
		watchTime = false;
		if(user){
			user->output(CdboDialogBase::StatusNotOkMessage, i18n("Operation timed out!"));
			user->output(CdboDialogBase::ErrorMessage, i18n("No responce recieved from audiocd slave."));
			user->output(CdboDialogBase::InfoMessage, i18n("Try to use another device."));
			user->output(CdboDialogBase::InfoMessage, i18n("Files ripped up to this point will remain on your disk."));
			user->blockBtns(false);
			user->status(i18n("Ready"));
			user = 0;
		}
		killTimer(timeOutId);
		list = 0;
		curJob = 0;
		killCurrentTask();
		emit processDone(false);
	}
}


//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
///////////////////////////////////////////////// PLAY ///////////////////////////////////////////////////////////////////
//=============================================================//
//=============================================================//
//=============================================================//
//=============================================================//
void CdboAudioActionFactory::play(QString device, QString tracks, int sector, CdboDialogBase *user){
	this->user = user;
	//user->setTotalProcesses( tracks->count());
	
	///////////////////////////////////////////////////////
	if(config)
 		delete config;
	config = new KConfig("cdbakeovenrc");
	config->setGroup("Default Settings");
	
	cdboShellProc = new KShellProcess();	
	cdboShellProc->setWorkingDirectory(config->readEntry("Tmp Dir", "/tmp/"));
	*cdboShellProc << "cdda2wav" << "-g";

	
	
	*cdboShellProc << "-D" << device;
	config->setGroup("Audio Read Options");
	
	int iBuff = config->readNumEntry("CD-Rom Speed", 0);
	if(iBuff > 0)
		*cdboShellProc << "-S " + QString::number(iBuff);
		
	
	switch(config->readNumEntry("CD Type", 0)){
		case 0: *cdboShellProc << "-x"; break;
		case 1: *cdboShellProc << "-c 2"; break;
		case 2: *cdboShellProc << "-c 1"; break;
		case 3: *cdboShellProc << "-c s"; break;
	}
	//quiet, enable echo, no output file, no info file
	*cdboShellProc << "-q -e -N -H";
	/////////////////////////////////////////////////
	*cdboShellProc << "-t " + tracks;
	////////////////////////////////////
	
	/*if(config->readBoolEntry("Split Output", false))
		imageAddr->insert(imageAddr->findRev("."), "*");*/
	
	
	*cdboShellProc << "-Owav" ;
		
	connect(cdboShellProc, SIGNAL(processExited(KProcess *)),
      			this, SLOT(playProcessDoneSlot(KProcess *)));
 	connect(cdboShellProc, SIGNAL(receivedStdout (KProcess *, char *, int)),
		this, SLOT(receivedPlayMessageSlot(KProcess *, char *, int)));
	connect(cdboShellProc, SIGNAL(receivedStderr (KProcess *, char *, int)),
		this, SLOT(receivedPlayMessageSlot(KProcess *, char *, int)));

	/*	cerr << "\n\n\n";
		QValueList<QCString> list = cdboShellProc->args();
		for(int i = 0; i < (int)list.count(); i++)	
			cerr << list[i];	
		cerr << "\n\n\n";
	*/	
	if(!cdboShellProc->start(KProcess::NotifyOnExit, KProcess::AllOutput)){
		
		delete cdboShellProc;
		emit failedToLunchProcess();
		currentProc = 0;
		curJob = 0;
		return;
	}
	curJob = 0;
	currentProc = cdboShellProc;
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::receivedPlayMessageSlot(KProcess *, char *buffer, int buflen){
	//cerr << "Counting audio tracks...\n";
	if(buflen > 0){
		QString buff(buffer);
		buff += buff.left(buflen);
		//cerr << buff;
		//cerr << "\n\n\n";
		if(buff.contains("Permission denied") || buff.contains("No such") || buff.contains("--help") ||
			buff.contains("Inquiry command failed") || buff.contains("error") || buff.contains("load cdrom please") ||
			buff.contains("Invalid argument") || buff.contains("No medium found") || buff.contains("Usage:") ||
			buff.contains("This disk has no audio tracks")){
				
			emit processDone(false);
			killCurrentTask();
			if(user)
				user->output(CdboDialogBase::ErrorMessage,
					i18n("Failed to load track(s)."));
			
			return;
		}
		/////////////// seems like no errors
		int spot = buff.find("%");
		if(spot !=-1){
			buff = buff.left(spot);
			buff = (buff.right(buff.length() - buff.findRev(" "))).stripWhiteSpace();
			//cerr << "message = '" << message << "'\n";
		
			if(buff.toInt() == 100)
				emit songDone();
			return;
		}
	}
}
//---------------------------------------------------------------------------------------------
void CdboAudioActionFactory::playProcessDoneSlot(KProcess *proc){
	delete proc;
	user = 0;
	currentProc = 0;
	list =0;
	emit playDone();
	//uncheck = false;
}





