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

// INCLUDE FILES
#include "HttpCacheEntry.h"
#include "HttpCacheEvictionHandler.h"
#include "HttpCacheStreamHandler.h"
#include "HttpCacheUtil.h"
#include <s32file.h>

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES

// CONSTANTS
const TInt CHttpCacheEntry::iOffset = _FOFF( CHttpCacheEntry, iSqlQueLink );
const TInt KBufferSize32k = 32768;
const TInt KBufferGranularity4k = 4096;

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

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

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

// -----------------------------------------------------------------------------
// CHttpCacheEntry::CHttpCacheEntry
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CHttpCacheEntry::CHttpCacheEntry( CHttpCacheEvictionHandler& aEvictionHandler )
    : iState( ECacheUninitialized ),
      iEvictionHandler( &aEvictionHandler )
    {
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::ConstructL( const TDesC8& aUrl )
    {
    iUrl = aUrl.AllocL();
    iHeaderBuffer = KNullDesC8().AllocL();
    iCacheBuffer = CSegmentedHeapBuffer::NewL( KBufferSize32k, KBufferGranularity4k );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CHttpCacheEntry* CHttpCacheEntry::NewL(
    const TDesC8& aUrl,
    CHttpCacheEvictionHandler& aEvictionHandler )
    {
    CHttpCacheEntry* self = new( ELeave ) CHttpCacheEntry( aEvictionHandler );

    CleanupStack::PushL( self );
    self->ConstructL( aUrl );
    CleanupStack::Pop();

    return self;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CHttpCacheEntry* CHttpCacheEntry::NewLC(
    const TDesC8& aUrl,
    CHttpCacheEvictionHandler& aEvictionHandler )
    {
    CHttpCacheEntry* self = CHttpCacheEntry::NewL( aUrl, aEvictionHandler );
    CleanupStack::PushL( self );
    return self;
    }

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
//
CHttpCacheEntry::~CHttpCacheEntry()
    {
#ifdef __CACHELOG__
    HttpCacheUtil::WriteFormatLog(0, _L("Deleting CHttpCacheEntry %08x"), this);
#endif
    // Clean up eviction handler
    if ( iEvictionCandidate && iEvictionHandler )
        {
#ifdef __CACHELOG__
        HttpCacheUtil::WriteLog(0, _L("Removing from eviction candidate list"));
#endif
        iEvictionHandler->Remove( *this );
        }

    if( iDeleteObserver )
        {
#ifdef __CACHELOG__
        HttpCacheUtil::WriteFormatLog(0, _L("Notifying delete observer %08x"), this);
#endif
        iDeleteObserver->EntryDeleted(this);
        }
    
    // Close files, this will commit changes
    iBodyFile.Close();

    // Clean up our memory
    delete iUrl;
    delete iFileName;
    delete iHeaderBuffer;
    delete iCacheBuffer;
    delete iWriteHelper;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::SetState
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::SetState( TCacheEntryState aState )
    {
    // Add entry to the eviction table once it gets completed
    if ( aState == ECacheComplete && !iEvictionCandidate )
        {
        // don't add it twice
        iEvictionHandler->Insert( *this );
        iEvictionCandidate = ETrue;
        }

    iState = aState;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::SetFileNameL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::SetFileNameL( const TFileName& aFileName )
    {
    delete iFileName;
    iFileName = NULL;

    iFileName = aFileName.AllocL(); 
    }


// -----------------------------------------------------------------------------
// CHttpCacheEntry::Accessed
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::Accessed()
    {
    TTime now;
    now.HomeTime();
    iLastAccessed = now.Int64();

    iRef++;

    if ( iEvictionCandidate )
        {
        iEvictionHandler->Accessed( *this );
        }

#ifdef __CACHELOG__
    _LIT( KAccessFormat, "entry accessed: %d" );
    TBuf<100> buf;
    buf.Format( KAccessFormat, iRef );
    HttpCacheUtil::WriteUrlToLog( 0, buf, iUrl->Des() );
#endif // __CACHELOG__

    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::SetBodySize
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::SetBodySize( TUint aBodySize )
    {
    if ( iBodySize && !aBodySize )
        {
        // Remove from the eviction table, this is no longer a candidate
        if ( iEvictionCandidate )
            {
            iEvictionHandler->Remove( *this );
            iEvictionCandidate = EFalse;
            }
        }
    iBodySize = aBodySize;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::SetProtected
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::SetProtected()
    {
    iProtected = ETrue;
    iRef = 50;

#ifdef __CACHELOG__
    HttpCacheUtil::WriteLog( 0, _L( "CHttpCacheEntry::SetProtected - protected item" ) );
#endif
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::Internalize
//
// -----------------------------------------------------------------------------
//
TInt CHttpCacheEntry::Internalize( RReadStream& aReadStream, const TDesC& aDirectory )
    {
    TRAPD( err, InternalizeL( aReadStream, aDirectory ) );
    return err;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::InternalizeL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::InternalizeL( RReadStream& aReadStream, const TDesC& aDirectory )
    {
    // url length
    TInt len;
    len = aReadStream.ReadInt32L();
    delete iUrl;
    iUrl=NULL;
    iUrl = HBufC8::NewL( len );
    TPtr8 ptr8( iUrl->Des() );
    // url
    aReadStream.ReadL( ptr8, len );

    // calculate full path and filename length
    // aDirectory/ + "x/xxxxxxxx" : note aDirectory has trailing '/'
    len = aDirectory.Length() + KSubdirNameLength + KFilenameLength;
    HBufC* filename = HBufC::NewLC( len );
    TPtr ptr( filename->Des() );

    // Read max char length of filename.
    // NOTE: The filename and filename length is calculated by the code in
    // HttpCacheUtil::GenerateNameLC. The sub directory is the same as the
    // last char of the filename, e.g. ..\A\0123DCBA
    TBuf<KFilenameLength> uniqueFilename;
    aReadStream.ReadL( uniqueFilename , KFilenameLength );
    TPtrC uniqueSubDir = uniqueFilename.Right(1);

    // assemble path and filename
    ptr.Format(_L("%S%S\\%S"), &aDirectory, &uniqueSubDir, &uniqueFilename);
    //
    SetFileNameL( filename->Des() );
    //
    CleanupStack::PopAndDestroy(); // filename
    // la
    TReal64 la;
    la = aReadStream.ReadReal64L();
    iLastAccessed = la;
    // ref
    iRef = aReadStream.ReadUint32L();
    // size
    iBodySize = aReadStream.ReadUint32L( );
    // size
    iHeaderSize = aReadStream.ReadUint32L( );
    // protected
    iProtected = aReadStream.ReadInt32L();
    // header data
    delete iHeaderBuffer;
    iHeaderBuffer = NULL;
    len = aReadStream.ReadInt32L();
    iHeaderBuffer = HBufC8::NewL(len);
    TPtr8 header_ptr( iHeaderBuffer->Des() );
    aReadStream.ReadL( header_ptr, len );
    //
    SetState( ECacheComplete );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::Externalize
//
// -----------------------------------------------------------------------------
//
TInt CHttpCacheEntry::Externalize( RWriteStream& aWriteStream, const TDesC& aDirectory )
    {
    TRAPD( err, ExternalizeL( aWriteStream, aDirectory ) );
    return err;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::Externalize
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::ExternalizeL( RWriteStream& aWriteStream, const TDesC& aDirectory )
    {
    // check directory matches filename
    ASSERT(aDirectory.CompareF(iFileName->Left(aDirectory.Length())) == 0);

    // url length
    aWriteStream.WriteInt32L( iUrl->Length() );
    // url
    aWriteStream.WriteL( iUrl->Des() );
    // filename
    // know that filenames are 8 chars and no extension. Can reconstruct on
    // import from directory and last char. See HttpCacheUtil::GenerateNameLC.
    aWriteStream.WriteL( iFileName->Des().Right( KFilenameLength ) );
    // la
    aWriteStream.WriteReal64L( iLastAccessed );
    // ref
    aWriteStream.WriteUint32L( iRef );
    // size
    aWriteStream.WriteUint32L( iBodySize );
    // size
    aWriteStream.WriteUint32L( iHeaderSize );
    // protected
    aWriteStream.WriteInt32L( iProtected );
    // header data length
    aWriteStream.WriteInt32L( iHeaderBuffer->Length() );
    // header data
    aWriteStream.WriteL( iHeaderBuffer->Des() );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::Accessed
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::Accessed( TInt64 aLastAccessed, TUint16 aRef )
    {
    iLastAccessed = aLastAccessed;
    iRef = aRef;
    if ( iEvictionCandidate )
        {
        iEvictionHandler->Accessed( *this );
        }

#ifdef __CACHELOG__
    _LIT( KAccessFormat, "entry accessed: %d" );
    TBuf<100> buf;
    buf.Format( KAccessFormat, iRef );
    HttpCacheUtil::WriteUrlToLog( 0, buf, iUrl->Des() );
#endif // __CACHELOG__

    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::SetCacheBufferL
// NOTE: Cache buffer is created on:
// 1. Normal content entrypoint into CacheManager
//    CacheManager::ReceivedResponseHeadersL -> CacheHandler::ReceivedResponseHeadersL ->
//    CacheHandler::HandleResponseOkL (calls method - SetCacheBuffer, needed to
//    accumulate body content on multiple CacheHandler::ReceivedBodyDataL calls)
// 2. Multipart content entrypoint into CacheManager
//    CacheManager::SaveL -> CacheHandler::SaveL -> CacheHandler::SaveBuffer ->
//    CacheStreamHandler::SaveBodyData (calls this method - SetCacheBufferL, needed
//    because cacheBuffer=null and single call made, no accumulation of body data)
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::SetCacheBufferL( TInt aCacheBufferSize )
    {
    // Delete cacheBuffer and null, a way to zero buffer and handle if NewL leave
    delete iCacheBuffer;
    iCacheBuffer = NULL;

    if ( aCacheBufferSize > 0 )
        {
        iCacheBuffer = CSegmentedHeapBuffer::NewL( aCacheBufferSize, KBufferGranularity4k );
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::CreateHeaderBufferL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::CreateHeaderBufferL( TInt aHeaderBufferSize )
    {
    // Delete cacheBuffer and null, a way to zero buffer and handle if NewL leave
    delete iHeaderBuffer;
    iHeaderBuffer = NULL;

    if ( aHeaderBufferSize > 0 )
        {
        iHeaderBuffer = HBufC8::NewL( aHeaderBufferSize );
        }
    SetHeaderSize( aHeaderBufferSize );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::CreateHeaderBufferL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::CreateHeaderBufferL( const TDesC8& aHeaderData )
    {
    // Delete cacheBuffer and null, a way to zero buffer and handle if NewL leave
    delete iHeaderBuffer;
    iHeaderBuffer = NULL;

    TInt aHeaderBufferSize = aHeaderData.Length();
    if ( aHeaderBufferSize > 0 )
        {
        iHeaderBuffer = aHeaderData.AllocL();
        }
    SetHeaderSize( aHeaderBufferSize );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::BodyData
//
// -----------------------------------------------------------------------------
//
CSegmentedHeapBuffer& CHttpCacheEntry::BodyData()
    {
    return *iCacheBuffer;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::BodyFile
//
// -----------------------------------------------------------------------------
//
RFile& CHttpCacheEntry::BodyFile()
    {
    return iBodyFile;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::HeaderData
//
// -----------------------------------------------------------------------------
//
TDesC8& CHttpCacheEntry::HeaderData()
    {
    return *iHeaderBuffer;
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::BodyWriteInProgress
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::BodyWriteInProgress()
    {
    SetBodyDataPartiallyWritten( ETrue );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::BodyWriteComplete
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::BodyWriteComplete()
    {
    iCacheBuffer->Reset();
    SetBodyDataPartiallyWritten( EFalse );
    SetBodyDataCached( EFalse );
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::WriteBodyDataAsync
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::WriteBodyDataAsync(TRequestStatus& aStatus)
    {
    delete iWriteHelper;
    iWriteHelper = NULL;
    TRAPD(err, iWriteHelper = CHttpCacheEntryAsyncWriteHelper::NewL( this, aStatus ) );
    if(err != KErrNone)
        {
        TRequestStatus *stat = &aStatus;
        User::RequestComplete(stat, err);
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheEntry::CancelBodyWrite
//
// -----------------------------------------------------------------------------
//
void CHttpCacheEntry::CancelBodyWrite()
    {
    if ( BodyDataPartiallyWritten() && iWriteHelper )
        {
        iWriteHelper->Cancel();
        }
    }

void CHttpCacheEntry::SetDeleteObserver(MHttpCacheEntryDeleteObserver* aObserver)
    {
    iDeleteObserver = aObserver;
    }

void CHttpCacheEntry::ClearDeleteObserver()
    {
    iDeleteObserver = NULL;
    }
//  End of File
