// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
//

// System includes
#include <e32std.h>
#include <http/framework/cheadercodec.h>

// Local includes
#include "CHeaders.h"

// Class signature
#include "CHeaderField.h"


CHeaderField* CHeaderField::NewL(RStringF aHeaderFieldName, CHeaders& aOwner)
	{
	return new(ELeave)CHeaderField(aHeaderFieldName, aOwner);
	}

CHeaderField* CHeaderField::New(RStringF aHeaderFieldName, CHeaders& aOwner)
    {
    return new CHeaderField(aHeaderFieldName, aOwner);
    }


CHeaderField* CHeaderField::NewL(RStringF aHeaderFieldName, CHeaders& aOwner, THTTPHdrVal aVal)
	{
	CHeaderField* me = new(ELeave)CHeaderField(aHeaderFieldName, aOwner);
	CleanupStack::PushL(me);
	CHeaderFieldPart* part = CHeaderFieldPart::NewL(aVal);
	CleanupStack::PushL(part);
	me->AddPartL(part);
	CleanupStack::Pop(2);
	return me;
	}


CHeaderField* CHeaderField::NewL(RStringF aHeaderFieldName, CHeaders& aOwner, const TDesC8& aRawData)
	{
	CHeaderField* me = new(ELeave)CHeaderField(aHeaderFieldName, aOwner);
	CleanupStack::PushL(me);
	me->ConstructL(aRawData);
	CleanupStack::Pop(me);
	return me;
	}


/** D'tor
*/

CHeaderField::~CHeaderField()
	{
	iName.Close();
	ClearParsedData();
	ClearRawData();
	iElementArray.Close();	
	iParameterArray.Close();
	}




TInt CHeaderField::NumPartsL()
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// Check the array exists first
	return iParts.Count();
	}


THeaderFieldPartIter CHeaderField::PartsL()
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// create an iterator initialised for this header
	return THeaderFieldPartIter(this);
	}

TInt CHeaderField::Part(TInt aIndex, CHeaderFieldPart*& aPart)
    {
    TInt err = KErrNone;
    // Check, is already in parsed format
    if (iDataParseState == EDataInRawForm)
        {
        // Convert, to the parsed format
        TRAP(err, ConvertToParsedFormatL());        
        }        

    if(err == KErrNone && aIndex < iParts.Count() && (aIndex >= 0))
        {
        aPart = iParts[aIndex];
        }
    return err;
    }


CHeaderFieldPart* CHeaderField::PartL(TInt aIndex)
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// Check the part is in range, if so return it
	if ((aIndex < NumPartsL()) && (aIndex >= 0))
		return iParts[aIndex];
	else
		User::Leave(KErrNotFound);
	
	return NULL;
	}

EXPORT_C
void CHeaderField::RawDataL(TPtrC8& aRawData)
	{
	ConvertToRawFormatL();
	aRawData.Set(*iRawData);
	}


/** Setters
*/

void CHeaderField::SetPartL(CHeaderFieldPart* aPart, TInt aIndex)
	{
	if (aIndex < iParts.Count())
		{
		CHeaderFieldPart* oldPart = iParts[aIndex];
		iParts[aIndex] = aPart;
		delete oldPart;
		}
	else
		User::LeaveIfError(iParts.Append(aPart));
	}

void CHeaderField::BeginRawDataL(TInt aChunkSize)
	{
	iRawChunkSize = aChunkSize;

	// clear out any existing Raw data, but don't reallocate if not needed
	if (!iRawData)
		iRawData = HBufC8::NewL(iRawChunkSize);
	else
		{
		TPtr8 rawPtr = iRawData->Des();
		rawPtr.Zero();
		}
	}


void CHeaderField::WriteRawDataL(const TDesC8& aData)
	{
    // Check to see if the buffer needs to grow
	TInt maxLength  = iRawData->Des().MaxLength();
	TInt reqdLength = iRawData->Length() + aData.Length();
	if (reqdLength > maxLength)
		{
		// Yes - allocate a new buffer of a larger size, and copy the contents
		// of the old one over. This may need more than one extra chunk. Check
		// for partial chunks - add an extra one if necessary.
		TInt numChunksReqd = (reqdLength - maxLength)/iRawChunkSize;
		 if ( (reqdLength - maxLength)%iRawChunkSize > 0)
			++numChunksReqd;
		HBufC8* largerBuffer = iRawData->ReAllocL(maxLength + iRawChunkSize*numChunksReqd);

		// Switch buffers. The old one was removed by ReAlloc.
		iRawData = largerBuffer;
		}

	// Can now append confidently
	iRawData->Des().Append(aData);
	}


void CHeaderField::WriteRawDataL(TChar aData)
	{
	// Check to see if the buffer descriptor needs to grow
	TInt maxLength  = iRawData->Des().MaxLength();
	TInt reqdLength = iRawData->Length() + 1;
	if (reqdLength > maxLength)
		{
		// Yes - allocate a new buffer of a larger size, and copy the contents
		// of the old one over. Since a single character is being added, one
		// chunk will be enough.
		HBufC8* largerBuffer = iRawData->ReAllocL(maxLength + iRawChunkSize);

		// Switch buffers. The old one was removed by ReAlloc.
		iRawData = largerBuffer;
		}

	// Can now append confidently
	iRawData->Des().Append(aData);
	}


void CHeaderField::CommitRawData()
	{
	// Change state and remove the parsed form of the header data
	iDataParseState = EDataInRawForm;
	ClearParsedData();
	}


void CHeaderField::AddPartL(CHeaderFieldPart* aPart)
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// Append the item
	User::LeaveIfError(iParts.Append(aPart));
	}

TInt CHeaderField::AddPart(CHeaderFieldPart* aPart)
    {
    TInt err = KErrNone;
    if (iDataParseState == EDataInRawForm)
        {
        TRAP(err, AddPartL(aPart));
        }
    if(err == KErrNone)
        {
        err = iParts.Append(aPart);
        }
    return err;
    }

void CHeaderField::InsertPartL(CHeaderFieldPart* aPart, TInt aIndex)
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// If the array isn't yet constructed, or is empty, then use 'AddPart'
	if (!NumPartsL()) 
		AddPartL(aPart);
	else
		User::LeaveIfError(iParts.Insert(aPart, aIndex));
	}


TInt CHeaderField::RemovePartL(CHeaderFieldPart* aPart)
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// Sanity check - if no parts, then the part can't be removed...
	if (iParts.Count() == 0)
		return KErrNotFound;

	// Find the part.  
	TInt idx = iParts.Find(aPart);

	// Now remove by index
	iParts.Remove(idx);
	iParts.Compress();
	delete aPart;
	return KErrNone;
	}


TInt CHeaderField::RemovePartL(TInt aIndex)
	{
	// Convert, if necessary, to the parsed format
	ConvertToParsedFormatL();

	// Sanity check - if no array exists or if the index supplied is out of the bounds
	// of the array, then the part can't be removed...
	if ((aIndex < 0) || (aIndex >= NumPartsL()))
		return KErrNotFound;

	// Note that the array doesn't actually delete the part itself. Also the array must be
	// compressed to close the 'gap' left by the deleted part
	CHeaderFieldPart* part = PartL(aIndex);
	iParts.Remove(aIndex);
	iParts.Compress();
	delete part;
	return KErrNone;
	}


/** Constructors and second-phase construction
*/

void CHeaderField::ConvertToRawFormatL()
	{
	if (iDataParseState != EDataParsedOK)
		return;
	RHeaderField rf(*this);
	// State change and removal of parsed data  is done when the Raw data is committed by the codec when it is encoded
	iCodec.EncodeHeaderL(rf);
	}

void CHeaderField::ConvertToParsedFormatL()
	{
	if (iDataParseState != EDataInRawForm)
		return;
	RHeaderField rf(*this);
	iCodec.DecodeHeaderL(rf);

	// If we haven't left, then it must have succeeded. Change state and remove the Raw data
	iDataParseState = EDataParsedOK;
	ClearRawData();
	}

TInt CHeaderField::ConvertToParsedFormat()
    {
    if (iDataParseState != EDataInRawForm)
        return KErrNone;

    RHeaderField rf(*this);
    TRAPD(err, iCodec.DecodeHeaderL(rf));
    if(err != KErrNone)
        return err;

    // If we haven't left, then it must have succeeded. Change state and remove the Raw data
    iDataParseState = EDataParsedOK;
    ClearRawData();

    return KErrNone;
    }

TInt CHeaderField::SplitValueAndParameterArray()
    {
    const TUint count = iParts.Count();
    CHeaderFieldPart* part = NULL;
    TInt ret = KErrNone;
    for(TUint i = 0; (i < count) && (ret == KErrNone); ++i)
        {
        part = iParts[i];
        THTTPHdrVal val = part->Value();        
        if(((val.Type() == THTTPHdrVal::KStrVal) && val.Str().DesC().Length() > 0) || 
                ((val.Type() == THTTPHdrVal::KStrFVal) && val.StrF().DesC().Length() > 0))
            {
            ret = iElementArray.Append(part);
            }
        else
            {
            const TUint numParams = part->NumParameters();
            if(numParams > 0)  // Push only if it has some parameters.
                {
                for(TUint j = 0; j < numParams && ret == KErrNone; ++j)
                    {
                    ret = iParameterArray.Append(part->Parameter(j));
                    }
                }
            else
                {
                ret = iElementArray.Append(part);
                }
            }        
        }
    return ret;
    }

CHeaderField::CHeaderField(RStringF aHeaderFieldName, CHeaders& aOwner)
	: iCodec(aOwner.Codec()), iDataParseState(ENoDataSupplied), iOwner(&aOwner) 
	{
	// Ensure that we maintain string references correctly with the header field name
	iName = aHeaderFieldName.Copy();
	iDataParseState = EDataParsedOK;
	}

void CHeaderField::ConstructL(const TDesC8& aRawData)
	{
	// Copy the data.  Set the state.
	iRawData = aRawData.AllocL();
	iDataParseState = EDataInRawForm;
	}

void CHeaderField::ClearParsedData()
	{
	// Clear and destroy parts array and its contents
	iParts.ResetAndDestroy();
	}

void CHeaderField::ClearRawData()
	{
	// Destroy any Raw data
	delete iRawData;
	iRawData = NULL;
	}


/** Implementation of CHeaderFieldPart class *************************************************
*/


/** Getter's
*/

CHeaderFieldParam* CHeaderFieldPart::Parameter(RStringF aParamName) const
	{
	// Iterate the parameter list looking for a name match
	THeaderFieldParamIter it(this);
	CHeaderFieldParam* retVal = NULL;
	const CHeaderFieldParam* nextParam = NULL;
	TBool done = EFalse;
	while (!done && !it.AtEnd())
		{
		nextParam = it();
		if (nextParam && (nextParam->Name() == aParamName))
			{
			retVal = CONST_CAST(CHeaderFieldParam*, nextParam);
			done = ETrue;
			}
		++it;
		}
	return retVal;
	}



/** Setter's
*/


void CHeaderFieldPart::SetValue(THTTPHdrVal aVal)
	{
	// We must check for an existing string value since it will have to be closed
	if (iValue.Type() == THTTPHdrVal::KStrFVal)
		iValue.StrF().Close();
	if (iValue.Type() == THTTPHdrVal::KStrVal)
		iValue.Str().Close();
	iValue = aVal.Copy();
	}



TInt CHeaderFieldPart::RemoveParam(CHeaderFieldParam* aParam)
	{
	// Sanity check - if no array exists, then the part can't be removed...
	if (iParams.Count() == 0)
		return KErrNotFound;

	// Find the param. 
	TInt idx = iParams.Find(aParam);

	// Now remove by index. Note that the array doesn't actually delete the param itself. Also
	// the array must be compressed to close the 'gap' left by the deleted param
	if (idx != KErrNotFound)
		{
		iParams.Remove(idx);
		iParams.Compress();
		delete aParam;
		idx = KErrNone;
		}
	return idx;
	}


