/*
* Copyright (c) 2007 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: This file implements class CIpsPlgMruList.
*
*/



#include "emailtrace.h"
#include "ipsplgheaders.h"

const TUint32 KMruListMailboxMask = 0x0000FFFF;
const TUint32 KMruListMailboxPartKey = 0x0;
const TUint32 KMruListValueMask = 0xFFFF0000;
const TUint32 KMruListIncorrectMBoxKey = 0xFFFFFFFF;
const TUint32 KMruListKeyBaseValue = 0x00010000;
// what are actual size in Freestyle framework?
const TInt KMruListPrefDesSize = 40;
const TInt KMruListMaxDesSize = 200;
const TInt KMruListArrayInitSize = 8;

// <cmail>
const TInt KIpsSmtpOperationCharLessThan = '<';
const TInt KIpsSmtpOperationCharMoreThan = '>';
// </cmail>

// ---------------------------------------------------------------------------
// CIpsPlgMruList::NewL( )
// ---------------------------------------------------------------------------
//
CIpsPlgMruList* CIpsPlgMruList::NewL( )
    {
    FUNC_LOG;
    CIpsPlgMruList* self = new( ELeave ) CIpsPlgMruList( );
    CleanupStack::PushL( self );
    self->ConstructL( );
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::~CIpsPlgMruList()
// ---------------------------------------------------------------------------
//
CIpsPlgMruList::~CIpsPlgMruList()
    {
    FUNC_LOG;
    delete iRepository;
    iHbufArray.ResetAndDestroy();
    iHbufArray.Close();
    iKeys.Reset();
    iKeys.Close();
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::CIpsPlgMruList()
// ---------------------------------------------------------------------------
//
CIpsPlgMruList::CIpsPlgMruList( )
    : iRepository( NULL )
    {
    FUNC_LOG;

    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::ConstructL( )
// ---------------------------------------------------------------------------
//
void CIpsPlgMruList::ConstructL( )
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::GetMruListL
// ---------------------------------------------------------------------------
//
CDesCArrayFlat* CIpsPlgMruList::GetMruListL( const TFSMailMsgId& aMailbox )
    {
    FUNC_LOG;
    // create iRepository if not yet created
    CreateRepositoryL( iRepository );

    TBool returnEmpty( EFalse );

    // Do not cache anything, there are multiple plugin instances
    iKeys.Reset();
    TUint32 mboxKey;
    if ( KErrNone != GetMailboxKeyFromRepositoryL( aMailbox.Id(), mboxKey, iKeys ) )
        {
        returnEmpty = ETrue;
        }
    iKeys.Reset();

    // get correct mailbox key and fill corresponding name and address keys
    // in keys array
    if ( !returnEmpty )
        {
        if ( KErrNone != iRepository->FindL( mboxKey, KMruListValueMask, iKeys ) )
            {
            returnEmpty = ETrue;
            }
        }

    // if we have found correct key find all keys that
    // maches pattern 0xMKEY**** key array should contain
    // at least 3 items, and size shoud be odd
    // or array is empty or corrupted
    TInt keyCount( iKeys.Count() );
    if ( !returnEmpty && keyCount >= 3 && ( keyCount%2 ) == 1 )
        {
        iHbufArray.ResetAndDestroy();
        iKeys.SortUnsigned();
        TInt i(0);

        // first item in array should be mbox key
        if ( iKeys[0] == mboxKey )
            {
            // index to first name key
            i = 1;
            // store name and address in one loop cycle
            while ( !returnEmpty && i <= ( keyCount-2 ) )
                {
                TUint key = iKeys[i];
                // name is always in odd and address in even key
                if ( (key%2==1) && (iKeys[i+1]==key+1) )
                    {
                    // adding name and email to hbuf array
                    if ( StoreToArrayL(iKeys[i]) != KErrNone )
                        {
                        // something went wrong in name store
                        // just break the loop and return empty
                        HandleRepositoryError( mboxKey, KErrCorrupt );
                        returnEmpty = ETrue;
                        }
                    if ( StoreToArrayL(iKeys[i+1]) != KErrNone )
                        {
                        // store is corrupted
                        HandleRepositoryError( mboxKey, KErrCorrupt );
                        returnEmpty = ETrue;
                        }
                    }
                i = i+2;
                }
            }
        else
            {
            // first item was not mailbox key
            // centrep data is corrupted
            HandleRepositoryError( mboxKey, KErrCorrupt );
            returnEmpty = ETrue;
            }
        }
    else if ( !returnEmpty )
        {
        // mailbox found but there is no data or centrep is corrupted,
        // return empty
        HandleRepositoryError( mboxKey, KErrCorrupt );
        returnEmpty = ETrue;
        }

    // create and return des array
    CDesCArrayFlat* retArr = NULL;

    if ( !returnEmpty )
        {
        retArr = new ( ELeave ) CDesCArrayFlat( KMruListArrayInitSize );
        CleanupStack::PushL( retArr );

        TInt count( iHbufArray.Count() );
        // fill descriptor array
        for ( TInt i(0); i < count; i++ )
            {
            HBufC* app = iHbufArray[i];
            retArr->AppendL(*app);
            }
        CleanupStack::Pop( retArr );
        }
    else
        {
        iHbufArray.ResetAndDestroy();
        retArr = new ( ELeave ) CDesCArrayFlat( KMruListArrayInitSize );
        }

    return retArr;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::SetMruListL
// ---------------------------------------------------------------------------
//
void CIpsPlgMruList::SetMruListL( const TFSMailMsgId& aMailBoxId,
    MDesCArray*  aNewMruList )
    {
    FUNC_LOG;
    // create iRepository if not yet created
    CreateRepositoryL( iRepository );

    TUint32 mboxKey = KMruListIncorrectMBoxKey;
    iKeys.Reset();
    // get mailbox keys to keys array and
    // check if mailboxid is already stored in some key
    TInt error = GetMailboxKeyFromRepositoryL(
        aMailBoxId.Id(), mboxKey, iKeys );

    if ( KErrNone == error )
        {
        // mailbox found
        TUint32 errorKey;
        error = iRepository->Delete( mboxKey, KMruListValueMask, errorKey );
        HandleRepositoryError( mboxKey, error );
        }
    else
        {
        // mailbox not found, create new key
        mboxKey = GetUnusedMboxKey( iKeys );
        }

    // set new values to central repository
    // create mailboxid key
    error = iRepository->Create( mboxKey, TInt( aMailBoxId.Id() ) );
    // handle error
    TInt count( aNewMruList->MdcaCount() );
    // <cmail>
    for ( TInt i(0); error == KErrNone && i < count; i += 2 )
        {
        if( i + 1 < count )
            {
            TPtrC displayName  = aNewMruList->MdcaPoint(i);
            TPtrC emailAddress = aNewMruList->MdcaPoint(i + 1);

            // Need to locate angle brackets from end of the data, if there
            // is display name with special characters
            TInt start( emailAddress.LocateReverse( KIpsSmtpOperationCharLessThan ) ); 
            TInt end( emailAddress.LocateReverse( KIpsSmtpOperationCharMoreThan ) );
            
            if ( KErrNotFound != start && KErrNotFound != end )
                {
                start += 1;
                emailAddress.Set( aNewMruList->MdcaPoint(i + 1).Mid( start, ( end - start ) ) );
                }

            // check validity of email before saving diplay name and address
            if( IpsSetUtils::IsValidEmailAddressL(emailAddress) )
                {
                // set values to ascending keys
                mboxKey++;

                if ( aNewMruList->MdcaPoint(i).Length() <= KMruListMaxDesSize )
                    {
                    error = iRepository->Create( mboxKey, aNewMruList->MdcaPoint(i) );
                    // handle errors
                    }
                else
                    {
                    TDesC des = aNewMruList->MdcaPoint(i);
                    error = iRepository->Create(
                        mboxKey, des.Mid(0,KMruListMaxDesSize) );
                    }
                
                mboxKey++;

                if( error == KErrNone )
                    {
                    if ( aNewMruList->MdcaPoint(i + 1).Length() <= KMruListMaxDesSize )
                        {
                        error = iRepository->Create( mboxKey, aNewMruList->MdcaPoint(i + 1) );
                        }
                    else
                        {
                        TDesC des = aNewMruList->MdcaPoint(i + 1);
                        error = iRepository->Create(
                            mboxKey, des.Mid(0,KMruListMaxDesSize) );
                        }
                    }
                }
            }
        }
    // </cmail>

    if ( KErrNone != error )
        {
        HandleRepositoryError( mboxKey, error );
        }
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::ClearDataL
// ---------------------------------------------------------------------------
//
void CIpsPlgMruList::ClearDataL( const TFSMailMsgId& aMailBoxId )
	{
    FUNC_LOG;
	CreateRepositoryL( iRepository );

    TUint32 mboxKey = KMruListIncorrectMBoxKey;
    iKeys.Reset();
    // get mailbox keys to keys array and
    // check if mailboxid is already sotred in some key
    TInt error = GetMailboxKeyFromRepositoryL(
        aMailBoxId.Id(), mboxKey, iKeys );

    // we have found correct mboxKey
    if ( KErrNone == error )
        {
        TUint32 errorKey;
        error = iRepository->Delete( mboxKey, KMruListValueMask, errorKey );
        HandleRepositoryError( mboxKey, error );
        }
	}

// ---------------------------------------------------------------------------
// CIpsPlgMruList::StoreToArrayL
// ---------------------------------------------------------------------------
//
TInt CIpsPlgMruList::StoreToArrayL( TUint32 aKey )
    {
    FUNC_LOG;
    HBufC* hbuf = HBufC::NewLC( KMruListPrefDesSize );

    TPtr pdes = hbuf->Des();
    TInt actualSize(0);

    TInt error = iRepository->Get( aKey, pdes, actualSize );

    // the case descriptor is larger than pref size
    if ( error == KErrOverflow )
        {
        if ( actualSize > KMruListMaxDesSize )
            {
            // centrep contains too large descriptors
            // impossible if centrep is edited only by this code,
            // set corrupted
            error = KErrCorrupt;
            }
        else
            {
            CleanupStack::PopAndDestroy( hbuf );
            hbuf = HBufC::NewLC( actualSize );
            TPtr pdes2 = hbuf->Des();
            error = iRepository->Get( aKey, pdes2 );
            }
        }

    // add or delete
    if ( KErrNone == error )
        {
        iHbufArray.AppendL( hbuf );
        CleanupStack::Pop( hbuf );
        }
    else
        {
        CleanupStack::PopAndDestroy( hbuf );
        }
    return error;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::GetMailboxKeyFromRepositoryL
// ---------------------------------------------------------------------------
//
TInt CIpsPlgMruList::GetMailboxKeyFromRepositoryL(
    TInt aMboxId,
    TUint32& aMboxKey,
    RArray<TUint32>& aArray )
    {
    FUNC_LOG;
    // keys is found from centrep with partkey 0x0 and mask 0x0000FFFF
    // result is array of keys that maches to pattern 0x****0000
    TInt error = iRepository->FindL(
        KMruListMailboxPartKey, KMruListMailboxMask, aArray );

    // No need to handle other errors than not found,
    // this function should only return KErrNone or KErrNotFound
    TInt tempId;
    error = KErrNotFound;

    //find correct mailbox key from array:
    for ( TInt i = 0; error == KErrNotFound && i < aArray.Count(); i++ )
        {
        if ( iRepository->Get( aArray[i], tempId ) == KErrNone
            && tempId == aMboxId )
            {
            aMboxKey = aArray[i];
            error = KErrNone;
            }
        }

    return error;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::GetUnusedMboxKey
// ---------------------------------------------------------------------------
//
TUint32 CIpsPlgMruList::GetUnusedMboxKey( RArray<TUint32>& aKeys )
    {
    FUNC_LOG;
    // Warning: this function is complex and may contain bugs!!!

    TUint32 newKey = aKeys.Count()*KMruListKeyBaseValue;

    if ( aKeys.Count() > 1 )
        {
        // this loop finds smallest unused key from sorted key array,
        // if array[i] == i*basekey at every i when i == 0...count-1
        // then newKey is not changed
        aKeys.SortUnsigned();
        TUint32 tempKey = newKey;
        for ( TInt i = 0; tempKey == newKey && i < ( aKeys.Count() - 1); i++ )
            {
            if ( aKeys[i] + KMruListKeyBaseValue != aKeys[i+1] )
                {
                tempKey = aKeys[i] + KMruListKeyBaseValue;
                }
            }
        newKey = tempKey;
        }
    else if ( aKeys.Count() == 1 && aKeys[0] == KMruListKeyBaseValue )
        {
        newKey = 0;
        }


    return newKey;
    }

// ---------------------------------------------------------------------------
// CIpsPlgMruList::HandleRepositoryError
// ---------------------------------------------------------------------------
//
void CIpsPlgMruList::HandleRepositoryError( TUint32 aMboxKey, TInt aError )
    {
    FUNC_LOG;
    TBool doClear = EFalse;
    switch ( aError )
        {
            // log these error cases
        case KErrNone:
            // no error
            break;
        case KErrNotFound:
            // no serious error
            break;
        case KErrPermissionDenied:
            // this should be panic because application has no
            // privileges for mru repository
            break;
        case KErrAbort:
            // transaction has previously failed
            // clear mailbox data if it helps...
            doClear = ETrue;
            break;
        case KErrArgument:
            // error in argument, for ex. trying to put integer to descriptor
            // key, solution clear all keys matching aMailbox
            doClear = ETrue;
            break;
        case KErrCorrupt:
            // just clear all keys matching aMailbox
            doClear = ETrue;
            break;
        case KErrAlreadyExists:
            // should handle elsewhere, if ends here clear data
            doClear = ETrue;
            break;
        default:
            // something wrong , clear data
            doClear = ETrue;
            break;
        }

    if ( doClear )
        {
        // don't care about errors...
        TUint32 errorKey;
        TInt error = iRepository->Delete( aMboxKey, KMruListValueMask, errorKey );
        }
    }


// End of file


