/***************************************************************************
*                                slave.cpp
*                             -------------------
*
*    Revision     : $Id$
*    begin        : Tue Jan 29 2002
*    copyright    : (C) 2002 by Patrick Charbonnier
*                 : Based On Caitoo v.0.7.3 (c) 1998 - 2000, Matej Koss
*    email        : pch@freeshell.org
*
****************************************************************************/

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

#include <tdeapplication.h>

#include "getfilejob.h"
#include "slave.h"
#include "slaveevent.h"
#include "transfer.h"

#include <assert.h>

Slave::Slave(Transfer * _parent, const KURL & _src, const KURL & _dest)
    : TQObject(),
      TQThread()
{
    // FIXME
    // KGet uses an unconventional threading model that relies on TDEIO slave execution from the main GUI thread
    disableThreadPostedEvents(true);

    mDebug << ">>>>Entering" << endl;
    copyjob = NULL;
    m_src = _src;
    m_dest = _dest;
    m_parent = _parent;

    nPendingCommand = 0;

    mDebug << ">>>>Leaving" << endl;
}

Slave::~Slave()
{}

void Slave::Op(SlaveCommand _cmd)
{
    mDebugIn << " _cmd = " << _cmd << endl;

    if ( !running() ) { // start on demand
        start();
        moveToThread(this);
    }

    mutex.lock();
    stack.push(_cmd);
    nPendingCommand++;
    worker.wakeOne();
    mutex.unlock();

    mDebugOut << endl;
}

/** No descriptions */
void Slave::PostMessage(SlaveResult _event, TQ_ULLONG _data)
{
    SlaveEvent *e1 = new SlaveEvent(m_parent, _event, _data);

    TQApplication::postEvent(kapp->mainWidget(), (TQEvent *) e1);
}

void Slave::PostMessage(SlaveResult _event, const TQString & _msg)
{
    SlaveEvent *e1 = new SlaveEvent(m_parent, _event, _msg);

    TQApplication::postEvent(kapp->mainWidget(), (TQEvent *) e1);
    mDebug << "Msg:" << "_msg = " << _msg << endl;
}

void Slave::InfoMessage(const TQString & _msg)
{
    SlaveEvent *e1 = new SlaveEvent(m_parent, SLV_INFO, _msg);

    TQApplication::postEvent(kapp->mainWidget(), (TQEvent *) e1);
    mDebug << "Infor Msg:" << "_msg = " << _msg << endl;
}

void Slave::run()
{
    mDebugIn << endl;

    SlaveCommand cmd;
    bool running = true;

    while (running)
    {
        if (!nPendingCommand) {
            worker.wait();
        }
        switch (cmd = fetch_cmd())
        {
            case RESTART:
                // fall through
            case RETR:
                mDebug << " FETCHED COMMAND       RETR" << endl;
                PostMessage(SLV_RESUMED);
                break;

            case RETR_CACHE:
                mDebug << " FETCHED COMMAND       RETR_CACHE" << endl;
                break;

            case PAUSE:
                mDebug << " FETCHED COMMAND       PAUSE" << endl;
                PostMessage(SLV_PAUSED);
                break;

            case KILL:
                mDebug << " FETCHED COMMAND      KILL" << endl;
                running = false;
                // no message posted
                break;

            case REMOVE:
                mDebug << " FETCHED COMMAND       REMOVE" << endl;
                running = false;
                PostMessage(SLV_REMOVED);
                break;

            case SCHEDULE:
                mDebug << " FETCHED COMMAND       SCHEDULE" << endl;
                PostMessage(SLV_SCHEDULED);
                break;

            case DELAY:
                mDebug << " FETCHED COMMAND       DELAY" << endl;
                PostMessage(SLV_DELAYED);
                break;

            case NOOP:
                mDebug << "FETCHED COMMAND        NOOP, i.e. empty stack" << endl;
                running = false;
                break;

            default:
                mDebug << " UNKNOWN COMMAND DIE...." << endl;
                assert(0);
        }
    }

    assert(!copyjob);
    mDebugOut << endl;
}


Slave::SlaveCommand Slave::fetch_cmd()
{
    mutex.lock();
    SlaveCommand cmd = NOOP;
    if ( !stack.isEmpty() )
    {
        nPendingCommand--;
        cmd = stack.pop();
    }
    mutex.unlock();
    return cmd;
}


void Slave::Connect()
{
    mDebugIn << endl;

    connect(copyjob, TQ_SIGNAL(canceled(TDEIO::Job *)), TQ_SLOT(slotCanceled(TDEIO::Job *)));
    connect(copyjob, TQ_SIGNAL(connected(TDEIO::Job *)), TQ_SLOT(slotConnected(TDEIO::Job *)));
    connect(copyjob, TQ_SIGNAL(result(TDEIO::Job *)), TQ_SLOT(slotResult(TDEIO::Job *)));

    connect(copyjob, TQ_SIGNAL(totalSize(TDEIO::Job *, TDEIO::filesize_t)), TQ_SLOT(slotTotalSize(TDEIO::Job *, TDEIO::filesize_t)));

    connect(copyjob, TQ_SIGNAL(processedSize(TDEIO::Job *, TDEIO::filesize_t)), TQ_SLOT(slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t)));

    connect(copyjob, TQ_SIGNAL(speed(TDEIO::Job *, unsigned long)), TQ_SLOT(slotSpeed(TDEIO::Job *, unsigned long)));

    connect(copyjob, TQ_SIGNAL(infoMessage(TDEIO::Job *, const TQString &)), TQ_SLOT(slotInfoMessage(TDEIO::Job *, const TQString &)));

    mDebugOut << endl;
}


void Slave::slotCanceled(TDEIO::Job *)
{
    mDebugIn << endl;


    mDebugOut << endl;
}

void Slave::slotConnected(TDEIO::Job *)
{
    mDebugIn << endl;


    mDebugOut << endl;
}

void Slave::slotResult(TDEIO::Job * job)
{
    mDebugIn << endl;

    assert(copyjob == job);
    copyjob=0L;
    TDEIO::Error error=TDEIO::Error(job->error());
    if (!error) {
        PostMessage(SLV_FINISHED);
    }
    else {
        if (m_parent->getMode() == Transfer::MD_NEW)
        {
            PostMessage(SLV_NOTINCACHE);
            return;
        }
        TQString tmsg="<font color=\"red\"> <b>" + job->errorString() + \
                "</b></font>";
        InfoMessage(tmsg);
        if (m_parent->retryOnError() && \
            ((error==TDEIO::ERR_COULD_NOT_LOGIN) || (error==TDEIO::ERR_SERVER_TIMEOUT))) {
            //Timeout or login error
            PostMessage(SLV_ERROR);
        }
        else if (m_parent->retryOnBroken() && (error==TDEIO::ERR_CONNECTION_BROKEN)) {
            // Connection Broken
            PostMessage(SLV_BROKEN);
        }
        else {
            job->showErrorDialog();
            PostMessage(SLV_DELAYED);
        }
    }
    mDebugOut << endl;
}


void Slave::slotSpeed(TDEIO::Job *, unsigned long lSpeed)
{
    // mDebugIn<<endl;
    PostMessage(SLV_PROGRESS_SPEED, lSpeed);
    // mDebugOut<<endl;

}

void Slave::slotTotalSize(TDEIO::Job *, TDEIO::filesize_t _total_size)
{
    mDebugIn << "= " << _total_size << endl;

    if ((int)_total_size == 0)//shouldn't happen, but does
        return;

    if (m_parent->getMode() != Transfer::MD_NEW)
    {
    PostMessage(SLV_CAN_RESUME, copyjob->getCanResume());
    PostMessage(SLV_CONNECTED);
    }
    else
        InfoMessage("checking if file is in cache...yes");
    PostMessage(SLV_TOTAL_SIZE, _total_size);
    mDebugOut << endl;
}

void Slave::slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t _processed_size)
{
    // mDebugIn<<endl;
    PostMessage(SLV_PROGRESS_SIZE, _processed_size);

    // mDebugOut<<endl;
}

void Slave::slotInfoMessage(TDEIO::Job *, const TQString & _msg)
{
    mDebugIn << "MSG=" << _msg << endl;
    InfoMessage(_msg);
    mDebugOut << endl;
}

#include "slave.moc"
