/*
 * Copyright (c) 2004 Nokia. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * Neither the name of Nokia nor the names of its contributors may be
 * used to endorse or promote products derived from this software
 * without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "KWQLogging.h"

#include "KWQResourceLoader.h"
#include "KWQLoader.h"
#include "KWQString.h"
#include "KWQKJobClasses.h"
#include "KWQFoundationExtras.h"

#include "KWQDict.h"
class KWQResourceLoader;

namespace KIO {
    
class TransferJobPrivate
{
public:
    TransferJobPrivate(const KURL &kurl)
        : status(0)
		, metaData(0)
		, URL(kurl)	   
		, method("GET")
		, assembledResponseHeaders(true)
		, handle (0)
		, response(0)
		{
		}

    TransferJobPrivate(const KURL& kurl, const QByteArray &_postData)
        : status(0)
        , metaData(0)
		, URL(kurl)	   
		, method("POST")
		, postData(_postData)
		, assembledResponseHeaders(true)
		, handle(0)
		, response(0)
		{
		}
   
    ~TransferJobPrivate()
		{
			delete metaData;
			KWQReleaseResponse(response);
		}
    
    int status;
    QDict<QString> *metaData;
    KURL URL;
    QString method;
    QByteArray postData;

    bool assembledResponseHeaders;
    QString responseHeaders;    
	WebCoreResourceHandle* handle;
    WebCoreResponse* response;

};

TransferJob::TransferJob(const KURL &url, bool reload, bool showProgressInfo)
    : d(new TransferJobPrivate(url)),
      m_data(this, SIGNAL(data(KIO::Job*, const char*, int))),
      m_redirection(this, SIGNAL(redirection(KIO::Job*, const KURL&))),
      m_result(this, SIGNAL(result(KIO::Job*))),
      m_receivedResponse(this, SIGNAL(receivedResponse(KIO::Job*, WebCoreResponse *)))
{
}

TransferJob::TransferJob(const KURL &url, const QByteArray &postData, bool showProgressInfo)
    : d(new TransferJobPrivate(url, postData)),
      m_data(this, SIGNAL(data(KIO::Job*, const char*, int))),
      m_redirection(this, SIGNAL(redirection(KIO::Job*, const KURL&))),
      m_result(this, SIGNAL(result(KIO::Job*))),
      m_receivedResponse(this, SIGNAL(receivedResponse(KIO::Job*, WebCoreResponse *)))    
{

	//FIXME: this is hack to counter the fact that
    // http headers are unpleasant to parse :)
	addMetaData("expire-date","0");
}

TransferJob::~TransferJob()
{
	if (d->handle)
		d->handle->cancel(); // this will call TransferJob::cancel()

    delete d;
}

bool TransferJob::isErrorPage() const
{
    return d->status != 0;
}

int TransferJob::error() const
{
    return d->status;
}

void TransferJob::setError(int e)
{
    d->status = e;
}

QString TransferJob::errorText() const
{
    LOG(NotYetImplemented, "not yet implemented");
    return QString::null;
}


void TransferJob::assembleResponseHeaders() const
{
    if (!d->response)
		return;
    
    if (!d->assembledResponseHeaders) {
		d->responseHeaders = KWQResponseHeaderString(d->response);
		d->assembledResponseHeaders = true;
    }
}

QString TransferJob::queryMetaData(const QString &key) const
{
    if (key == "HTTP-Headers") {
		assembleResponseHeaders();
		return d->responseHeaders;
    }
    
    if (!d->metaData)
		return QString::null;
    
    QString* value = d->metaData->find(key);
    return value ? *value : QString::null;
}

void TransferJob::addMetaData(const QString &key, const QString &value)
{
    if (!d->metaData){
		d->metaData = new QDict<QString>();
		d->metaData->setAutoDelete(true);
    }
    
    d->metaData->insert(key, new QString(value));
}

void TransferJob::addMetaData(const QMap<QString, QString> &keysAndValues)
{
    if (!d->metaData){
		d->metaData = new QDict<QString>();
		d->metaData->setAutoDelete(true);
    }
    
    QMapConstIterator<QString, QString> it = keysAndValues.begin();
    QMapConstIterator<QString, QString> end = keysAndValues.end();
    while (it != end) {
		d->metaData->insert(it.key(), new QString(it.data()));
        ++it;
    }
}

void TransferJob::kill()
{
    delete this;
}

KURL TransferJob::url() const
{
    return d->URL;
}

QByteArray TransferJob::postData() const
{
    return d->postData;
}

QString TransferJob::method() const
{
    return d->method;
}

void TransferJob::emitData(const char *data, int size)
{
    m_data.call(this, data, size);
}

void TransferJob::emitRedirection(const KURL &url)
{
    m_redirection.call(this, url);
}

void TransferJob::emitResult()
{
    m_result.call(this);
}

void TransferJob::emitReceivedResponse(WebCoreResponse *response)
{
    d->assembledResponseHeaders = false;
    d->response = response;
    KWQRetainResponse(d->response);

    m_receivedResponse.call(this, response);
}


void TransferJob::setHandle(WebCoreResourceHandle* handle)
{
	ASSERT(!d->handle);

	d->handle = handle;
}

// WebCoreResourceLoader interface implementation 

void TransferJob::receivedResponse(WebCoreResponse* response)
{
	ASSERT(d->handle);
    ASSERT(response);

    emitReceivedResponse(response);
}

void TransferJob::redirectedToURL(const gchar *URL)
{
	ASSERT(URL);

    emitRedirection(KURL(URL));
}

void TransferJob::addData(const void *data, unsigned int length)
{
    ASSERT(data);
	ASSERT(lenght>=0);

    emitData((const char *)data, length);
}

/** called when resource loading is cancelled. */
void TransferJob::cancel(void)
{
	d->handle = 0;			  // handle must not be referenced anymore
	setError(1);
	emitResult();
}

/** called when resource loading has an error. */
void TransferJob::reportError(void)
{
	d->handle = 0; 				//handle must not be referenced anymore
    setError(1);
	emitResult();
}

/** called when resource loading is done successfully */
void TransferJob::finish(void)
{
	d->handle = 0;			  // handle must not be referenced anymore
	emitResult();
}


} // namespace KIO
