/***************************************************************************
                          kbearlistjob.cpp  -  description
                             -------------------
    begin                : sön apr 28 2002
    copyright            : (C) 2003 by Björn Sahlström
    email                : kbjorn@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.                                   *
 *                                                                         *
 ***************************************************************************/

//////////////////////////////////////////////////////////////////////
// Qt specific include files
//////////////////////////////////////////////////////////////////////
// KDE specific include files
#include <kio/scheduler.h>
#include <kapplication.h>
#include <kdebug.h>
//////////////////////////////////////////////////////////////////////
// Application specific include files
#include "kbearlistjob.h"
#include "connectionmanager.h"


using namespace KBear;

#include "kbearlistjob.moc"

KBearListJob::KBearListJob( int ID, const KURL& u, bool showProgressInfo, bool _recursive, QString _prefix, bool _includeHidden) :
    KIO::SimpleJob(u, KIO::CMD_LISTDIR, QByteArray(), showProgressInfo),
    recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0),
	 m_ID( ID )
{
    // We couldn't set the args when calling the parent constructor,
    // so do it now.
    QDataStream stream( m_packedArgs, IO_WriteOnly );
    stream << u;
}

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

    if (recursive) {
        KIO::UDSEntryListConstIterator it = list.begin();
        KIO::UDSEntryListConstIterator end = list.end();

        for (; it != end; ++it) {
            bool isDir = false;
            bool isLink = false;
            QString filename;

            KIO::UDSEntry::ConstIterator it2 = (*it).begin();
            KIO::UDSEntry::ConstIterator end2 = (*it).end();
            for( ; it2 != end2; it2++ ) {
                switch( (*it2).m_uds ) {
                    case KIO::UDS_FILE_TYPE:
                        isDir = S_ISDIR((*it2).m_long);
                        break;
                    case KIO::UDS_NAME:
                        filename = (*it2).m_str;
                        break;
                    case KIO::UDS_LINK_DEST:
                        // This is a link !!! Don't follow !
                        isLink = !(*it2).m_str.isEmpty();
                        break;
                    default:
                        break;
                }
            }
            if (isDir && !isLink) {
                // skip hidden dirs when listing if requested
                if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
                    KURL newone = url();
                    newone.addPath(filename);
                    KBearListJob *job = new KBearListJob( m_ID, newone, m_progressId!=0, true, prefix + filename + "/",includeHidden);
                    ConnectionManager::getInstance()->attachJob( m_ID, job );
                    connect(job, SIGNAL(entries( KIO::Job *,
                                                 const KIO::UDSEntryList& )),
                            SLOT( gotEntries( KIO::Job*,
                                              const KIO::UDSEntryList& )));
                    addSubjob(job);
                }
            }
        }
    }

    // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
    // exclusion of hidden files also requires the full sweep, but the case for full-listing
    // a single dir is probably common enough to justify the shortcut
    if (prefix.isNull() && includeHidden) {
        emit entries(this, list);
    } else {
        // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
        KIO::UDSEntryList newlist;

        KIO::UDSEntryListConstIterator it = list.begin();
        KIO::UDSEntryListConstIterator end = list.end();
        for (; it != end; ++it) {

            KIO::UDSEntry newone = *it;
            KIO::UDSEntry::Iterator it2 = newone.begin();
            QString filename;
            for( ; it2 != newone.end(); it2++ ) {
                if ((*it2).m_uds == KIO::UDS_NAME) {
                    filename = (*it2).m_str;
                    (*it2).m_str = prefix + filename;
                }
            }
            // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
            // the the toplevel dir, and skip hidden files/dirs if that was requested
            if (  (prefix.isNull() || (filename != ".." && filename != ".") )
               && (includeHidden || (filename[0] != '.') )  )
                newlist.append(newone);
        }

        emit entries(this, newlist);
    }
}

void KBearListJob::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
{
    // Forward entries received by subjob - faking we received them ourselves
    emit entries(this, list);
}

void KBearListJob::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 KBearListJob::slotRedirection( const KURL & url )
{
     if (!kapp->authorizeURLAction("redirect", m_url, url))
     {
       kdWarning(7007) << "KBearListJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " 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, url );
}

void KBearListJob::slotFinished()
{
    if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error )
    {
        // Return slave to the scheduler
        KIO::SimpleJob::slotFinished();
    } else {
        kdDebug(7007) << "KBearListJob: Redirection to " << m_redirectionURL.prettyURL() << 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();
        ConnectionManager::getInstance()->attachJob( m_ID, this );
    }
}

KBearListJob* KBearListJob::listDir( int ID, const KURL& url, bool showProgressInfo, bool includeHidden )
{
    KBearListJob * job = new KBearListJob( ID, url, showProgressInfo,false,QString::null,includeHidden);
    return job;
}

KBearListJob* KBearListJob::listRecursive( int ID, const KURL& url, bool showProgressInfo, bool includeHidden )
{
    KBearListJob * job = new KBearListJob( ID, url, showProgressInfo, true,QString::null,includeHidden);
    return job;
}

void KBearListJob::start( KIO::Slave *slave)
{
    connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )),
             SLOT( slotListEntries( 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 &) ) );

    KIO::SimpleJob::start(slave);
}
