/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  Implementation of SynchResourceHandleClient
*
*/


// INCLUDE FILES

#include "config.h"

#include "SynchLoader.h"

#include "DocumentLoader.h"
#include "FrameLoader.h"

namespace WebCore {

const int KSyncRequestTimeOut  = 3*1000*1000;

int syncRequestTimerCb( void* ptr );

// ============================SynchLoader MEMBER FUNCTIONS ===============================

class SynchResourceHandleClient;

SynchLoader::SynchLoader(Frame* frame, SynchResourceHandleClient* client, bool sendResourceLoadCallbacks, bool shouldContentSniff)
    : ResourceLoader(frame, sendResourceLoadCallbacks, shouldContentSniff)
    , m_client(client)
    , m_isdone(false)
{
    m_documentLoader->addSubresourceLoader(this);
}

SynchLoader::~SynchLoader()
{    
}

PassRefPtr<SynchLoader> SynchLoader::create(Frame* frame, const ResourceRequest& request, SynchResourceHandleClient* client)
{
    if (!frame)
        return 0;
    
    FrameLoader* fl = frame->loader();
    if (fl->state() == FrameStateProvisional)
        return 0;
    
    RefPtr<SynchLoader> loader(new SynchLoader(frame, client, true, false));
    if (!loader->load(request)) {
        return 0;
    }

    return loader.release();
}

bool SynchLoader::load(const ResourceRequest& r)
{
    return ResourceLoader::load(r);
}

void SynchLoader::didReceiveResponse(const ResourceResponse& r)
{
    ASSERT(!r.isNull());

    if (m_client) {
        m_client->didReceiveResponse(m_client->resourceHandle(), r);
    }
}

void SynchLoader::didReceiveData(const char* data, int length, long long lengthReceived, bool allAtOnce)
{
    if (m_client) {
        m_client->didReceiveData(m_client->resourceHandle(), data, length, lengthReceived);
    }
}

void SynchLoader::didFinishLoading()
{
    if (cancelled()) {
        return;
    }

    // Decrement ref counter on handle, so connection can be torn down or cancelled
    m_handle = 0;

    if (m_client) {
        m_client->didFinishLoading(m_client->resourceHandle());
    }
}

void SynchLoader::didFail(const ResourceError& error)
{
    if (cancelled()) {
        return;
    }

    // Decrement ref counter on handle, so connection can be torn down or cancelled
    m_handle = 0;

    if (m_client) {
        m_client->didFail(m_client->resourceHandle(), error);
    }
}

void SynchLoader::didCancel(const ResourceError& /*error*/)
{
    // Decrement ref counter on handle, so connection can be cancelled
    m_handle = 0;

    if (m_client) {
        m_client->cancel();    
    }
}

void SynchLoader::removeSubresourceLoader()
{
    if (!m_isdone) {
        // Avoid multiple cycles
        m_isdone = true;
        
        RefPtr<SynchLoader> protect(this);
        
        m_documentLoader->removeSubresourceLoader(this);
    }
 
}


// ============================ SynchResourceHandleClient MEMBER FUNCTIONS ===============================

SynchResourceHandleClient::SynchResourceHandleClient(TRequestStatus& aStatus, 
                                                     const ResourceRequest& request, 
                                                     Frame* frame)
    : m_finished(false)
    , m_response()
    , m_error(String(), KErrNone, String(), String())
    , m_data(0)
    , m_syncRequestTimer(0)
{
    m_status = &aStatus;
    
    // Send request
    m_loader = SynchLoader::create(frame, request, this);
    
    if (m_loader) {
        m_resourceHandle = m_loader->handle();
        
        //Start the synchronous request expiry timer
        m_syncRequestTimer = CPeriodic::NewL(CActive::EPriorityHigh);
        m_syncRequestTimer->Start( KSyncRequestTimeOut,0,TCallBack(&syncRequestTimerCb,this));
    }
}

SynchResourceHandleClient::~SynchResourceHandleClient()
{
    if (m_syncRequestTimer && m_syncRequestTimer->IsActive()) {
        m_syncRequestTimer->Cancel();
    }    
    delete m_syncRequestTimer;
}

PassRefPtr<SynchResourceHandleClient> SynchResourceHandleClient::create(TRequestStatus& aStatus, 
                                                                        const ResourceRequest& request, 
                                                                        Frame* frame)
{
    RefPtr<SynchResourceHandleClient> client(new SynchResourceHandleClient(aStatus, request, frame));
    if (!client->loader()) {
        return 0;
    }

    return client.release();
}

void SynchResourceHandleClient::didReceiveResponse(ResourceHandle* /*resource*/, const ResourceResponse& response)
{
    m_response = response;
}

void SynchResourceHandleClient::didReceiveData(ResourceHandle* /*resource*/, const char* data, int length, int /*lengthReceived*/)
{
    m_data.append(data, length);
}

void SynchResourceHandleClient::didFinishLoading(ResourceHandle* /*resource*/)
{
    // Decrement ref counter on handle, so connection can be torn down or cancelled
    m_resourceHandle = 0;
    
    finish(ResourceError());
}

void SynchResourceHandleClient::didFail(ResourceHandle* /*resource*/, const ResourceError& error)
{
    // Decrement ref counter on handle, so connection can be torn down or cancelled
    m_resourceHandle = 0;
    
    finish(error);
}

void SynchResourceHandleClient::cancel()
{
    cancel(ResourceError(String(), KErrCancel, String(), String()));
}

void SynchResourceHandleClient::cancel(const ResourceError& err)
{
    // Operation was cancelled
    // Need to cancel outstanding request, if not done so already
    if (resourceHandle()) {
        ResourceHandle* h = m_resourceHandle.get();
        m_resourceHandle = 0;
        h->cancel();
    }
    
    finish(err);
}

void SynchResourceHandleClient::finish(const ResourceError& error)
{
    m_error = error;
    
    //Cancel the synchronous Request timer
    if (m_syncRequestTimer->IsActive()) {
        m_syncRequestTimer->Cancel();
    }
    
    if (!m_finished) { // Avoid multiple execution
        if (m_loader) {
            m_loader->removeSubresourceLoader();
        }

        if (m_status) {       
            // Complete SynchRequest AO
            if (KRequestPending == m_status->Int()) {
                User::RequestComplete(m_status, error.errorCode());
            }        
        }
    }
    
    m_finished = true;
    //m_loader = 0;
}

void SynchResourceHandleClient::handleSyncRequestTimeOut()
{
    cancel(ResourceError(String(), KErrCancel, String(), String()));
}

// -----------------------------------------------------------------------------
// syncRequestTimerCb
// -----------------------------------------------------------------------------
int syncRequestTimerCb(void* ptr)
{
    ((SynchResourceHandleClient*)ptr)->handleSyncRequestTimeOut();
    return 0;
}

} // End namespace WebCore

