/***************************************************************************
 *   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.                                   *
 ***************************************************************************/

#include "kdarthread.h"

#include "kdar.h"

#include "controller.h"
#include "kdarConfig.h"

#include "kdardeletethreadevent.h"
#include "kdarArchiveBrowserUpdatesEvent.h"
#include "kdarCheckFileSizeEvent.h"
#include "kdarCancelButtonEvent.h"
#include "kdarListViewItem.h"
#include "kdarLogFileCommandEvent.h"
#include "kdarProgressEvent.h"
#include "kdarSetCursorEvent.h"
#include "kdarStatusBarEvent.h"
#include "kdarWarningEvent.h"

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

#include <qapplication.h>
#include <qevent.h>

KDarThread::KDarThread( controller * parent, KDarConfig * kdarconfig, KListView *archiveFileView )
    : QThread( 0 ),
      m_controller( parent ),
      m_kdc( kdarconfig ),
      m_archiveFileView( archiveFileView ),
      m_threadID( 0 )
{
    m_endMessage = QString::null;
}

KDarThread::~KDarThread()
{
    QApplication::sendPostedEvents( m_controller, KDar::LOGFILECOMMAND );
    logMessage( m_endMessage, true );
    closeLogFile();
}

void KDarThread::enableCancelButton()
{
    kdarCancelButtonEvent * cancelButton = new kdarCancelButtonEvent( kdarCancelButtonEvent::ENABLE );
    QApplication::postEvent( m_controller, cancelButton );
}

void KDarThread::disableCancelButton()
{
    kdarCancelButtonEvent * cancelButton = new kdarCancelButtonEvent( kdarCancelButtonEvent::DISABLE );
    QApplication::postEvent( m_controller, cancelButton );
}

bool KDarThread::cancelOperation()
{
    bool cancelled = false;
    // m_threadID is the ID of the running thread. You need to make sure
    // that m_threadID is set in the "run" method of the derived class.
    if ( m_threadID )
    {
        libdar::cancel_thread( m_threadID );
        pthread_t libdartid;
        cancelled = libdar::cancel_current( libdartid );
        kdDebug() << "KDarThread::cancelOperation(): m_threadID=" << m_threadID << " and libdartid=" << libdartid << endl;
        if ( libdartid != m_threadID )
        {
            //Libdar is stuck on an old thread. Why?!!
            bool cleared = libdar::cancel_clear();
            if ( cleared )
            {
                kdDebug() << "KDarThread::cancelOperation(): trying to cancel m_threadID..." << endl;
                libdar::cancel_thread( m_threadID );
                cancelled = libdar::cancel_current( libdartid );
                kdDebug() << "KDarThread::cancelOperation(): m_threadID=" << m_threadID << " and libdartid=" << libdartid << endl;
            }
            else
            {
                kdDebug() << "KDarThread::cancelOperation(): could not cancel thread." << endl;
            }
        }
    }
    return cancelled;
}

void KDarThread::setWaitCursor()
{
    kdarSetCursorEvent *setWaitCursor = new kdarSetCursorEvent( KCursor::waitCursor() );
    QApplication::postEvent( m_controller, setWaitCursor );
}

void KDarThread::setNormalCursor()
{
    kdarSetCursorEvent *setArrowCursor = new kdarSetCursorEvent( KCursor::arrowCursor() );
    QApplication::postEvent( m_controller, setArrowCursor );
}

void KDarThread::statusMessage( const QString & message, const int duration )
{
    kdarStatusBarEvent *statusEvent = new kdarStatusBarEvent( message, duration );
    QApplication::postEvent( m_controller, statusEvent );

}

void KDarThread::logMessage( const QString & message, bool logFileOnly )
{
    if ( message.isNull() ) return;
    if ( logFileOnly )
    {
        if ( m_kdc->logLevel() != KDar::LOG_NONE )
        {
            QCustomEvent *logEvent = new kdarLogFileCommandEvent( kdarLogFileCommandEvent::WRITEMESSAGE, message );
            QApplication::postEvent( m_controller, logEvent );
        }
    }
    else
    {
        QCustomEvent *logEvent = new kdarWarningEvent( message );
        QApplication::postEvent( m_controller, logEvent );
    }
}

void KDarThread::openLogFile() const
{
    //open the log file
    kdDebug() << "KDarThread::openLogFile() creating new event." << endl;
    kdarLogFileCommandEvent * logCommandEvent = new kdarLogFileCommandEvent( kdarLogFileCommandEvent::OPEN, m_kdc->logFile() );
    kdDebug() << "KDarThread::openLogFile() posting the open logFile event" << endl;
    QApplication::postEvent( m_controller, logCommandEvent );
    kdDebug() << "KDarThread::openLogFile() done." << endl;
}

void KDarThread::closeLogFile() const
{
    //close the log file
    kdarLogFileCommandEvent * logCommandEvent = new kdarLogFileCommandEvent( kdarLogFileCommandEvent::CLOSE );
    QApplication::sendPostedEvents( m_controller, KDar::LOGFILECOMMAND );
    QApplication::postEvent( m_controller, logCommandEvent );
    kdDebug() << "KDarThread::closeLogFile()" << endl;
}

void KDarThread::disableBrowser()
{
    kdarArchiveBrowserUpdatesEvent * disableBrowser = new kdarArchiveBrowserUpdatesEvent( kdarArchiveBrowserUpdatesEvent::DISABLE );
    QApplication::postEvent( m_controller, disableBrowser );
}

void KDarThread::enableBrowser()
{
    kdarArchiveBrowserUpdatesEvent * enableBrowser = new kdarArchiveBrowserUpdatesEvent( kdarArchiveBrowserUpdatesEvent::ENABLE );
    QApplication::postEvent( m_controller, enableBrowser );
}

void KDarThread::startProgressBarUpdates()
{
    kdarProgressEvent *progressBarStart = new kdarProgressEvent( kdarProgressEvent::PROGRESS_START );
    QApplication::postEvent( m_controller, progressBarStart );
}

void KDarThread::stopProgressBarUpdates()
{
    kdarProgressEvent *progressBarStop = new kdarProgressEvent( kdarProgressEvent::PROGRESS_STOP );
    QApplication::postEvent( m_controller, progressBarStop );
}

void KDarThread::startCheckFileSizeUpdates()
{
    kdarCheckFileSizeEvent *checkFileSizeEventStart = new kdarCheckFileSizeEvent( kdarCheckFileSizeEvent::STARTTIMER );
    QApplication::postEvent( m_controller, checkFileSizeEventStart );
}

void KDarThread::stopCheckFileSizeUpdates()
{
    kdarCheckFileSizeEvent *checkFileSizeEventStop = new kdarCheckFileSizeEvent( kdarCheckFileSizeEvent::STOPTIMER );
    QApplication::postEvent( m_controller, checkFileSizeEventStop );
}

void KDarThread::deleteMe()
{
    kdarDeleteThreadEvent * deleteThisThread = new kdarDeleteThreadEvent( this );
    QApplication::postEvent( m_controller, deleteThisThread );
}

QString KDarThread::timeDifference( const QDateTime & startTime, const QDateTime & endTime )
{
    QString timeString;
    timeString += i18n( "Time to do the libdar operation", "Total time: " );
    //calculate days, hours, minutes, seconds
    int days = startTime.daysTo( endTime );
    int seconds = startTime.secsTo( endTime );
    int totalTime = seconds;
    //days
    if ( days > 0 )
    {
        timeString += i18n( "%n day, ", "%n days, ", days );
        seconds -= days * 86400;
    }
    //hours
    int hours = seconds / 3600;
    if ( hours > 0 )
    {
        timeString += i18n( "%n hour, ", "%n hours, ", hours );
        seconds -= hours * 3600;
    }
    //minutes
    int minutes = seconds / 60;
    if ( minutes > 0 )
    {
        timeString += i18n( "%n minute, ", "%n minutes, ", minutes );
        seconds -= minutes * 60;
    }
    //seconds
    if ( seconds > 0 )
    {
        timeString += i18n( "%n second, ", "%n seconds, ", seconds );
    }
    //less than one second
    if ( totalTime == 0 )
    {
        int secs = startTime.secsTo( QDateTime( startTime.date() ) );
        endTime.addSecs( secs );
        timeString += i18n( "total time for operation, in milliseconds. Leave \", \" at the end.", "%1 seconds, " ).arg( endTime.toString( "0.zzz" ) );
    }
    timeString.remove( timeString.length() - 2, 2 );
    timeString += ".\n";
    //return string
    return timeString;
}

libdar::ou_mask KDarThread::buildDirectoryMaskFromListView( QString extractionPrefix )
{
    libdar::ou_mask sub_include;
    if ( !m_archiveFileView ) return sub_include;

    //find which items are selected:
    QListViewItemIterator it( m_archiveFileView );
    kdDebug() << "KDarThread::buildDirectoryMaskFromListView(): extractionPrefix: " << extractionPrefix << endl;
    while ( it.current() )
    {
        kdarListViewItem *item = static_cast<kdarListViewItem*>(it.current());
        ++it;
        if ( item->isSelected() )
        {
            QString addToExtractionList = KDarConfig::cleanPath( extractionPrefix + "/" + item->path() );
            //We may have an empty QString since the user may select the
            //listView's firstChild(), i.e. the archive Name, which returns
            // "" from path().
            if ( !addToExtractionList.isEmpty() && !item->path().isEmpty() )
            {
// TODO handle caseSensitive in directory filters
                bool caseSensitive = false;
                sub_include.add_mask( ( libdar::simple_path_mask( kdar::toStdString( addToExtractionList ), caseSensitive ) ) );
                kdDebug() << "KDarThread::buildDirectoryMaskFromListView(): " << addToExtractionList << " OR'd with directory masks." << endl;
            }
        }
    }
    return sub_include;
}
