meetingui/meetingrequestviewers/src/CMRProcessor.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 10:12:19 +0200
changeset 0 f979ecb2b13e
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* Copyright (c) 2002-2005 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: Implementation for meeting request processor 
*
*/




// INCLUDE FILES
#include "CMRProcessor.h"
#include "MRHelpers.h"
#include "mrdatalog.h"
#include "ICalUILogDef.h"
#include <e32std.h>
#include <calentry.h> 		//CCalEntry (Calendar entry API V2)
#include <caluser.h> 		//caluser and attendee
#include <MsgMailUIDs.h> 	//uid for mail application
#include <cemailaccounts.h>
#include <smtpset.h>
#include <imapset.h>
#include <pop3set.h>
#include <MuiuMsvUiServiceUtilities.h>
#include "MRViewersPanic.h" 			//panic codes
#include "meetingrequestviewers.hrh" 	//common constants
#include "ProcessingStructs.h" 			//scenario structures
#include <SendUiConsts.h> 				//mailbox uids
#include <stringloader.h> 				//loading of string resources
#include <meetingrequestviewersuires.rsg> 	//resource definitions header
#include <CalenInterimUtils2.h>
#include <cmrmailboxutils.h>

// CONSTANTS
/// Unnamed namespace for local definitions
namespace {

_LIT( KPanicMsg, "CMRProcessor" );

void Panic( TPanicCode aReason )
    {
    User::Panic( KPanicMsg, aReason );
    }

}  // namespace

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

// -----------------------------------------------------------------------------
// CMRProcessor::CMRProcessor
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMRProcessor::CMRProcessor(
    CMRMailboxUtils& aMRMailboxUtils,
    CMRUtilsInternal& aMRUtils,
    const MAgnEntryUi::TAgnEntryUiInParams& aParams,
    RPointerArray<CCalEntry>& aEntries )
    : iMRMailboxUtils( aMRMailboxUtils ),
      iMRUtils( aMRUtils ),
      iEntryUiInParams( aParams ),
      iArrayOfEntries( aEntries ),
      iEditMode( MMRModelInterface::EModeNotSet ),
      iOwnerRole( ENotFound )
    {
	// set initial values to scenario data
	iCombinedScenData.iMethod = CCalEntry::EMethodNone;
	iCombinedScenData.iCallingApp = ENotSet;
	iCombinedScenData.iOpenedMailbox = ENotSet;
    iCombinedScenData.iUtilsDBResult = MMRUtilsTombsExt::EUndefined;		    	
    iCombinedScenData.iMethodLevelValidity = ENotSet;
	iCombinedScenData.iEditorMode = ENotSet;
    iCombinedScenData.iMROperation = ENotSet;    
	}

// -----------------------------------------------------------------------------
// CMRProcessor::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMRProcessor::ConstructL()
    {    
    }

// -----------------------------------------------------------------------------
// ?classname::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CMRProcessor* CMRProcessor::NewL(
    CMRMailboxUtils& aMRMailboxUtils,
    CMRUtilsInternal& aMRUtils,
    const MAgnEntryUi::TAgnEntryUiInParams& aParams,
    RPointerArray<CCalEntry>& aEntries )
    {
    CMRProcessor* self = new( ELeave ) CMRProcessor( aMRMailboxUtils,
                                                     aMRUtils,
                                                     aParams,
                                                     aEntries );
   CleanupStack::PushL( self );
   self->ConstructL();
   CleanupStack::Pop();

   return self;
   }

// Destructor
CMRProcessor::~CMRProcessor()
    {
    delete iCombinedEntry;
    }

CCalEntry* CMRProcessor::CombinedEntry()
	{
	// Should always be valid when this method is called
    __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) );	
	return iCombinedEntry;
	}

void CMRProcessor::SetPhoneOwnerL()
    {
	__ASSERT_DEBUG( iArrayOfEntries.Count() > 0, Panic( EEmptyEntryArray ) );
	
    // set phone owner field of each entry:
    
    TInt count( iArrayOfEntries.Count() );
    for ( TInt i( 0 ); i < count; ++i )    
        {        
        CCalEntry& entry = *( iArrayOfEntries[i] );
        iMRMailboxUtils.SetPhoneOwnerL( entry, iEntryUiInParams.iMailBoxId );
        }
    
    // set iOwnerRole, use entry at index zero:
        
	CCalEntry& baseEntry = *( iArrayOfEntries[0] );	
	if ( iMRMailboxUtils.IsOrganizerL( baseEntry ) )
	    {
	    iOwnerRole = EOrganiser;
	    }	    
    else
        {        
	    CCalAttendee* thisAttendee = iMRMailboxUtils.ThisAttendeeL( baseEntry );
    	if ( thisAttendee )
    		{
    		switch ( thisAttendee->RoleL() )
    			{
    			case CCalAttendee::EOptParticipant:
    				{
    				iOwnerRole = EOptionalParticipant;
    				break;
    				}
    			case CCalAttendee::ENonParticipant:
    				{
    				iOwnerRole = ENonRequiredParticipant;
    				break;
    				}
                case CCalAttendee::EReqParticipant: // fall through
    			case CCalAttendee::EChair: // fall through
    			default:
    				{ // Note: chair MUST NOT be interpreted as organizer!
    				  // Req.participant is not correct either but has less
    				  // side effects.
    				iOwnerRole = ERequiredParticipant;
    				break;
    				}
    			}
    		}
        }
    }

CMRProcessor::TOwnerRole CMRProcessor::OwnerRole()
    {
    return iOwnerRole;
    }

MMRUtilsTombsExt::TMRUtilsDbResult CMRProcessor::ProcessingDbResult()
    {
    return iCombinedScenData.iUtilsDBResult;
    }

TInt CMRProcessor::ProcessingResultOp()
    {
    return iCombinedScenData.iMROperation;
    }

MMRModelInterface::TEditingMode CMRProcessor::EditingMode() const
    {
    return iEditMode;
    }
    
void CMRProcessor::SetEditingModeL( MMRModelInterface::TEditingMode aEditMode )
    {
    __ASSERT_DEBUG( iEditMode == MMRModelInterface::EModeNotSet,
                    Panic( EEditModeResetAttempt ) );    
    iEditMode = aEditMode;
    if ( aEditMode == MMRModelInterface::EEditMeeting )
        { // if we switch to edit meeting mode the combined entry must be re-set
        CCalEntry* entry = iArrayOfEntries[0];
        ResetCombinedEntryL( *entry, TCalTime() );                
		iCombinedEntry->CopyFromL( *entry );		
		iCombinedEntry->SetMethodL( entry->MethodL() );        
        }
    // no need to do anything if aEditMode == EEditInstance since that is the
    // default when launching the MR Viewer with incoming entry array.
    }

// TODO: CMRUtils::CheckEntryCondL doesn't, and shouldn't (because of e.g.
// calendar sync), notice problems with out-of-date entry (meeting occurred in past),
// but in processor we should identify that situation. Possibly add a new processing
// struct data field, and possibly a new TMROperation code also?

// TODO: somewhere we must set outparams: saved, meeting/instance deleted,
// but is processor a good place for it.     

// TODO: when handling processing leave, we must take care that iCombinedEntry
// is set to point to some entry, or to ensure that there doesn't become an access
// violation because someone is trying to access it.
void CMRProcessor::ProcessEntriesL()
	{
	if ( iArrayOfEntries.Count() < 1 )
	    {
	    User::Leave( KErrArgument );
	    }	    
    CCalEntry* entry = iArrayOfEntries[0];
    
    // Sets phone owner in entries and in iOwnerRole, this must be done at
    // early stage, before trying to read organizer or attendee fields
    SetPhoneOwnerL();
	    
	// set common values to scenario data
	iCombinedScenData.iMethod = entry->MethodL();
	iCombinedScenData.iCallingApp = CallingApp();
	iCombinedScenData.iOpenedMailbox = OpenedMailboxL( *entry );
	iCombinedScenData.iEditorMode = EditorMode();
    if ( iCombinedScenData.iEditorMode == MAgnEntryUi::EViewEntry )
        { // in other cases editing mode will be decided later, but 
          // in EViewEntry case we know it already now
        SetEditingModeL( MMRModelInterface::EViewOnly );
        }

    TRAPD( err,
        {        
        if ( entry->MethodL() == CCalEntry::EMethodReply )
            {
            ProcessResponseArrayL();
            }        
        else 
            {        
            if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication )
                {
                ProcessRequestInCalendarL();
                }
            else
                {
                ProcessRequestOrCancelArrayL();
                }
            }        
        } );
    if ( err != KErrNone )
        {
        ProcessErrorL();
        }            
	}
		
/**
* In case of calendar we show in start-up:
* a) parent entry if meeting non-repeating
* b) existing child entry if received as input
* c) newly created child if repeating meeting but child doesn't exist
* User may later on choose "edit series" mode if meeting is repeating.
*/
void CMRProcessor::ProcessRequestInCalendarL()
    {
	if ( iArrayOfEntries.Count() != 1 )
	    {
	    // From calendar we support only receiving single entry at a time
	    User::Leave( KErrArgument );
	    }
	
    CCalEntry* entry = iArrayOfEntries[0];
    
    iCombinedScenData.iMROperation =
        MatchScenarioToDataL( iCombinedScenData, *entry );
    if ( iCombinedScenData.iMROperation <= ENullOp )
        {
        User::Leave( KErrArgument );
        }    

    if ( MREntryConsultant::IsRepeatingMeetingL( *entry ) )
        {
        // reset combined entry, it will be a child representing the instance
        ResetCombinedEntryL( *entry, iEntryUiInParams.iInstanceDate );
        
        if ( MREntryConsultant::IsModifyingEntryL( *entry ) )
            { // entry exists for the instance, use that
    		iCombinedEntry->CopyFromL( *entry );		
    		iCombinedEntry->SetMethodL( entry->MethodL() );
            }
        else
            { // create a fully populated child representing the instance
            SetInstanceStartAndEndL( *iCombinedEntry,
                                     *entry, 
                                     iEntryUiInParams.iInstanceDate );                      
            CCalenInterimUtils2::PopulateChildFromParentL( *iCombinedEntry,
                                                           *entry );
            }
        }
    else
        { // non-repeating meeting -> view/edit entire meeting
        // reset combined entry, it will be a parent
        ResetCombinedEntryL( *entry, TCalTime() );                
		iCombinedEntry->CopyFromL( *entry );		
		iCombinedEntry->SetMethodL( entry->MethodL() );
		
		// in case of non-repeating meeting the editing mode is always
		// EEditMeeting and user doesn't have a possibility to change it
		SetEditingModeL( MMRModelInterface::EEditMeeting );     
        }    
    }
	
void CMRProcessor::ProcessResponseArrayL()
	{

// TODO: we must check that entry isn't out of date or cancelled,
// should we do that in HandleResponseStatusL or where? It would be
// better to do it before calling TryCreateModForResponseL() to avoid
// unnecessary mod entry in	db.
	
	// 1. Handle entry at index 0 ,it may be response to a parent or a child
		
	CCalEntry& response = *( iArrayOfEntries[0] );
		
	// when checking against db entry, respond should look like a valid update:
	iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( response );
	
	// ...but if attendee has responded to an instance of a meeting, then
	// corresponding modifying entry doesn't necessarily exist -> create it:
	if ( iCombinedScenData.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew )
	    {	    
	    CreateModForResponseL( response, iCombinedScenData );
	    }
	
	// check response validity on method level and set status to request, if ok:
	CCalEntry* request = HandleResponseStatusL( response, iCombinedScenData );
	if ( request )
	    {
	    CleanupStack::PushL( request );
    	// match to scenario, entry to be updated is request, not response
        MatchScenarioAndSaveIfNeededL( *request, iCombinedScenData );
	    }
    else
        {
        // if request not found saving won't occur, we still perform matching
        // to get scenario completed, we give response as a parameter but it
        // won't be used in this case
        MatchScenarioAndSaveIfNeededL( response, iCombinedScenData );
        }
		
    // 2. Go through additional entries, they don't affect combined entry
    //    and related functionality

    TScenarioData tmpScenario( iCombinedScenData );
	TInt count( iArrayOfEntries.Count() );
	for ( TInt i( 1 ); i < count; ++i )
		{
		CCalEntry& tmpResponse = *( iArrayOfEntries[i] );
		tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpResponse );
	    if ( tmpScenario.iUtilsDBResult == MMRUtilsTombsExt::ECheckedValidNew )
	        {	    
	        CreateModForResponseL( response, tmpScenario );
	        }		
		CCalEntry* tmpRequest = HandleResponseStatusL( response, tmpScenario );
		if ( tmpRequest )
		    { // for additional entries we only match scenario if request exists,
		      // otherwise it is unnecessary
		    CleanupStack::PushL( tmpRequest );
		    MatchScenarioAndSaveIfNeededL( *tmpRequest, tmpScenario );
		    CleanupStack::PopAndDestroy( tmpRequest );
		    }
		}
    
    // 3. Set combined entry == first response possibly mixed with request

    ResetCombinedEntryL( response, response.RecurrenceIdL() );
    iCombinedEntry->CopyFromL( response );
    iCombinedEntry->SetMethodL( CCalEntry::EMethodReply );

	if ( iCombinedScenData.iMROperation == EUpdateStatusToCalendar )
		{
    	// TODO: set response summary with status information    	
    	iCombinedEntry->SetLocationL( request->LocationL() );
    	iCombinedEntry->SetStartAndEndTimeL( request->StartTimeL(),
    	                                     request->EndTimeL() );
        // Response won't necessarily contain RRule/RDate information, but
        // that isn't shown to the user either -> not needed in iCombinedEntry.
		}
    else
        {
        User::Leave( KErrArgument );
        }
        
    if ( request )
        {
        CleanupStack::PopAndDestroy( request );
        }
	}
	
/**
* This method assumes that response has been verified to be valid.
*/
void CMRProcessor::CreateModForResponseL(
    const CCalEntry& aResponse,
    TScenarioData& aScenarioData ) const
    {
    // fetch parent:
    CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(),
                                               TCalTime() );
    CleanupStack::PushL( request );
    // create modifying entry:
    HBufC8* modUid = request->UidL().AllocLC();
	CCalEntry* mod = CCalEntry::NewL( CCalEntry::EAppt,
                                      modUid,
                                      CCalEntry::EMethodRequest,
                                      request->SequenceNumberL(),
                                      aResponse.RecurrenceIdL(),
                                      CalCommon::EThisOnly );
    CleanupStack::Pop( modUid ); // ownership transferred
    CleanupStack::PushL( mod );
    
    // In this case response shouldn't really have any RRules or RDates,
    // and the recurrence id should specify the instance which attendee
    // responded to. Therefore create modifying entry representing that
    // instance.
    SetInstanceStartAndEndL( *mod, *request, aResponse.RecurrenceIdL() );
    // Populate modifying entry fields, especially it is important that
    // also entire attendee list gets copied, since this modifying entry
    // will be exceptioned in the parent
    CCalenInterimUtils2::PopulateChildFromParentL( *mod, *request );
    // Modifying entry must have the same DTSTAMP as originating entry,
    // since in this case they were sent as one request
    mod->SetDTStampL( request->DTStampL() );        
    // Set modifying request as sent!
    // TODO: this will be done differently when Symbian supports other fields
    // to indicate sending status
    mod->SetStatusL( CCalEntry::EConfirmed );
        
    MMRUtilsTombsExt::TMRUtilsDbResult res = iMRUtils.StoreL( *mod, EFalse );
    if ( res != MMRUtilsTombsExt::EStoredUpdate )
        { // store should succeed if response was for a valid instance
        User::Leave( KErrGeneral );
        }
    
    CleanupStack::PopAndDestroy( 2 ); // mod, request
    
    // now there exists a modifying entry and response is
    // an update to it:
    aScenarioData.iUtilsDBResult = MMRUtilsTombsExt::ECheckedValidUpdate;
    }
 
CCalEntry* CMRProcessor::HandleResponseStatusL(
    const CCalEntry& aResponse,
    TScenarioData& aScenarioData ) const
    {
    aScenarioData.iMethodLevelValidity = EInvalid;
    if ( aResponse.AttendeesL().Count() != 1 )
        { // response MUST have exactly one attendee, the respondent
        User::Leave( KErrArgument );
        }
	CCalAttendee& respondent = *( aResponse.AttendeesL()[0] );	

    CCalEntry* request = iMRUtils.FetchEntryL( aResponse.UidL(),
                                               aResponse.RecurrenceIdL() );
    if ( request  )
        {
        CleanupStack::PushL( request );        	
    	TBool changed( EFalse );
    	CCalAttendee* dbAttendee =
    	    RespondentInRequestL( aResponse, *request, changed );
    	if ( dbAttendee && changed )
    		{
    		dbAttendee->SetStatusL( respondent.StatusL() );
    		aScenarioData.iMethodLevelValidity = EValidNeedsSave;
    		}
        else if ( dbAttendee && !changed )
            {
            aScenarioData.iMethodLevelValidity = EValidNoNeedSave;
            }
        CleanupStack::Pop( request );
        }
    return request; // may be NULL
    }

/**
* We always show the first entry, if it is a child user will see that
* child and responses affect that (and other children in the array)
* If the first entry is a parent responses affect also children in the array.
*
* Also in case of cancellation entry is stored to db instead of just updating
* the status, this means that e.g. description may be changed, but we
* consider that is the desired behavior.
*/
void CMRProcessor::ProcessRequestOrCancelArrayL()
	{
	// 1. Handle entry at index 0 ,it may be a parent or a child	
	
	CCalEntry& entry = *( iArrayOfEntries[0] );
	iCombinedScenData.iUtilsDBResult = iMRUtils.CheckEntryCondL( entry );
    MatchScenarioAndSaveIfNeededL( entry, iCombinedScenData );
		
    // 2. Go through additional entries, they don't affect combined entry
    //    and related functionality

    TScenarioData tmpScenario( iCombinedScenData );
	TInt count( iArrayOfEntries.Count() );
	for ( TInt i( 1 ); i < count; ++i )
		{
		CCalEntry& tmpEntry = *( iArrayOfEntries[i] );
		tmpScenario.iUtilsDBResult = iMRUtils.CheckEntryCondL( tmpEntry );
		MatchScenarioAndSaveIfNeededL( tmpEntry, tmpScenario );
		}
    
    // 3. Set combined entry == first entry in the input array

    ResetCombinedEntryL( entry, entry.RecurrenceIdL() );

	if ( iCombinedScenData.iMROperation == EViewExistingEntry )
		{
		iCombinedEntry->CopyFromL( entry );				
		iCombinedEntry->SetMethodL( entry.MethodL() );				
		}
    else if ( iCombinedScenData.iMROperation == ELoadIdenticalEntryFromDB || 
              iCombinedScenData.iMROperation ==EStoreEntryToCalendar )
		{
		// Identical entry exists in db -> fetch parent entry
		// from db and prepare for viewing
		ReadEntryFromDbL( entry, *iCombinedEntry );
		}
    else
        {
        User::Leave( KErrArgument );
        }
    }

TBool CMRProcessor::MatchScenarioAndSaveIfNeededL(
    CCalEntry& aCalEntry,
    TScenarioData& aScenarioData ) const
    {
    TBool retVal( EFalse );	
	aScenarioData.iMROperation =
	    MatchScenarioToDataL( aScenarioData, aCalEntry );
	
	if ( aScenarioData.iMROperation == EStoreEntryToCalendar )
		{
		MMRUtilsTombsExt::TMRUtilsDbResult dbResult =
		    iMRUtils.StoreL( aCalEntry, EFalse );
		if ( dbResult < MMRUtilsTombsExt::EUndefined )
			{ // store shouldn't fail if all checks were successful
			User::Leave( KErrGeneral );
			}
        else
			{ // we have saved something
			retVal = ETrue;
			}
		}
    else if ( aScenarioData.iMROperation == EUpdateStatusToCalendar )
        { // update shouldn't fail if all checks were successful
        User::LeaveIfError( iMRUtils.UpdateEntryL( aCalEntry ) );        
        retVal = ETrue;
        }
    return retVal;
    }

void CMRProcessor::ProcessErrorL()
    {
    switch ( iCombinedScenData.iMROperation )
        {
        case EErrorSituation:
            {
    		if ( iCombinedScenData.iUtilsDBResult ==
	    	     MMRUtilsTombsExt::EErrorHasBeenDeleted )
		        { // possibly valid entry but has been deleted from the phone
		        
        	    // request has been deleted, we show the received response then
        	    // according to UI spec. if organizer cancels request it gets deleted
        	    // TODO: show info note Original meeting request is not in phone’s calendar. §qtn.mail.mtg.req.note.no.mtg§ with
                // TODO: this note should be shown always as an opening note, some opening notes
                // are shown only when launching for the first time -> handle that!
    		    }
            else
                {// TODO: handle
                }
            break;
            }
        case EErrorUnexpectedViewOnly:
            { // possibly valid entry but unexpected in this context
            // TODO: handle
            break;
            }
        case EErrorObsoleteViewOnly:
            { // possibly valid entry but obsolete in this context
            // TODO: handle
            break;
            }
        default:
            {
            User::Leave( KErrGeneral );
            }       
        }
    // show errorneus entry, but only for viewing
	CCalEntry& entry = *( iArrayOfEntries[0] );
    ResetCombinedEntryL( entry, entry.RecurrenceIdL() );
	iCombinedEntry->CopyFromL( entry );				
	iCombinedEntry->SetMethodL( entry.MethodL() );	
    }

TInt CMRProcessor::MatchScenarioToDataL(
    TScenarioData currentScenario,
    const CCalEntry& aEntry ) const
	{
    MRDATA_LOG("# start scenario match #");
    MRDATA_LOG1("entry name: %S", &(aEntry.SummaryL()));
    	
	TInt retVal( EErrorSituation );
	
	CArrayFixFlat<TInt>* scenarioValues =
	    new( ELeave ) CArrayFixFlat<TInt>( dataFieldCount-1 );

	CleanupStack::PushL( scenarioValues );

    // Order of fields must match the order used in ProcessingStructs.h
	scenarioValues->AppendL( currentScenario.iMethod );
	scenarioValues->AppendL( currentScenario.iCallingApp );
	scenarioValues->AppendL( currentScenario.iOpenedMailbox );
	scenarioValues->AppendL( currentScenario.iUtilsDBResult );
	scenarioValues->AppendL( currentScenario.iMethodLevelValidity );
	scenarioValues->AppendL( currentScenario.iEditorMode );
	
	// Go through each predefined scenario and see if one of them matches
	for ( TInt scenarioCounter( 0 );
	      scenarioCounter < scenarioCount;
	      scenarioCounter++ )
		{
        TBool scenarioMismatch( EFalse );
		const TInt* arrayPtr = scenarioArray[scenarioCounter];
		for ( TInt i( 0 ); i < dataFieldCount-1; i++ )
			{
			TInt arrayVal = *( arrayPtr+i );
			TInt scenarioVal = scenarioValues->At( i );

			if( arrayVal != ENotSet )
				{
				if ( scenarioVal != arrayVal )
				    {				    
					scenarioMismatch = ETrue;
				    }
				}
			}

		if ( !scenarioMismatch )
			{ // Match was found, retVal is operation corresponding to the
			  // matching scenario, i.e. the last field in that scenario array
			retVal = *( arrayPtr + dataFieldCount - 1 );
            MRDATA_LOG1("scenario id: %d", scenarioCounter );
            MRDATA_LOG1("operation: %d", retVal );			
			break;
			}
		}

	CleanupStack::PopAndDestroy( scenarioValues );
	    
    MRDATA_LOG("# end scenario match #");	
	return retVal;
	}

TInt CMRProcessor::CallingApp() const
	{
	TInt callingApp = iEntryUiInParams.iCallingApp.iUid;
    TInt retVal( ENotSet );

	switch ( callingApp )
		{
		case KUidCalendarApplication:
			{
			retVal = ECallerCalendarApp;
			break;
			}
		case KUidMailApplication:
			{
			retVal = ECallerMailApp;
			break;
			}
		case KUidBVAApplication:
			{
			retVal = ECallerBVApp;
			break;
			}
		default:
			{
			//error situation, should never come here
			retVal = ENotSet;
			break;
			}
		}
    return retVal;
	}

TInt CMRProcessor::EditorMode() const
    {
    if ( iEntryUiInParams.iCallingApp.iUid == KUidCalendarApplication )
        { // Editor mode is relevant only when calling app is calendar
        return iEntryUiInParams.iEditorMode;
        }
    else
        {
        return MAgnEntryUi::EViewEntry;
        }
    }

// TODO: should we also support drafts mailbox type?
// That should be done by checking IsSent( entry ) in the outbox case.
TInt CMRProcessor::OpenedMailboxL( const CCalEntry& aEntry ) const
	{
	TInt retVal( ENotSet );	
	if ( iEntryUiInParams.iCallingApp.iUid == KUidMailApplication )	
	    {	    
        // Organizer sends other types than responses, and attendees
        // on the other hand send only responses
        TBool isOrganizer( iMRMailboxUtils.IsOrganizerL( aEntry ) );
        TBool isResponse( aEntry.MethodL() == CCalEntry::EMethodReply );
        if ( ( isOrganizer && !isResponse ) || ( !isOrganizer && isResponse  ) )
            {
            retVal = EOpenedFromOutbox;
            }
        else
            {
            retVal = EOpenedFromInbox;
            }
	    }
    return retVal;
	}

CCalAttendee* CMRProcessor::RespondentInRequestL(
    const CCalEntry& aResponse,
    const CCalEntry& aRequest,
    TBool& aStatusChange ) const
	{
	__ASSERT_DEBUG( aResponse.MethodL() == CCalEntry::EMethodReply,
	                Panic( EUnexpectedEntryMethodType ) );   

	aStatusChange = EFalse;
	CCalAttendee* respondent = aResponse.AttendeesL()[0];
	CCalAttendee* dbAttendee =
	    MREntryConsultant::EqualAttendeeL( *respondent, aRequest );
    if ( dbAttendee )
        {            
		if ( dbAttendee->StatusL() != respondent->StatusL() )
            {
            aStatusChange = ETrue;
            }
        }
    return dbAttendee;
	}
	
void CMRProcessor::ReadEntryFromDbL(
    const CCalEntry& aSourceEntry,
    CCalEntry& aTargetEntry ) const
	{
    CCalEntry* dbEntry = iMRUtils.FetchEntryL( aSourceEntry.UidL(),
                                               aSourceEntry.RecurrenceIdL() );
	CleanupStack::PushL( dbEntry );	
	aTargetEntry.CopyFromL( *dbEntry );
	aTargetEntry.SetMethodL( dbEntry->MethodL() );	

	CleanupStack::PopAndDestroy( dbEntry );
	}
 
// method to be called externally
void CMRProcessor::RefreshViewableEntryL()
    {
    __ASSERT_DEBUG( iCombinedEntry, Panic( ECombinedCalEntryNull ) );
    if ( MREntryConsultant::ExistsInDbL( *iCombinedEntry, iMRUtils ) )
        { // this method is only feasible if entry exists in the database,
          // otherwise we cannot refresh anything...
        ResetCombinedEntryL( *iCombinedEntry, iCombinedEntry->RecurrenceIdL() );
        ReadEntryFromDbL( *iCombinedEntry, *iCombinedEntry );
        }
    }

void CMRProcessor::ResetCombinedEntryL(
    const CCalEntry& aBase, 
    const TCalTime& aInstanceDate )
    {
	HBufC8* calUid = aBase.UidL().AllocLC();
    CCalEntry* newEntry;
    if ( aInstanceDate.TimeUtcL() != Time::NullTTime() )
        {
    	newEntry = CCalEntry::NewL( CCalEntry::EAppt,
	                                calUid,
	                                aBase.MethodL(),
	                                aBase.SequenceNumberL(),
	                                aInstanceDate,
	                                CalCommon::EThisOnly );
        }
    else
        {        
    	newEntry = CCalEntry::NewL( CCalEntry::EAppt,
	                                calUid,
	                                aBase.MethodL(),
	                                aBase.SequenceNumberL() );
        }
    delete iCombinedEntry;
    iCombinedEntry = NULL;    
    iCombinedEntry = newEntry;
	CleanupStack::Pop( calUid ); // ownership transferred to iCombinedEntry    
    }
    
void CMRProcessor::SetInstanceStartAndEndL(
    CCalEntry& aChild,
    const CCalEntry& aParent,
    const TCalTime& aInstanceStart ) const
    {
    TTime dtstart( aParent.StartTimeL().TimeUtcL() ); // first instance start
    TTime dtend( aParent.EndTimeL().TimeUtcL() ); // first instance end
    TTime instStart( aInstanceStart.TimeUtcL() );    
    TTime nullTime( Time::NullTTime() );
    if ( dtstart == nullTime || dtend == nullTime || instStart == nullTime )
        {
        User::Leave( KErrArgument );
        }        
    
    TTimeIntervalMicroSeconds duration( 0 );
    duration = dtend.MicroSecondsFrom( dtstart );
    TCalTime instEnd;    
	instEnd.SetTimeUtcL( instStart + duration );

    aChild.SetStartAndEndTimeL( aInstanceStart, instEnd );
    }