/* This file is part of the Keep project
   Copyright (C) 2006 Jean-Rémy Falleri <jr.falleri@laposte.net>

   Keep 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.

   Keep 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 Keep; if not, write to the
   Free Software Foundation, Inc.,
   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.           */

#include "rdbmanager.h"

#include <kprocess.h>
#include <kdebug.h>
#include <tqfile.h>
#include <keepsettings.h>

RDBManager::RDBManager()
{
}

RDBManager::~RDBManager()
{
}

void RDBManager::slotCheckBackup()
{
	TQValueList<Backup> backups = outdatedBackupList();
	TQValueList<Backup>::iterator it;
	for ( it = backups.begin(); it != backups.end(); ++it )
	{
		doBackup(*it);
	}
}

void RDBManager::slotForceBackup(TQValueList<Backup> backupList)
{
	TQValueList<Backup>::iterator it;
	for ( it = backupList.begin(); it != backupList.end(); ++it )
	{
		doBackup(*it);
	}
}

TQString RDBManager::compareAtTime(Backup backup, TQDateTime date)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--compare-at-time";
	*proc << TQString::number(date.toTime_t()); // increment date
	// Adds source and dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.source())));
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	// Adds the listener
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList output = listen->stdOut();
	TQStringList::iterator it = output.begin();

	kdDebug() << "Executed process: " << proc->args() << endl;

	delete listen;
	delete proc;
	
	return (*it);
}

TQString RDBManager::listChangedSince(Backup backup, TQDateTime date)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--list-changed-since";
	*proc << TQString::number(date.toTime_t()); // increment date
	// Adds the dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	// Adds a listener
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList output = listen->stdOut();
	TQStringList::iterator it = output.begin();

	kdDebug() << "Executed process: " << proc->args() << endl;

	delete listen;
	delete proc;
	
	return (*it);
}

TQString RDBManager::listAtTime(Backup backup, TQDateTime date)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--list-at-time";
	*proc << TQString::number(date.toTime_t()); // increment date
	// Adds the dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	// Adds a listener
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList output = listen->stdOut();
	TQStringList::iterator it = output.begin();

	kdDebug() << "Executed process: " << proc->args() << endl;

	delete listen;
	delete proc;
	
	return (*it);
}

TQValueList<TQDateTime> RDBManager::incrementList(Backup backup)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--list-increments";
	*proc << "-v2";
	*proc << "--parsable-output";
	// Adds the dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	// Adds a listener
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList output = listen->stdOut();
	TQStringList::iterator it = output.begin();
	
	TQStringList lines = TQStringList::split("\n",*it);
	TQValueList<TQDateTime> dateList;

	TQStringList::iterator it2 = output.begin();
	for ( it2 = lines.begin(); it2 != lines.end(); ++it2 )
	{
		TQStringList field = TQStringList::split(" ",*it2);
		TQStringList::iterator dateStr = field.begin();
		long timestamp = (*dateStr).toUInt();
		TQDateTime datetime;
		datetime.setTime_t(timestamp);
		dateList.append(datetime);
	}
	
	kdDebug() << "Executed process: " << proc->args() << endl;

	delete listen;
	delete proc;
	
	return dateList;
}

TQDateTime RDBManager::lastIncrement(Backup backup)
{
	TQValueList<TQDateTime> increments = incrementList(backup);
	TQDateTime last = increments.last();
	return last;
}

TQValueList<Backup> RDBManager::outdatedBackupList()
{
	BackupConfig config;
	TQValueList<Backup> backups = config.backupList();
	TQValueList<Backup> outdated;
	TQValueList<Backup>::iterator it;
	kdDebug() << "Detecting outdated backup." << endl;
	for ( it = backups.begin(); it != backups.end(); ++it )
	{
		TQDateTime last = lastIncrement(*it);
		TQDate today = TQDate::currentDate();
		if ( last.date().daysTo(today) >= (*it).interval() )
		{
			kdDebug() << "Detected outdated backup: " << (*it).source() << endl;
			outdated.append(*it);
		}
 	}
	return outdated;
}

void RDBManager::slotRestoreBackup(Backup backup,TQDateTime time)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--force";
	*proc << "--restore-as-of";
	*proc << TQString::number(time.toTime_t()); // Date of the increment to restore
	// Adds source and dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.source())));
	// Adds a listener (for output recording)
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	connect(proc,TQ_SIGNAL(receivedStderr(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdErr(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	kdDebug() << "Executed process: " << proc->args() << endl;

	if ( !listen->isOk() )
	{
		kdDebug() << "Error message: " << listen->errorMessage() << endl;
		emit backupError(backup,listen->errorMessage());
	}
	
	delete listen;
	delete proc;
}

bool RDBManager::isRDB()
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	*proc << "-V";
	// Adds a listener (for output recording)
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList outList = listen->stdOut();
	TQStringList::iterator out = outList.begin();

	kdDebug() << "Executed process: " << proc->args() << endl;
	
	delete listen;
	delete proc;

	if ( *out == "" )
		return false;
	return true;
}

TQString RDBManager::RDBVersion()
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	*proc << "-V";
	// Adds a listener (for output recording)
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	TQStringList outList = listen->stdOut();
	TQStringList::iterator out = outList.begin();

	kdDebug() << "Executed process: " << proc->args() << endl;

	delete listen;
	delete proc;

	return (*out).mid(13,5);
}

void RDBManager::doBackup(Backup backup)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess(KeepSettings::controlRDBPriority,KeepSettings::rDBPriority());
	// Adds include and exclude
	if ( backup.useIncludeExclude() )
	{
		TQStringList includeExcludeList = backup.includeExcludeList();
		TQStringList::iterator it;
		for ( it = includeExcludeList.begin(); it != includeExcludeList.end(); ++it )
		{
			TQString type = (*it).left(1);
			TQString file = (*it).right((*it).length() - 1);
			if ( type == "I" )
			{
				*proc << "--include";
				*proc << TQString(TQFile::encodeName(TDEProcess::quote(file)));
			}
			else if ( type == "E" )
			{
				*proc << "--exclude";
				*proc << TQString(TQFile::encodeName(TDEProcess::quote(file)));
			}
		}
	}	
	// Adds the option
	// For simple mode
	if ( !backup.useAdvancedConfig() )
	{
		if ( !backup.useCompression() )
			*proc << "--no-compression";
	
		if ( backup.excludeSpecialFiles() )
			*proc << "--exclude-special-files";
	}
	// For advanced mode
	else
	{
		TQStringList optionList = backup.optionList();
		for ( TQStringList::Iterator it = optionList.begin(); it != optionList.end(); ++it )
		{
			*proc << *it;
		}
	}
	// Adds source and dest
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.source())));
	*proc << TQString(TQFile::encodeName(TDEProcess::quote(backup.dest())));
	// Adds a listener (for output recording)
	RDBListener *listen = new RDBListener();
	connect(proc,TQ_SIGNAL(receivedStdout(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdOut(TDEProcess *,char *,int)));
	connect(proc,TQ_SIGNAL(receivedStderr(TDEProcess *,char *,int)),listen,TQ_SLOT(receivedStdErr(TDEProcess *,char *,int)));
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;
	
	kdDebug() << "Executed process: " << proc->args() << endl;
	kdDebug() << "Process status:  " << listen->isOk() << endl;

	if ( !listen->isOk() )
	{
		kdDebug() << "Error message: " << listen->errorMessage() << endl;
		emit backupError(backup,listen->errorMessage());
	}
	else
		emit backupSuccess(backup);

	delete listen;
	delete proc;

	if ( !backup.neverDelete() )
	{
		removeOldIncrements(backup);
	}
}

void RDBManager::removeOldIncrements(Backup backup)
{
	// Gets the rdiff-backup process
	TDEProcess *proc = RDBProcess();
	// Adds the options
	*proc << "--remove-older-than" << TQString("%1").arg(backup.deleteAfter()) + "D";
	// Adds dest
	*proc << backup.dest();
	// Starts the process
	if ( !proc->start(TDEProcess::Block, TDEProcess::AllOutput) )
        	kdDebug() << "Error starting rdiff-backup" << endl;

	kdDebug() << "Executed process: " << proc->args() << endl;

	delete proc;
}

TDEProcess *RDBManager::RDBProcess(bool isNice,int niceLevel)
{
	TDEProcess *proc = new TDEProcess();
	proc->setUseShell(true);
	if ( isNice )
	{
		*proc << "nice" << "-n" << TQString("%1").arg(niceLevel);
	}
	*proc << "rdiff-backup";

	return proc;
}

#include "rdbmanager.moc"
