/***************************************************************************
*	Copyright (C) 2004 by karye												*
*	karye@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.										*
*																			*
*	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.,											*
*	59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.				*
***************************************************************************/

#include <cstring>            // for memset
#include <kdirwatch.h>         // for KDirWatch
#include <kio/filecopyjob.h>   // for file_copy, file_move
#include <kio/job_base.h>      // for operator|, HideProgressInfo, Overwrite
#include <kio/listjob.h>       // for ListJob, listRecursive
#include <kio/simplejob.h>     // for chmod, file_delete
#include <KIO/StatJob>
#include <klocalizedstring.h>  // for i18n
#include <kmessagebox.h>       // for information, sorry
#include <qbytearray.h>        // for QByteArray
#include <qdatetime.h>         // for QDateTime
#include <qdebug.h>            // for QDebug, operator<<
#include <qdir.h>              // for QDir
#include <qfile.h>             // for QFile
#include <qglobal.h>           // for QGlobalStatic, Q_UNUSED, foreach, qDebug
#include <qurl.h>              // for QUrl
#include <sys/stat.h>          // for stat

#include "common.h"            // for LogSingleton, DEBUG_LINE_INFO, KUROO
#include "core/etcupdate.h"
#include "etcupdate.h"         // for EtcUpdate, EtcUpdate::m_rxBackupDir
#include "global.h"            // for kurooDir
#include "log.h"               // for Log
#include "portagedb.h"         // for KurooDB
#include "settings.h"          // for KurooConfig

/**
* @class EtcUpdate
* @short Handles etc-updates.
*
* Runs etc-update to collect the list of etc-files that needs merging.
* Launches the exernal diff-tool for selected files.
*/
void EtcUpdate::init(QWidget* parent)
{
	DEBUG_LINE_INFO;
	m_parent = parent;

	eProc = new KProcess();
	connect(eProc, static_cast<void(KProcess::*)(int,QProcess::ExitStatus)>(&KProcess::finished), this, &EtcUpdate::slotCleanupDiff);

	m_mergingFile = new KDirWatch( this );
	connect(m_mergingFile, &KDirWatch::dirty, this, &EtcUpdate::slotChanged);
	connect(m_mergingFile, &KDirWatch::created, this, &EtcUpdate::slotChanged);
}

/**
* Scan for new configuration files.
*/
void EtcUpdate::slotEtcUpdate()
{
	DEBUG_LINE_INFO;
	if ( KurooConfig::etcUpdateTool().isEmpty() )
		KMessageBox::information( m_parent, i18n( "Please specify merge tool in settings!" ), i18n( "Kuroo" ) );
	else {
		m_etcFilesList.clear();
		m_backupFilesList.clear();

		// First collect old merged files
		m_configProtectList = QStringList( kurooDir + QStringLiteral("backup/configuration") );

		// Then scan for new unmerged files
		m_configProtectList += KurooConfig::configProtectList().split( u' ' );

		qDebug() << m_configProtectList;

		// Launch scan slave
		slotFinished();
	}
}

/**
* Scan each confProtect dirs.
*/
void EtcUpdate::slotFinished(/*KJob* j*/)
{
	if ( m_configProtectList.count() > 0 ) {
		m_configProtectDir = m_configProtectList.takeFirst();
		//The Slave was causing a 'kuroo(5827)/kio (KIOJob) KIO::SlaveInterface::dispatch: error  111   "/var/cache/kuroo/backup/configuration"' message
		//so don't do it if the dir doesn't exist
		if (QFile::exists( m_configProtectDir )) {
			KIO::ListJob* job = KIO::listRecursive( QUrl::fromLocalFile( m_configProtectDir ), KIO::HideProgressInfo);
			connect( job, &KIO::ListJob::entries, this, &EtcUpdate::slotListFiles );
			connect( job, &KIO::ListJob::result, this, &EtcUpdate::slotFinished );
		} else
			slotFinished();
	} else
		Q_EMIT signalScanCompleted();
}

/**
* Collect new configuration files.
*/
void EtcUpdate::slotListFiles( KIO::Job* /*unused*/, const KIO::UDSEntryList& entries )
{
	QString configFile;
	for( const KIO::UDSEntry& entry : entries ) {
		configFile = entry.stringValue( KIO::UDSEntry::UDS_NAME );

		if ( configFile.contains( m_rxBackupDir ) && !configFile.endsWith( QStringLiteral(".orig") ) ) {
			m_backupFilesList += m_configProtectDir + u'/' + configFile;
		} else if ( !m_configProtectDir.startsWith( QStringLiteral("/var/cache/kuroo") ) && configFile.contains( QStringLiteral("._cfg") ) ) {
			m_etcFilesList += m_configProtectDir + u'/' + configFile;
		}
	}
}

/**
* Launch diff tool with first etc-file in list.
*/
void EtcUpdate::runDiff( const QString& source, const QString& destination/*, const bool& isNew */)
{
	if ( !source.isEmpty() ) {
		m_changed = false;
		m_source = source;
		m_destination = destination;
		QString backupPath = kurooDir + QStringLiteral("backup/configuration/");

		// Check for etc-files warnings
		QString etcWarning;
		const QStringList etcFilesWarningsList = KurooConfig::etcFiles().split( u' ' );
		for( const QString& file : etcFilesWarningsList ) {
			if ( file == m_destination ) {
				etcWarning = i18n( "<font color=red>Warning!<br/>%1 has been edited by you.</font><br/>", m_destination );
			}
		}

		eProc->close();
		eProc->clearProgram();
		QStringList args;
		if (QStringLiteral("kdiff3") == KurooConfig::etcUpdateTool()) {
			// -m starts in merge mode and -o declares the destination
			args << QStringLiteral("-m") << QStringLiteral("-o") << m_destination;
		}
		args << m_source;
		args << m_destination;
		eProc->setArguments( args );
		eProc->setProgram( KurooConfig::etcUpdateTool(), args );

		/*if ( isNew )
			*eProc << "-o" << m_destination;
		else
			*eProc << "-o" << backupPath + "merging";
		*/
		eProc->start();

		if ( eProc->state() == QProcess::NotRunning ) {
			LogSingleton::Instance()->writeLog( i18n( "%1 didn't start.", KurooConfig::etcUpdateTool() ), ERROR );
			KMessageBox::error( m_parent, i18n( "%1 could not start!", KurooConfig::etcUpdateTool() ), i18n( "Kuroo" ) );
		} else {
			LogSingleton::Instance()->writeLog( i18n( "Merging changes in \'%1\'.", m_destination ), KUROO );

			// get the original file mode
			m_mergedMode = -1;
			KIO::StatJob* statJob = KIO::stat( QUrl::fromLocalFile( m_destination ) );
			if( statJob->exec() ) {
				m_mergedMode = statJob->statResult().numberValue(KIO::UDSEntry::StandardFieldTypes::UDS_ACCESS);
			}

			// Watch for changes
			m_mergingFile->addFile( m_destination );
			m_mergingFile->startScan();

			// Make temporary backup of original conf file
			KIO::file_copy( QUrl::fromLocalFile( m_destination ), QUrl::fromLocalFile( backupPath + QStringLiteral("merging.orig") ), m_mergedMode, KIO::Overwrite | KIO::HideProgressInfo );
		}
	}
}

void EtcUpdate::slotChanged( const QString& path )
{
	Q_UNUSED( path )
	m_changed = true;
}

/**
* After diff tool completed, close all.
* @param proc
*/
void EtcUpdate::slotCleanupDiff( int exitCode, QProcess::ExitStatus exitStatus )
{
	Q_UNUSED( exitCode )
	Q_UNUSED( exitStatus )
	//Unregister the watcher
	m_mergingFile->removeFile( m_destination );

	if ( m_changed ) {

		QDateTime dt = QDateTime::currentDateTime();
		QString backupPath = kurooDir + QStringLiteral("backup/configuration/");
		QString backupPathDir = backupPath + dt.toString( QStringLiteral("yyyyMMdd_hhmm") ) + u'/';
		QDir d( backupPathDir );
		if ( !d.exists() ) {
			QDir bc( backupPath );
			if( !bc.exists() )
				bc.mkdir( backupPath );
			d.mkdir( backupPathDir );
		}

		// Make backup of original file
		QString destination = m_destination;
		destination.replace( u'/', u':' );

		//Change this to a move instead of copy so we don't leave the temp file around
		KIO::file_move( QUrl::fromLocalFile( backupPath + QStringLiteral("merging.orig") ), QUrl::fromLocalFile( backupPathDir + destination + QStringLiteral(".orig") ), m_mergedMode, KIO::Overwrite | KIO::HideProgressInfo );
		KIO::file_copy( QUrl::fromLocalFile( m_destination ), QUrl::fromLocalFile( backupPathDir + destination ), m_mergedMode, KIO::Overwrite );

		//This is only necessary because it seems that kdiff3 rewrites the mode.
		KIO::chmod( QUrl::fromLocalFile( m_destination ), m_mergedMode );

		KIO::file_delete( QUrl::fromLocalFile( m_source ) );

		LogSingleton::Instance()->writeLog( i18n( "Deleting \'%1\'. Backup saved in %2.", m_source, kurooDir + QStringLiteral("backup") ), KUROO );

		KurooDBSingleton::Instance()->addBackup( m_source, m_destination );
		Q_EMIT signalEtcFileMerged();
	}
}

