/*
 * 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 "ResourceLoadListener.h"

#include "GLibHelpers.h"

extern "C" {
static void _str_free(gpointer data)
{
    gchar* str = (gchar*) data;
    g_free(str);
}
}

class ResourceLoadResponse : public KWIQResponse
{
    gchar* _mimeType;
    GHashTable* _headers;
    int _statusCode;

 public:
    ResourceLoadResponse() 
	:_mimeType(0), _statusCode(-1)
    {
	_headers = g_hash_table_new_full(g_str_hash, g_str_equal, _str_free, _str_free);
    }

    ~ResourceLoadResponse() 
    { 
	g_hash_table_destroy(_headers);
	if (_mimeType) g_free(_mimeType);
    }    
   
    void setMimeType(const gchar* mime)
    { 
	assignToString(&_mimeType, mime);
    }

    void setStatusCode(int code) 
    {
	_statusCode = code;
    }

    int statusCode() 
    {
	return _statusCode;
    }

    const gchar* mimeType() 
    { 
	return _mimeType;
    }

    GHashTable* allHeaderFields()
    { 
	return _headers;
    }

    GTime expiresTime() 
    { 
	return 0; 
    }

    void addHeader(const HttpHeader *header) {
	assert(header);
	g_hash_table_insert(_headers, g_strdup(header->key()), g_strdup(header->value()));	
    }
};


/** idea of ResourceLoader is to hold WebCoreResourceLoader until corresponding handle is released.
 * after that, WebCoreResourceLoader is released
 */
ResourceLoadListener::ResourceLoadListener(BridgeImpl* b, WebCoreResourceLoader* aloader)
    :bridge(b)
    ,loader(aloader)
    ,response(new ResourceLoadResponse)
    ,request(0)
    ,status(0)
    ,resp_sent(false)
    ,_started(false)
    ,auth_attempted(false)
    ,cancelled(false)
{
    assert(bridge);
    assert(loader);
    loader->retain();
}

ResourceLoadListener::~ResourceLoadListener() 
{
#ifdef DEBUG
	g_printerr("%s :%x\n",__PRETTY_FUNCTION__, (int)(this));
#endif
}
    
bool ResourceLoadListener::header(const HttpRequest* r, const HttpHeader *header)
{
    switch (header->type()) {
    case HttpHeader::ContentType:
    {
	const HttpHeaderContentType *ct
	    = static_cast<const HttpHeaderContentType*>(header);
	response->setMimeType(ct->contentType());        
	break;
    }
    case HttpHeader::ContentLength:
    {
	const HttpHeaderContentLength *cl
	    = static_cast<const HttpHeaderContentLength*>(header);
        status.setSize(cl->contentLength());
	break;
    }
    case HttpHeader::Invalid:
    default:
    {
	break;
    }
    }

    response->addHeader(header);
    return true;
}

bool ResourceLoadListener::data(const HttpRequest*, const char* data, int len) 
{
    assert(resp_sent);

#ifdef DEBUG
    g_printerr("%s :%x  -- Received %d bytes data\n",__PRETTY_FUNCTION__, (int)(this), len);
#endif    

    loader->addData(data, len);
    status.addReceived(len);
    bridge->emitResourceLoadStatus(&status);

    return true;
}

bool ResourceLoadListener::started(const HttpRequest*) 
{
    assert(!_started);
    _started=true;
    bridge->emitResourceLoadStarted(&status);
    return true;
}

bool ResourceLoadListener::finished(const HttpRequest* r)	
{
    assert(loader);

    if (response->statusCode() >= 400)
	error(r);
    else	    
        loader->finish();

    loader->release();
    loader = 0;

    bridge->emitResourceLoadFinished(&status);
    if ( bridge->numPendingOrLoadingRequests()==0) {
	bridge->emitFrameLoadFinished(0);
    } else {
#ifdef DEBUG
	g_printerr("%s pending loads: %d\n",__PRETTY_FUNCTION__, bridge->numPendingOrLoadingRequests());
#endif
    }

    return true;
}
    
bool ResourceLoadListener::error(const HttpRequest*)
{
    status.setError();
    loader->reportError();
    return true;
}

bool ResourceLoadListener::headersEnd(const HttpRequest * request, int astatus)
{
    assert(loader);
    assert(!resp_sent);

    response->setStatusCode(astatus);

    resp_sent = true;
    loader->receivedResponse(response);
    bridge->emitResourceLoadHeaders(&status);
	
    return false;
}


bool ResourceLoadListener::authenticate(HttpRequest* request) 
{    
    OSB::URLCredentialStorage* creds = bridge->credentials();    
    OSB::URLProtectionSpace space(request->url(),
				  request->authRealm(),
				  OSB::URLProtectionSpace::Default, 
				  OSB::URLProtectionSpace::NoProxy);

    const OSB::URLCredential *cred = creds->defaultCredential(space);
    if (auth_attempted) {
	// authentication failed -- clear auth cache for that space
	if (cred) 
	    creds->removeCredential(*cred, space);
	cred = 0;
    }

    auth_attempted = true;
    
    if (!cred) {	
	gchar* user = 0;
	gchar* password = 0;
	bool ret = bridge->authenticate(request->authRealm(), &user, &password);
	if (ret && user && password) {
	    // update credentials
	    OSB::URLCredential newcred(user, password, OSB::URLCredential::ForSession);
	    creds->setCredential(newcred, space);
	    request->authenticate(newcred.user(), newcred.password());
	}
	if (user) g_free(user);
	if (password) g_free(password);
	return ret;
    }
    
    // try to apply authentication
    request->authenticate(cred->user(), cred->password());
    return true;
}


WebCoreResourceHandle* ResourceLoadListener::handle(HttpRequest* req)
{
    request = req;
    return this;
}

// WebCoreResourceHandle
void ResourceLoadListener::cancel()
{
    delete request;
    request = 0;
}
