/***************************************************************************
 *   Copyright (C) 2004, 2005 by Johnathan Burchill                        *
 *   jkerrb@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.                                   *
 ***************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "createArchiveThread.h"

#include "kdar.h"
#include "controller.h"
#include "kdardeclarations.h"
#include "archiveHandle.h"

#include "kdarCheckFileSizeEvent.h"
#include "kdarConfig.h"
#include "KDarInteraction.h"
#include "kdarListViewItem.h"
#include "kdarProgressEvent.h"
#include "kdarSetCursorEvent.h"
#include "kdarstatistics.h"
#include "kdarStatusBarEvent.h"
#include "kdarWarningEvent.h"
#include "openArchiveThread.h"

#include <kcursor.h>
#include <kdebug.h>
#include <klocale.h>

#include <qcheckbox.h>
#include <qdatetime.h>
#include <qfile.h>
#include <qlistview.h>
#include <qregexp.h>
#include <qstring.h>
#include <qstringlist.h>

#include <dar/libdar.hpp>
#include <dar/erreurs.hpp>

#include <string>

#include <pthread.h>

extern bool libdarWorking;

extern libdar::archive *theArchive;

createArchiveThread::createArchiveThread( controller * aController, KDarConfig * kdarconfig )
    : KDarThread( aController, kdarconfig )
{}

createArchiveThread::~createArchiveThread()
{}

void createArchiveThread::run()
{
    //Every class that is derived from KDarThread must set this member variable:
    m_threadID = pthread_self();

    libdarWorking = true;
    openLogFile();
    setWaitCursor();
    enableCancelButton();
    logMessage( "\n" + i18n( "BEGIN CREATE: " ) + QDateTime::currentDateTime().toString( "ddd dd MMM yyyy hh:mm:ss" ) + " " + m_kdc->newArchiveName() + "\n", true );
    logMessage( i18n( "command line: " ) + m_kdc->buildDarCommandLine( KDar::LIBDAR_CREATE ) + "\n", true );
    statusMessage( i18n( "Creating archive...\n" ), FOREVER );
    kdDebug() << "createArchiveThread::run(): m_kdc->newArchiveName() is " << m_kdc->newArchiveName() << endl;
    archiveHandle archiveHndl( m_kdc->newArchiveName() );
    kdDebug() << "createArchiveThread::run(): m_kdc->directoryToBackup() is " << m_kdc->directoryToBackup() << endl;
    libdar::path fs_root( kdar::toStdString( m_kdc->directoryToBackup() ) );
    kdDebug() << "The fs_root is " << fs_root.display().c_str() << endl;

    //Need some way to interact with user:
    KDarInteraction userInteractionDialog( m_controller );
    //Run the backup...

    //Remove any archives of the same path that already exist
    //See dar-libdar_api@lists.sf.net mailing list for discussion of
    //removing files from disk
    //Now libdar performs this task. Also, we should ask user if
    //the archive should be overwritten.
/*    int i( 1 );
    QFile oldArchive( m_kdc->newArchiveName() + QString( "." ) + QString::number( i ) + QString( "." ) + EXTENSION );
    while ( oldArchive.exists() )
    {
        kdDebug() << "removing " << oldArchive.name() << endl;
        oldArchive.remove();
        oldArchive.setName( m_kdc->newArchiveName() + QString( "." ) + QString::number( ++i ) + QString( "." ) + EXTENSION );
    }
*/
    //Start the progressbar timer;
    startProgressBarUpdates();
    //Start the creationCheckFileSizeTimer
    startCheckFileSizeUpdates();

    if ( !m_kdc->differentialBackup() )
    {
        try
        {
            kdDebug() << "createArchiveThread::run(): fs_root: " << fs_root.display().c_str() << endl;
            kdDebug() << "createArchiveThread::run(): archiveHndl.getArchiveLibdarPath(): " << archiveHndl.getArchiveLibdarPath().display().c_str() << endl;
            kdDebug() << "createArchiveThread::run(): theArchive: N/A" << endl;
            kdDebug() << "createArchiveThread::run(): archiveHndl.getArchiveBasename(): " << archiveHndl.getArchiveBasename() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->allowOverwrite(): " << m_kdc->allowOverwrite() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->warnOnOverwrite(): " << m_kdc->warnOnOverwrite() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->verbose(): " << m_kdc->verbose() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->pauseBetweenSlices(): " << m_kdc->pauseBetweenSlices() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->keepPruned(): " << m_kdc->keepPruned() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->compressionAlgorithm: " << (int) m_kdc->compressionAlgorithm() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->compressionLevel(): " << m_kdc->compressionLevel() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->libdarSliceSize(): " << libdar::deci(m_kdc->libdarSliceSize()).human().c_str() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->libdarFirstSliceSize(): " << libdar::deci(m_kdc->libdarFirstSliceSize()).human().c_str() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->systemEA(): " << m_kdc->systemEA() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->userEA(): " << m_kdc->userEA() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->executeCommand(): " << m_kdc->executeCommand() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->minimumCompressionSizeInt(): " << m_kdc->minimumCompressionSizeInt() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->ignoreDump(): " << m_kdc->ignoreDump() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->ignoreID(): " << m_kdc->ignoreID() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->hourShiftInt(): " << libdar::deci(m_kdc->hourShiftInt()).human().c_str() << endl;
            kdDebug() << "createArchiveThread::run(): m_kdc->:dryRun()" << m_kdc->dryRun() << endl;
// TODO put these in KDarConfig:
            bool alterAccessTime = false;
            bool sameFilesystem = false;
// EndTODO
            kdDebug() << "createArchiveThread::run(): cryptoAlgorithm() is " << m_kdc->cryptoAlgorithm() << endl;
            kdDebug() << "createArchiveThread::run(): cryptoBlockSize() is " << m_kdc->cryptoBlockSize() << endl;
            //Information returned by the creation constructor.
            libdar::statistics stats;
            libdar::archive * newArchive = new libdar::archive( userInteractionDialog, fs_root, archiveHndl.getArchiveLibdarPath(), NULL, m_kdc->libdarCreateFileMask(), m_kdc->libdarCreateDirectoryMask(), kdar::toStdString( archiveHndl.getArchiveBasename() ), EXTENSION, m_kdc->allowOverwrite(), m_kdc->warnOnOverwrite(), m_kdc->verbose(), m_kdc->pauseBetweenSlices(), m_kdc->keepPruned(), (libdar::compression) m_kdc->compressionAlgorithm(), (libdar::U_I) m_kdc->compressionLevel(), m_kdc->libdarSliceSize(), m_kdc->libdarFirstSliceSize(), m_kdc->systemEA(), m_kdc->userEA(), kdar::toStdString( m_kdc->executeCommand() ), ( libdar::crypto_algo ) m_kdc->cryptoAlgorithm(), "", ( libdar::U_32 ) m_kdc->cryptoBlockSize(), m_kdc->libdarCreateCompressionMask(), (libdar::U_I) m_kdc->minimumCompressionSizeInt(), m_kdc->ignoreDump(), m_kdc->ignoreID(), (libdar::infinint) m_kdc->hourShiftInt(), m_kdc->dryRun(), alterAccessTime, sameFilesystem, stats );
            statusMessage( i18n( "Creating archive...done.\n" ) );
            KDarStatistics ks( stats );
            logMessage( ks.createStatistics( (KDar::logLevel) m_kdc->logLevel() ) );
            //Post a messageEvent showing info:
            logMessage( slicesWritten() );
            //TODO if automatic isolation is requested, then do it here.
            //Delete the newly created archive.
            if ( newArchive )
            {
                delete newArchive;
                newArchive = 0;
            }
        }
        catch( libdar::Euser_abort &e )
        {
            QString errorMessage = i18n( "Creating archive...cancelled by user." );
            statusMessage( errorMessage );
            logMessage( errorMessage );
        }
        catch( libdar::Egeneric &e )
        {
            userInteractionDialog.warning( e.get_message() );
            QString errorMessage = i18n( "Creating archive...ERROR: could not create archive." );
            statusMessage( errorMessage );
            logMessage( e.get_message().c_str() );
            logMessage( errorMessage );
        }
    }
    else
    {
        m_controller->waitForThread( controller::OPEN_THREAD );
        if ( theArchive )
        {
            try
            {
// TODO put these in KDarConfig:
                bool alterAccessTime = false;
                bool sameFilesystem = false;
// EndTODO
                //Information returned by the creation constructor.
                libdar::statistics stats;
                libdar::archive * newArchive = new libdar::archive( userInteractionDialog, fs_root, archiveHndl.getArchiveLibdarPath(), theArchive, m_kdc->libdarCreateFileMask(), m_kdc->libdarCreateDirectoryMask(), kdar::toStdString( archiveHndl.getArchiveBasename() ), EXTENSION, m_kdc->allowOverwrite(), m_kdc->warnOnOverwrite(), m_kdc->verbose(), m_kdc->pauseBetweenSlices(), m_kdc->keepPruned(), (libdar::compression) m_kdc->compressionAlgorithm(), (libdar::U_I) m_kdc->compressionLevel(), m_kdc->libdarSliceSize(), m_kdc->libdarFirstSliceSize(), m_kdc->systemEA(), m_kdc->userEA(), kdar::toStdString( m_kdc->executeCommand() ), ( libdar::crypto_algo ) m_kdc->cryptoAlgorithm(), "", ( libdar::U_32 ) m_kdc->cryptoBlockSize(), m_kdc->libdarCreateCompressionMask(), (libdar::U_I) m_kdc->minimumCompressionSizeInt(), m_kdc->ignoreDump(), m_kdc->ignoreID(), (libdar::infinint) m_kdc->hourShiftInt(), m_kdc->dryRun(), alterAccessTime, sameFilesystem, stats );
                statusMessage( i18n( "Creating archive...done.\n" ) );
                KDarStatistics ks( stats );
                logMessage( ks.createStatistics( (KDar::logLevel) m_kdc->logLevel() ) );
                //Post a messageEvent showing info:
                logMessage( slicesWritten() );
                //TODO if automatic isolation is requested, then do it here.
                //Delete the newly created archive.
                if ( newArchive )
                {
                    delete newArchive;
                    newArchive = 0;
                }
            }
            catch( libdar::Euser_abort &e )
            {
                QString errorMessage = i18n( "Creating archive...cancelled by user." );
                statusMessage( errorMessage );
                logMessage( errorMessage );
            }
            catch( libdar::Egeneric &e )
            {
                userInteractionDialog.warning( e.get_message() );
                QString errorMessage = i18n( "Creating archive...ERROR: could not create archive." );
                statusMessage( errorMessage );
                logMessage( e.get_message().c_str() );
                logMessage( errorMessage );
            }
            catch( ... )
            {
                userInteractionDialog.warning( "createArchiveThread::run(): Caught unknown exception during differential creation!\n" );
            }
        }
        else
        {
            statusMessage( i18n( "Could not open reference archive: creation aborted.\n" ) );
        }
    }
    ///TODO: Report some statistics...

    setNormalCursor();
    //Stop the progressbar timer;
    stopProgressBarUpdates();
    //Stop checking the archive slice sizes.
    stopCheckFileSizeUpdates();
    //Disable the cancel button command.
    disableCancelButton();
    //This message has to be posted from the KDarThread destructor so that
    //the thread has had a chance to finish all of its logging output.
    m_endMessage = i18n( "END CREATE: " ) + QDateTime::currentDateTime().toString( "ddd dd MMM yyyy hh:mm:ss" ) + " " + m_kdc->newArchiveName() + "\n";
    libdarWorking = false;

    deleteMe();
}

QString createArchiveThread::slicesWritten()
{
    //Get the last slice number:
    archiveHandle name( m_kdc->newArchiveName() );
    long lastSliceNumber = name.lastArchiveSliceNumber();
    //The info string: contains archive name, slices created and time elapsed.
    QString slices( name.getArchiveBasename() + ": " );
    slices.append( i18n( "wrote %n slice.\n", "wrote %n slices.\n", lastSliceNumber ) );
    return slices;
}
