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


// INCLUDE FILES
#include "config.h"

#include <e32std.h>
#include <e32base.h>
#include "HttpPostDataSupplier.h"
#include "PostDataItem.h"
#include "FormData.h"
#include "brctl.h"

using namespace WebCore;

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES

// CONSTANTS
// The post is transmitted in parts. A buffer is allocated of max size to
// contain the next part to be transmitted. 
const TInt KPartMaxSize = 24576;

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ===============================


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


void HttpPostDataSupplier::initL(FormData* formData)
{
    m_firstPart = true;
    m_lastPart = false;
    m_dataPart = NULL;
    m_totalSize = 0;
    m_postItemIndex = 0;
    m_refCount = 0;

    PostDataItem* postDataItem = NULL;
    //iterate thru the post items and create CPostDataItem instance for each data
    size_t n = formData->elements().size();
    for (size_t index = 0; index < n; ++index) {
        const FormDataElement& formDataElement = formData->elements()[index];
        if (formDataElement.m_type == FormDataElement::data) {
            postDataItem = new (ELeave) PostDataItem;
            // No CleanupStack support for a class that is not based on CBase.
            m_postData.append(postDataItem);
            postDataItem->initL(&formDataElement);
        }
        else {
            ASSERT(formDataElement.m_type == FormDataElement::encodedFile);
            // if a file read failed, just ignore the form element and instead
            // post rest of the content
            postDataItem = new(ELeave) FileDataItem;
            m_postData.append(postDataItem);
            TRAPD(error, postDataItem->initL(&formDataElement));
            if (error != KErrNone) {
                m_postData.removeLast();
                delete postDataItem;
                if(error == KErrNoMemory) {
                    User::Leave(error);
                }
                continue;
            }
        }
        // get the size of the data and add to the total size 
        m_totalSize += postDataItem->pendingContentSize();
    }
}


// -----------------------------------------------------------------------------
// HttpPostDataSupplier::HttpPostDataSupplier
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
HttpPostDataSupplier::HttpPostDataSupplier(RHTTPTransaction* transaction, CBrCtl* brctl) 
:m_transaction(transaction), m_brctl(brctl)
{
}


HttpPostDataSupplier::~HttpPostDataSupplier()
{
    deleteAllValues(m_postData);
}

// -----------------------------------------------------------------------------
// HttpPostDataSupplier::ReleaseData
// 
// -----------------------------------------------------------------------------
//
void HttpPostDataSupplier::ReleaseData()
{
    delete m_dataPart;
    m_dataPart = NULL;
    if(!m_lastPart) {
        TRAP_IGNORE(m_transaction->NotifyNewRequestBodyPartL());
    }
}

// -----------------------------------------------------------------------------
// HttpPostDataSupplier::OverallDataSize
// 
// -----------------------------------------------------------------------------
//
TInt HttpPostDataSupplier::OverallDataSize() 
{
    return m_totalSize; 
}

// -----------------------------------------------------------------------------
// HttpPostDataSupplier::OverallDataSize
// 
// -----------------------------------------------------------------------------
//
TInt HttpPostDataSupplier::Reset() 
{
    m_firstPart = true;
    m_postItemIndex = 0;
    m_lastPart = false;
    //iterate thru the post items and create CPostDataItem instance for each data
    size_t n = m_postData.size();
    for (int index  = 0 ; index < m_postData.size(); index++) {
        PostDataItem* postDataItem = m_postData[index];     
        postDataItem->reset();
    }
    delete m_dataPart;
    m_dataPart = NULL;
    return KErrNone; 
}

// -----------------------------------------------------------------------------
// HttpPostDataSupplier::getNextDataPartL
// Accessor function to the request body. Return the body in one chunk.
// -----------------------------------------------------------------------------
//
void HttpPostDataSupplier::getNextDataPartL(TPtrC8& aDataPart)
{
    if(m_totalSize > KPartMaxSize && m_firstPart /*&& iBrCtlLoadEventObserver*/) {
        m_brctl->HandleBrowserLoadEventL( 
            TBrCtlDefs::EEventUploadStart, m_totalSize / 1000, 0) ;            
        m_firstPart = false;
    }
    // release previous part if it is still valid
    delete m_dataPart;
    m_dataPart = NULL;
    int partSize = 0;
    int partPostItems = m_postItemIndex;
    // find out the amount of memory that needs to be allocated for this part
    while (partPostItems < m_postData.size()) {
        PostDataItem* postDataItem = m_postData[partPostItems];
        if((partSize += postDataItem->pendingContentSize()) > KPartMaxSize ) {
            partSize = KPartMaxSize;
            break;
        }
        partPostItems++;
    }    
    // read the content of the items 
    m_dataPart = HBufC8::NewL(partSize);
    for (int index = m_postItemIndex; index < m_postData.size() && index <= partPostItems; index++) {
        PostDataItem* postDataItem = m_postData[index];
        HBufC8* itemContent = postDataItem->dataL(partSize - m_dataPart->Length());
        m_dataPart->Des().Append( *itemContent );
        delete itemContent;
    } 
    // if all the items have been read 
    if(partPostItems == m_postData.size()) {
        m_lastPart = true;
    }
    else {
    // if the last part has more data to be posted , set m_postItemIndex to that index else advance the index
        if(m_postData[partPostItems]->pendingContentSize()) {
            m_postItemIndex = partPostItems;
        }
        // advance to the next item
        else {
            m_postItemIndex = partPostItems + 1;    
        }
    }    
    aDataPart.Set(m_dataPart->Des());
    if(m_totalSize > KPartMaxSize) {
        if(!m_lastPart) {
            m_brctl->HandleBrowserLoadEventL( 
                TBrCtlDefs::EEventUploadIncrement, aDataPart.Length()/1000, 0) ;            
        }
        else {
            m_brctl->HandleBrowserLoadEventL( 
                TBrCtlDefs::EEventUploadFinished, aDataPart.Length()/1000, 0) ;            
        }
    }
}

// -----------------------------------------------------------------------------
// HttpPostDataSupplier::GetNextDataPart
// Accessor function to the request body. Return the body in one chunk.
// -----------------------------------------------------------------------------
//
TBool HttpPostDataSupplier::GetNextDataPart(TPtrC8& aDataPart)
{
    if(!m_lastPart) {
        TRAP_IGNORE( getNextDataPartL(aDataPart));
        // TODO:how should the error be handled ????, should the transaction be cancelled
    }
    else {
        aDataPart.Set(KNullDesC8);
    }
    return m_lastPart;
}

//  End of File
