/***************************************************************************
 *   Copyright (C) 2004 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 "kdarListRecursive.h"

#include "kdar.h"

#include <kio/job.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kio/scheduler.h>
#include <kio/slave.h>
#include <kurl.h>

using namespace KIO;

KDarListJob::KDarListJob( const KURL& u, libdar::et_mask directoryMask, libdar::et_mask fileMask, QString _prefix ) :
    SimpleJob( u, CMD_LISTDIR, QByteArray(), false/*showProgressInfo*/ ),
    recursive( true ), includeHidden( true ), prefix( _prefix ), m_processedEntries(0), m_directoryMask( directoryMask ),
    m_fileMask ( fileMask )
{
    // We couldn't set the args when calling the parent constructor,
    // so do it now.
    QDataStream stream( m_packedArgs, IO_WriteOnly );
    stream << u;
}

void KDarListJob::slotProcessEntries( const KIO::UDSEntryList & list )
{
    // Emit progress info (takes care of emit processedSize and percent)
    m_processedEntries += list.count();
    slotProcessedSize( m_processedEntries );

    unsigned long newArchiveEntries = 0;

    UDSEntryListConstIterator it = list.begin();
    UDSEntryListConstIterator end = list.end();
    for ( ; it != end; ++it )
    {
        bool isDir = false;
        bool isLink = false;
        QString filename;
        //Is this file being added to the archive?
        bool passesDirectoryFilter = false;
        bool passesFileFilter = false;

        UDSEntry::ConstIterator it2 = (*it).begin();
        UDSEntry::ConstIterator end2 = (*it).end();
        for( ; it2 != end2; it2++ )
        {
            switch( (*it2).m_uds )
            {
                case UDS_FILE_TYPE:
                    isDir = S_ISDIR( (*it2).m_long );
                    break;
                case UDS_NAME:
                    if( filename.isEmpty() )
                    {
                        filename = (*it2).m_str;
                    }
                    break;
                case UDS_URL:
                    filename = KURL( (*it2).m_str ).fileName();
                    break;
                case UDS_LINK_DEST:
                    // This is a link !!! Don't follow !
                    isLink = !(*it2).m_str.isEmpty();
                    break;
                default:
                    break;
            }
        }
        QString pathToCheck = m_url.path();
        if ( !pathToCheck.endsWith( "/" ) ) pathToCheck += "/";
        pathToCheck += filename;
        bool isDot = ( filename == ".." || filename == "." );
        if ( filename.isEmpty() || isDot )
        {
            passesDirectoryFilter = false;
            passesFileFilter = false;
        }
        else
        {
            try
            {
                if ( isDir )
                {
                    passesDirectoryFilter = m_directoryMask.is_covered( kdar::toStdString( pathToCheck ) );
                }
                else
                {
                    passesDirectoryFilter = m_directoryMask.is_covered( kdar::toStdString( pathToCheck ) );
                    passesFileFilter = m_fileMask.is_covered( kdar::toStdString( pathToCheck ) );
                }
            }
            catch( libdar::Egeneric &e )
            {
                std::cout << "KDarListJob::slotProcessEntries(): Caught Exception: " << e.get_message() << std::endl;
            }
        }
//          std::cout << "KDarListJob::slotProcessEntries(): " << pathToCheck << ": isDir="<< isDir << ": passesDirectoryFilter is " << passesDirectoryFilter << "; passesFileFilter is " << passesFileFilter << std::endl;
        //Do not descend into a directory if it is excluded by filters:
        if ( isDir && !isLink && passesDirectoryFilter )
        {
            KURL newone = url();
            newone.addPath( filename );
            KDarListJob *job = new KDarListJob( newone, m_directoryMask, m_fileMask, prefix + filename + "/" );
            Scheduler::scheduleJob( job );
            connect( job, SIGNAL( entries( KIO::Job *, const unsigned long ) ), SLOT( gotEntries( KIO::Job *, const unsigned long ) ) );
            addSubjob( job );
        }
        if ( isDir && !isDot && passesDirectoryFilter ) ++newArchiveEntries;
        if ( !isDir && passesDirectoryFilter && passesFileFilter ) ++newArchiveEntries;
    }
    emit entries( this, newArchiveEntries );
//    std::cout << "KDarListJob::slotProcessEntries(): path=\"" << m_url.path() << "\": newArchiveEntries = " << newArchiveEntries << std::endl;
}

void KDarListJob::gotEntries( KIO::Job *, const unsigned long newArchiveEntries )
{
    // Forward entries received by subjob - faking we received them ourselves
    emit entries( this, newArchiveEntries );
}

void KDarListJob::slotResult( KIO::Job * job )
{
    // If we can't list a subdir, the result is still ok
    // This is why we override Job::slotResult() - to skip error checking
    removeSubjob( job );
}

void KDarListJob::slotRedirection( const KURL & url )
{
    if ( !kapp->authorizeURLAction( "redirect", m_url, url ) )
    {
        kdWarning() << "KDarListJob: Redirection from " << m_url << " to " << url << " REJECTED!" << endl;
        return;
    }
    m_redirectionURL = url; // We'll remember that when the job finishes
    if ( m_url.hasUser() && !url.hasUser() && ( m_url.host().lower() == url.host().lower() ) )
    {
        m_redirectionURL.setUser(m_url.user()); // Preserve user
    }
    emit redirection( this, m_redirectionURL );
}

void KDarListJob::slotFinished()
{
    if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error )
    {
        // Return slave to the scheduler
        SimpleJob::slotFinished();
    }
    else
    {
        //kdDebug() << "KDarListJob: Redirection to " << m_redirectionURL << endl;
        if ( queryMetaData( "permanent-redirect" ) == "true" )
        {
            emit permanentRedirection( this, m_url, m_redirectionURL );
        }
        m_url = m_redirectionURL;
        m_redirectionURL = KURL();
        m_packedArgs.truncate( 0 );
        QDataStream stream( m_packedArgs, IO_WriteOnly );
        stream << m_url;

        // Return slave to the scheduler
        slaveDone();
        Scheduler::doJob( this );
    }
}

void KDarListJob::slotMetaData( const KIO::MetaData &_metaData )
{
    SimpleJob::slotMetaData( _metaData );
    storeSSLSessionFromJob( m_redirectionURL );
}

KDarListJob *KIO::KDarListRecursive( const KURL & url, libdar::et_mask directoryMask, libdar::et_mask fileMask )
{
    KDarListJob * job = new KDarListJob( url, directoryMask, fileMask, QString::null );
    return job;
}

void KDarListJob::setUnrestricted( bool unrestricted )
{
    if ( unrestricted )
    {
       extraFlags() |= EF_ListJobUnrestricted;
    }
    else
    {
       extraFlags() &= ~EF_ListJobUnrestricted;
    }
}

void KDarListJob::start( Slave *slave )
{
    if ( !kapp->authorizeURLAction( "list", m_url, m_url ) && !( extraFlags() & EF_ListJobUnrestricted ) )
    {
        m_error = ERR_ACCESS_DENIED;
        m_errorText = m_url.url();
        QTimer::singleShot( 0, this, SLOT( slotFinished() ) );
        return;
    }
    connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList & ) ),
             SLOT( slotProcessEntries( const KIO::UDSEntryList & ) ) );
    connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ),
             SLOT( slotTotalSize( KIO::filesize_t ) ) );
    connect( slave, SIGNAL( redirection( const KURL & ) ),
             SLOT( slotRedirection( const KURL & ) ) );

    SimpleJob::start( slave );
}

void KDarListJob::virtual_hook( int id, void* data )
{ SimpleJob::virtual_hook( id, data ); }


#include "kdarListRecursive.moc"
