/*
* 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 "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:   Email interface notification systems implementation.
*
*/



//<cmail>
#include "msgstore.h"
#include "msgstorefolder.h"
//</cmail>

#include "baseplugin.h"
#include "baseplugincommonutils.h"


/**
 *
 */
EXPORT_C void CBasePlugin::SubscribeMailboxEventsL(
    const TFSMailMsgId& aMailboxId,
    MFSMailEventObserver& aObserver )

	{
    CMailboxInfo& mailBox = GetMailboxInfoL( aMailboxId.Id() );

    if ( KErrNotFound == mailBox.iObservers.Find( &aObserver ) )
        {
        mailBox.iObservers.AppendL( &aObserver );
        }
	}


/**
 *
 */
EXPORT_C void CBasePlugin::UnsubscribeMailboxEvents(
    const TFSMailMsgId& aMailboxId,
    MFSMailEventObserver& aObserver )

	{
	TRAP_IGNORE( UnsubscribeMailboxEventsL( aMailboxId, aObserver ) );
	}


/**
 *
 */
EXPORT_C void CBasePlugin::AddObserverL(
    MFSMailEventObserver& aObserver )
    {
    
    if ( KErrNotFound == iObservers.Find( &aObserver ) )
        {
        iObservers.AppendL( &aObserver );
        }

    if ( iObservers.Count() == 1 )
        {
        iMsgStore->AddObserverL( this );
        }
    }


/**
 *
 */
EXPORT_C void CBasePlugin::RemoveObserver(
    MFSMailEventObserver& aObserver )
    {

    TInt idx = iObservers.Find( &aObserver );
    
    if ( KErrNotFound != idx )
        {
        iObservers.Remove( idx );
        }
        
    if ( iObservers.Count() == 0 )
        {
        TRAP_IGNORE( iMsgStore->RemoveObserverL( this ) );
        }
    }


/**
 *
 */
EXPORT_C void CBasePlugin::UnregisterRequestObserver(
    TInt /*aRequestId*/ )
    {
    
    }


#pragma mark -
#pragma mark --- MSGSTORE OBSERVERS ---


/**
 *
 */
EXPORT_C void CBasePlugin::SystemEventNotify( TMsgStoreSystemEvent aEvent )
	{
	TRAP_IGNORE( SystemEventNotifyL( aEvent ) );
	}


/**
 *
 */
void CBasePlugin::SystemEventNotifyL( TMsgStoreSystemEvent aEvent )
    
    {
    switch ( aEvent )
        {
        // The message store is unavailable.  This may be due to 
        // a backup or restore being in progress, a Pointsec lock being issued,
        // the disk drive being unavailable, etc.
        case EMsgStoreUnavailable:
            {
            // since the store is unavailabe we are unable to get 
            // list of mailboxes, setting plugin id only
            TFSMailMsgId id;
            id.SetPluginId( TUid::Uid( GetPluginId() ) );
            NotifyGlobalEventL( TFSMailboxUnavailable, id );
            }
        break;

        // The message store is available again.
        case EMsgStoreAvailable:
            {
            RArray<TFSMailMsgId> mailboxes;
            CleanupClosePushL( mailboxes );
            ListMailBoxesL( mailboxes );

            for ( TInt i( 0 ); i < mailboxes.Count(); i++ )
                {
                NotifyGlobalEventL( TFSMailboxAvailable, mailboxes[i] );
                }

            CleanupStack::PopAndDestroy( &mailboxes );
            }
        break;

        // The observer event queue has overflowed and has been reset, so events have been lost.  This
        // shouldn't happen unless the client thread has been starved for an extended period of time, during
        // which many message store operations have occurred.
        case EObserverEventQueueOverflow:
        //don't see anything meaningful to do here.
        default:
        break;
        };
    }


/**
 *
 */   
EXPORT_C void CBasePlugin::AccountEventNotify(
    TMsgStoreAccountEvent aEvent,
	TInt32 aOwnerId,
	const TDesC& aName,
	const TDesC& aNewName,
	TMsgStoreId aMailboxId )
	
	{
	if ( GetPluginId() == aOwnerId )
	    {
	    TRAP_IGNORE( AccountEventNotifyL( aEvent, aOwnerId, aName, aNewName, aMailboxId ) );
	    }
	}


/**
 *
 */   
void CBasePlugin::AccountEventNotifyL(
    TMsgStoreAccountEvent aEvent,
	TInt32 /*aOwnerId*/,
	const TDesC& /*aName*/,
	const TDesC& /*aNewName*/,
	TMsgStoreId aMailboxId )

    {
    switch ( aEvent )
        {
        case EMsgStoreAccountCreated:
            {
            TFSMailMsgId mailBox( GetPluginId(), aMailboxId );
            NotifyGlobalEventL( TFSEventNewMailbox, mailBox );
            }
        break;

        case EMsgStoreAccountDeleted:
            {
            //remove from the cache.
            if ( KErrNotFound != iMailboxes.Find( aMailboxId ) )
                {
                iMailboxes.RemoveL( aMailboxId );
                }

            TFSMailMsgId mailBox( GetPluginId(), aMailboxId );
            
            HandleMailboxDeleteL( mailBox );
            
            NotifyGlobalEventL( TFSEventMailboxDeleted, mailBox );
            }
        break;

        case EMsgStoreAccountRenamed:
            {
            }
        break;
        };   
    }


EXPORT_C void CBasePlugin::HandleMailboxDeleteL( const TFSMailMsgId& /*aMailboxId*/ )
    {
    //nothing to do
    }

/**
 *
 */
EXPORT_C void CBasePlugin::ModificationNotify(
    TMsgStoreId aMailBoxId,
    TMsgStoreOperation aOperation,
    TMsgStoreContainerType aType,
    TUint32 aFlags,
    TMsgStoreId aId,
    TMsgStoreId aParentId,
    TMsgStoreId aOtherId )

    {
    TRAP_IGNORE( ModificationNotifyL( aMailBoxId, aOperation, aType, aFlags, aId, aParentId, aOtherId ) );
    }


/**
 *
 */
void CBasePlugin::ModificationNotifyL(
    TMsgStoreId aMailBoxId,
    TMsgStoreOperation aOperation,
    TMsgStoreContainerType aType,
    TUint32 /*aFlags*/,
    TMsgStoreId aId,
    TMsgStoreId aParentId,
    TMsgStoreId aOtherId )

    {
    __LOG_ENTER( "ModificationNotifyL" );
    CMailboxInfo& mailBox = GetMailboxInfoL( aMailBoxId );
    
    __LOG_WRITE_FORMAT1_INFO( "aMailBoxId = 0x%X.", aMailBoxId );
    __LOG_WRITE_FORMAT1_INFO( "aId = 0x%X.", aId );
    __LOG_WRITE_FORMAT1_INFO( "aParentId = 0x%X.", aParentId );
    
    InvalidateCacheIfNecessary( aId, aParentId, aOtherId );
    
    if ( mailBox.iObservers.Count() > 0 )
        {
        switch ( aOperation )
            {
            
            case EMsgStoreAdd:
                {
                __LOG_WRITE_INFO( "EMsgStoreAdd." );
                if ( EMsgStoreMessageContainer == aType )
                    {
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventNewMail );
                    }
                else if ( EMsgStoreFolderContainer == aType )
                    {
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventNewFolder );
                    }
                }
            break;

            case EMsgStoreDelete:
                {
                __LOG_WRITE_INFO( "EMsgStoreDelete." );
                if ( EMsgStoreMessageContainer == aType )
                    {
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventMailDeleted );
                    }
                else if ( EMsgStoreFolderContainer == aType )
                    {
                    TRAPD( err, CMailboxInfo& mailBox = GetMailboxInfoL( aMailBoxId ) );
                    
                    if ( KErrNone == err )
                    	{
                    	for ( TInt i = EFSInbox; i <= EFSDeleted; i++ )
                    		{
                    		if ( mailBox.iRootFolders.iFolders[i] == aId )
                    			{
                    			TMsgStoreId newId = 0;
                    			
                    			/**@ this behavior is forced by the UI for some reason and should be revisited
                    			 * in the future as it allows more than one inbox folder which is not recommended. */
                    			
                    			//loop over all of the root folders looking for a replacement.
								RPointerArray<CMsgStoreFolder> folders;
								CleanupResetAndDestroyClosePushL( folders );

								mailBox().FoldersL( aMailBoxId, folders );

								for ( int j = 0; j < folders.Count(); j++ )
									{
									CMsgStoreFolder* folder = folders[j];

									TUint index = 0;
									if ( folder->FindProperty( KMsgStorePropertyFolderType, index ) )
										{
	                                    TUint32 type = folder->PropertyValueUint32L( index );

	                                    if ( type == i )	//i is a value in the folder type enum.
											{
											newId = folder->Id();
											break;
											}
	                                    }
									}
								
								CleanupStack::PopAndDestroy( &folders );
                                
                    			//set the new root folder info.
                    			mailBox.iRootFolders.iFolders[i] = newId;
                    			break;
                    			}                    		
                    		}
						}
                
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventFoldersDeleted );
                    }
                }
            break;
            
            case EMsgStoreMove:
                {
                __LOG_WRITE_INFO( "EMsgStoreMove." );
                NotifyEventL( aMailBoxId, aId, aOtherId, TFSEventMailMoved, aParentId );
                break;
                }

            case EMsgStoreUpdateProperties:
                {
                __LOG_WRITE_INFO( "EMsgStoreUpdateProperties." );
                if ( EMsgStoreMessageContainer == aType )
                    {
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventMailChanged );
                    }
                else if ( EMsgStoreFolderContainer == aType )
                    {
                    NotifyEventL( aMailBoxId, aId, aParentId, TFSEventFolderChanged );
                    }
                else if ( EMsgStoreMailboxContainer == aType )
                    {
                    TBool mailBoxNameHasChanged( EFalse );
                    TRAP_IGNORE( RefreshCachedMailBoxDisplayNameL( mailBoxNameHasChanged, aMailBoxId ) );
                    
                    if ( mailBoxNameHasChanged )
                        {
                        NotifyEventL( aMailBoxId, aId, aParentId, TFSEventMailboxRenamed );
                        }
                    }
                }
            break;

            case EMsgStoreRemoveContent:
                break;

            default:
                break;
            }
        }
        /* Reporting request status moved from above if-statement here. Now, request status is reported even there is no
         * mailbox observers.
         */ 
        switch ( aOperation )
            {
            case EMsgStoreUpdateProperties:
                {
                if ( EMsgStorePartContainer == aType )
                    {
                    ReportRequestStatusL( aMailBoxId, aOtherId, aParentId, aId );
                    }
                }
                break;
            /**@ check whether the ui removes the observer - then the observer must be kept
            until there are pending reqs ?*/
            //attachment download request handling.
            case EMsgStoreUpdateContent:
                {
                __LOG_WRITE_INFO( "EMsgStoreUpdateContent." );
                ReportRequestStatusL( aMailBoxId, aOtherId, aParentId, aId, ETrue );
                }
                break;
                
            default:
                break;
        }
    
        
        __LOG_EXIT;
    } //ModificationNotifyL.


/**
 *
 */
void CBasePlugin::NotifyEventL(
    TMsgStoreId aMailBoxId,
    TMsgStoreId aId,
    TMsgStoreId aParentId,
    TFSMailEvent aFsEvent,
    TMsgStoreId aOtherId /*=KMsgStoreInvalidId*/ )

    {
    CMailboxInfo& mailBox = GetMailboxInfoL( aMailBoxId );
    
    TFSMailMsgId mailBoxId( GetPluginId(), aMailBoxId );
    TFSMailMsgId parent( GetPluginId(), aParentId );
    TFSMailMsgId other( GetPluginId(), aOtherId );

    TFSMailMsgId* param3 = NULL;
    if ( KMsgStoreInvalidId != aOtherId )
    	{
    	param3 = &other;
    	}

    RArray<TFSMailMsgId> entries;
    CleanupClosePushL( entries );

    TFSMailMsgId entry( GetPluginId(), aId );
    entries.AppendL( entry );

    TInt count = mailBox.iObservers.Count();
    for ( TInt i = 0; i < count; i++ )
        {
        mailBox.iObservers[i]->EventL( aFsEvent, mailBoxId, &entries, &parent, param3 );
        }

    CleanupStack::PopAndDestroy( &entries );
    }


/**
 *
 */
void CBasePlugin::NotifyGlobalEventL(
    TFSMailEvent aEvent,
    TFSMailMsgId aMailBox /*=TFSMailMsgId()*/,   //defaults to a null id.
    TAny* aParam1 /*=NULL*/,
    TAny* aParam2 /*=NULL*/,
    TAny* aParam3 /*=NULL*/ )
    
    {
    TInt count = iObservers.Count();
    
    for ( TInt i = 0; i < count; i++ )
        {
        iObservers[i]->EventL( aEvent, aMailBox, aParam1, aParam2, aParam3 );
        }
    }

/**
 *
 */
EXPORT_C void CBasePlugin::NotifyMailboxEventL(
        TFSMailEvent aEvent,
        TFSMailMsgId aMailBox,
        TAny* aParam1 /*=NULL*/,
        TAny* aParam2 /*=NULL*/,
        TAny* aParam3 /*=NULL*/ )
    {
    CMailboxInfo& mailBox = GetMailboxInfoL( aMailBox.Id() );
    
    for ( TInt i = 0; i < mailBox.iObservers.Count(); i++ )
        {
        mailBox.iObservers[i]->EventL( aEvent, aMailBox, aParam1, aParam2, aParam3 );
        }
    }


/**
 *
 */
EXPORT_C /*protected virtual*/ void CBasePlugin::ReportRequestStatusL(
    TMsgStoreId aMailBox,
    TMsgStoreId aOtherId,
    TMsgStoreId aMsgId,
    TMsgStoreId aPartId,
    TBool aCanReportCompletion /*=EFalse*/ )

    {
    __LOG_ENTER( "ReportRequestStatusL" );

    __LOG_WRITE_FORMAT1_INFO( "aMailBox : 0x%X.", aMailBox );
    __LOG_WRITE_FORMAT1_INFO( "aMsgId : 0x%X.", aMsgId );
    __LOG_WRITE_FORMAT1_INFO( "aPartId : 0x%X.", aPartId );

    TOngoingFetchInfo fetchInfo;
    if ( FindFetchRequestL( aMailBox, aOtherId, aMsgId, aPartId, fetchInfo ) )
        {
        __LOG_WRITE_INFO( "Request match found." );

        CMailboxInfo& mailBox = GetMailboxInfoL( aMailBox );

        //get the fetched and the server sizes.                    
        CMsgStoreMessage* msg = mailBox().FetchMessageL(
        	fetchInfo.iMessageId, KMsgStoreInvalidId );
        CleanupStack::PushL( msg );
        CMsgStoreMessagePart* part = msg->ChildPartL( aPartId, ETrue );
        CleanupStack::PushL( part );

        TUint32 size = 0;
        TUint idx = 0;
        if ( part->FindProperty( KMsgStorePropertySize, idx ) )
            {
            size = part->PropertyValueUint32L( idx );
            }
        
        TUint32 fetchedSize = 0;
        idx = 0;
        if ( part->FindProperty( KMsgStorePropertyRetrievedSize, idx ) )
            {
            fetchedSize = part->PropertyValueUint32L( idx );
            }
        
        CleanupStack::PopAndDestroy( part );
        CleanupStack::PopAndDestroy( msg );

        TFSProgress progress;
        progress.iError = KErrNone;
        progress.iMaxCount = size;
        progress.iCounter = fetchedSize;
        
        __LOG_WRITE_FORMAT2_INFO(
            "Size: %d, fetched size: %d.", size, fetchedSize );
            
        if ( fetchedSize < size )
            {
            __LOG_WRITE_INFO( "Download in progress, about to notify the UI." );

            progress.iProgressStatus = TFSProgress::EFSStatus_Status;
            fetchInfo.iRequest->iObserver.RequestResponseL(
            	progress, fetchInfo.iRequest->iRequestId );
            }
        else if ( aCanReportCompletion )
            {
            if ( DeleteFetchRequestForPart( fetchInfo ) )
            	{
            	//no more parts left for downloading thus completed.
                progress.iProgressStatus
					= TFSProgress::EFSStatus_RequestComplete;                        
                __LOG_WRITE_INFO( "Download completed for all parts." );

                fetchInfo.iRequest->iObserver.RequestResponseL(
                	progress, fetchInfo.iRequest->iRequestId );
                DeleteFetchRequest( fetchInfo );
            	}
            else
            	{
                progress.iProgressStatus
					= TFSProgress::EFSStatus_Status;
                __LOG_WRITE_INFO( "A part download completed, more is left." );

                fetchInfo.iRequest->iObserver.RequestResponseL(
                	progress, fetchInfo.iRequest->iRequestId );
            	}
            }
        }
        
    __LOG_EXIT;
    }


/**
 * @return ETrue in case there are no more parts left for downloading.
 */
EXPORT_C /*protected*/ TBool CBasePlugin::DeleteFetchRequestForPart(
	TOngoingFetchInfo& aFetchInfo )
	{
    //remove the downloaded part from the request object.                        
	aFetchInfo.iRequest->iParts.Remove( aFetchInfo.iPartIndex );

	return 0 == aFetchInfo.iRequest->iParts.Count();
	}


/**
 * 
 */
EXPORT_C /*protected*/ void CBasePlugin::DeleteFetchRequest(
	TOngoingFetchInfo& aFetchInfo )
	{
	iReqs.Remove( aFetchInfo.iRequestIndex );
	delete aFetchInfo.iRequest;
	}


/**
 * 
 */
EXPORT_C TBool CBasePlugin::FindFetchRequestL(
	TMsgStoreId aMailBox,
	TMsgStoreId aOtherId,
	TMsgStoreId aMsgId,
	TMsgStoreId aPartId,
	CBasePlugin::TOngoingFetchInfo& aOngoingFetchInfo )
	{
	
	for ( TInt count = iReqs.Count(); aOngoingFetchInfo.iRequestIndex < count;
		aOngoingFetchInfo.iRequestIndex++ )
		{
		TFSMailMsgId fsId( GetPluginId(), aPartId );
		CFetchRequester* request = iReqs[aOngoingFetchInfo.iRequestIndex];
		
		//a request might specify more than one part for download, find the
		//index of the currently processed part.
		TInt partsCount = request->iParts.Count();
		for ( TInt i = 0; i < partsCount; i++ )
			{
			if ( fsId == request->iParts[i] )
				{
				aOngoingFetchInfo.iPartIndex = i;
				break;
				}
			}
	
		//there are MsgStore notification that receive the message id in the
		//"other" id, determine for sure the id of the message this part belongs
		//to. 
		if ( aMsgId == request->iMessageId.Id() )
			{
			aOngoingFetchInfo.iMessageId = aMsgId;
			}
		else if ( aOtherId 
			== request->iMessageId.Id() )
			{
			aOngoingFetchInfo.iMessageId = aOtherId;
			}
	
		//final check before choosing the download request object that
		//identifies this part download request.
		if ( aMailBox == request->iMailBoxId.Id()
			&& KMsgStoreInvalidId != aOngoingFetchInfo.iMessageId 
			&& KErrNotFound != aOngoingFetchInfo.iPartIndex )
			{
			aOngoingFetchInfo.iRequest = request;
			break;
			}
		}
	
	return NULL != aOngoingFetchInfo.iRequest;
	} //FindRequestL.


/**
 *
 */
void CBasePlugin::UnsubscribeMailboxEventsL(
    const TFSMailMsgId& aMailboxId,
    MFSMailEventObserver& aObserver )

	{
    CMailboxInfo& mailBox = GetMailboxInfoL( aMailboxId.Id() );

    TInt idx = mailBox.iObservers.Find( &aObserver );
    
    if ( KErrNotFound != idx )
        {
        mailBox.iObservers.Remove( idx );
        }
	}
