/*
* Copyright (c) 2007-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 : ESMR entry processor implementation
*  Version     : %version: 11.1.1 %
*
*/


// INCLUDE FILES

#include "cmrcaleventprocessor.h"
#include "esmrinternaluid.h"
#include "mesmrcalentry.h"
#include "esmrhelper.h"
#include "esmrentryhelper.h"
#include "cmrcalentry.h"
#include "cesmrrecurrenceinfohandler.h"
#include "emailtrace.h"

#include <e32std.h>
#include <calentry.h>
#include <calinstance.h>
#include <calrrule.h>
#include <caluser.h>
#include <caltime.h>
#include <CalenInterimUtils2.h>
#include <MAgnEntryUi.h>
#include <ct/rcpointerarray.h>

/// Unnamed namespace for local definitions
namespace {

_LIT( KReplaceLineFeedChars, "\r\n" );
_LIT( KLineFeed, " \x2029");

#ifdef _DEBUG

// Definition for module panic text
_LIT( KMRCalEntryProcessor, "MRCalEntryProcessor" );

/**
 * ESMREntryProcessor panic codes
 */
enum TMRCalEntryProcessorPanic
    {
    // Empty input array
    EMRCalEventProcessorEmptyEntryArray,    
    // Invalid processor mode
    EMRCalEventProcessorInvalidType,
    // Invalid processor mode
    EMRCalEventProcessorInvalidMode,
    // Non-supported method called
    EMRCalEventProcessorNotSupported
    };

/**
 * Raises panic.
 * @param aPanic Panic code
 */
void Panic(TMRCalEntryProcessorPanic aPanic)
    {
    User::Panic( KMRCalEntryProcessor(), aPanic);
    }

#endif // _DEBUG

/**
 * Maps CCalEntry defined entry type to ESMR entry type.
 * 
 * @param aType Calendar entry type
 * @return TESMRCalendarEventType Entry type
 */
TESMRCalendarEventType MapCalEntryTypeToMRType( 
        CCalEntry::TType aType )
    {
    TESMRCalendarEventType returnType(EESMREventTypeAppt);
        
    switch(aType)
        {
        case CCalEntry::EAppt:
            {
            returnType = EESMREventTypeAppt;
            break;
            }
        case CCalEntry::ETodo:
            {
            returnType = EESMREventTypeETodo;
            break;
            }
        case CCalEntry::EEvent:
            {
            returnType = EESMREventTypeEEvent;
            break;
            }
        case CCalEntry::EReminder:
            {
            returnType = EESMREventTypeEReminder;
            break;
            }
        case CCalEntry::EAnniv:
            {
            returnType = EESMREventTypeEAnniv;
            break;
            }
        default:
            // This should not happen
            __ASSERT_DEBUG( 
                    EFalse, 
                    Panic( EMRCalEventProcessorInvalidType ) );
            User::Leave( KErrArgument );
            break;
        }
    
    return returnType;
    }

HBufC* ReplaceCharactersFromBufferLC( const TDesC& aTarget, 
                              const TDesC& aFindString, 
                              const TDesC& aReplacement )
    {
    HBufC* newBuffer = aTarget.AllocLC();
    TPtr16 ptr = newBuffer->Des();
    
    // find next occurance:
    TInt offset = ptr.Find(aFindString);
    while ( offset != KErrNotFound )
        {
        // replace the data:
        ptr.Replace( offset, aFindString.Length(), aReplacement);
        
        // find next occurance:
        offset = ptr.Find(aFindString);
        }
    
    return newBuffer;
    }

}  // namespace

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

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::CMRCalEntryProcessor
// ---------------------------------------------------------------------------
//
CMRCalEntryProcessor::CMRCalEntryProcessor(
        MESMRCalDbMgr& aDbMgr )
:   iDbMgr( aDbMgr )
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::~CMRCalEntryProcessor
// ---------------------------------------------------------------------------
//
CMRCalEntryProcessor::~CMRCalEntryProcessor()
    {
    FUNC_LOG;
    
    delete iCalEntry;
    }
    
// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ConstructL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ConstructL()
    {
    FUNC_LOG;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::NewL
// ---------------------------------------------------------------------------
//
CMRCalEntryProcessor* CMRCalEntryProcessor::NewL(
        TAny* aDbMgr )
    {
    FUNC_LOG;
    
    MESMRCalDbMgr* dbMgr = static_cast<MESMRCalDbMgr*>(aDbMgr);
    return CreateL( *dbMgr );
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::CreateL
// ---------------------------------------------------------------------------
//
CMRCalEntryProcessor* CMRCalEntryProcessor::CreateL(
            MESMRCalDbMgr& aDbMgr )
    {
    FUNC_LOG;

    CMRCalEntryProcessor* self = new (ELeave) CMRCalEntryProcessor( aDbMgr );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    
    return self;    
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ScenarioData
// ---------------------------------------------------------------------------
//
const TESMRScenarioData& CMRCalEntryProcessor::ScenarioData() const
    {
    FUNC_LOG;
    return iScenData;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ContainsProcessedEntry
// ---------------------------------------------------------------------------
//
TBool CMRCalEntryProcessor::ContainsProcessedEntry() const
    {
    FUNC_LOG;
    return (iCalEntry != NULL);
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ProcessL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ProcessL(
        const MAgnEntryUi::TAgnEntryUiInParams* aParams,
        RPointerArray<CCalEntry>* aEntries )
    {
    FUNC_LOG;
    
    iParams = aParams;
    ProcessL( aEntries );
    
    if ( MAgnEntryUi::EViewEntry != iParams->iEditorMode )
        {
        iScenData.iViewMode = EESMREditMR;
        }    
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ProcessL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ProcessL(
            RPointerArray<CCalEntry>* aEntries )
    {
    FUNC_LOG;
    
    __ASSERT_DEBUG( aEntries->Count() > 0,
                    Panic( EMRCalEventProcessorEmptyEntryArray ) );
    
    iCalEntries = aEntries;
    
    CCalEntry& entry = *( (*iCalEntries)[ 0 ] );
    ProcessL( iParams, entry, ETrue );    
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ProcessL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ProcessL(
            const MAgnEntryUi::TAgnEntryUiInParams* aParams,
            CCalEntry& aEntry, 
            TBool aSetDefaultValuesToEntry,
            const TBool aTypeChanging )
    {
    FUNC_LOG;
    iParams = aParams;
    
    // Plain calendar entries are always used from calendar application        
    iScenData.iCallingApp = EESMRAppCalendar;
    iScenData.iEntryType = MapCalEntryTypeToMRType( aEntry.EntryTypeL() );

    if ( aParams->iEditorMode == MAgnEntryUi::EViewEntry )
        {
        iScenData.iViewMode = EESMRViewMR;
        }
    else
        {
        iScenData.iViewMode = EESMREditMR;
        }
    
    ConvertTextFieldLineFeedsL( aEntry );
    
    CreateEntryL( aEntry, aSetDefaultValuesToEntry );
    
    // When creating a new repeating meeting and adding participants, 
    // meeting request editor is opened and all occurences should be edited
    if ( ( iCalEntry->IsRecurrentEventL() && 
    		MAgnEntryUi::ECreateNewEntry == iParams->iEditorMode ) ||
    	 ( iCalEntry->IsRepeatingMeetingL( aEntry ) && aTypeChanging ) )	
        {
        iCalEntry->SetModifyingRuleL(
                MESMRCalEntry::EESMRAllInSeries, aTypeChanging );
        }
    else //Default case
        {
        iCalEntry->SetModifyingRuleL(
                MESMRCalEntry::EESMRThisOnly, aTypeChanging );
        }

    // If entry is new entry, we want to set default values to it. 
    // On the other hand, when changing entry type from one type to another, 
    // we do not want to set default values
    if ( iParams && MAgnEntryUi::ECreateNewEntry == 
                iParams->iEditorMode && 
                    aSetDefaultValuesToEntry )
        {
        iCalEntry->SetDefaultValuesToEntryL();
        }    
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ProcessL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ProcessL(
            CCalInstance& /*aInstance*/ )
    {
    FUNC_LOG;
    
    __ASSERT_DEBUG( EFalse, Panic(EMRCalEventProcessorNotSupported) );
    
    User::Leave( KErrNotSupported );
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::SwitchProcessorToModeL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::SwitchProcessorToModeL(
        TMRProcessorMode aMode )
    {
    FUNC_LOG;
    
    if ( EMRProcessorModeView == aMode )
        {
        iScenData.iViewMode = EESMRViewMR;
        }
    else if ( EMRProcessorModeEdit == aMode )
        {
        iScenData.iViewMode = EESMREditMR;
        }
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ESMREntryL
// ---------------------------------------------------------------------------
//
MESMRCalEntry& CMRCalEntryProcessor::ESMREntryL()
    {
    FUNC_LOG;
    
    if ( !iCalEntry )
        {
        CreateEntryL( *(*iCalEntries)[ 0 ], ETrue );
        }
    
    return *iCalEntry;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ResetL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ResetL()
    {
    FUNC_LOG;
    
    iScenData.iViewMode   = EESMRViewUndef;
    iScenData.iCallingApp = EESMRAppCalendar;
    
    delete iCalEntry;
    iCalEntry = NULL;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ProcessOutputParametersL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ProcessOutputParametersL(
        MAgnEntryUi::TAgnEntryUiOutParams& aOutParams,
        TESMRCommand aCommand )
    {
    FUNC_LOG;
    
    ASSERT( iCalEntry );

     TBool handlingInstanceOnly(
             MESMRCalEntry::EESMRCalEntryMeeting == iCalEntry->Type() &&
             iCalEntry->IsRecurrentEventL() &&
             MESMRCalEntry::EESMRThisOnly == iCalEntry->RecurrenceModRule() );

     switch ( aCommand )
         {
         case EESMRCmdSaveMR:
         case EESMRCmdTodoMarkAsDone:
         case EESMRCmdTodoMarkAsNotDone:
             {
             aOutParams.iAction = MAgnEntryUi::EMeetingSaved;
             if ( handlingInstanceOnly )
                 {
                 aOutParams.iAction = MAgnEntryUi::EInstanceRescheduled;
                 }
             }
             break;

         case EESMRCmdCalEntryUIDelete:
         case EESMRCmdDeleteMR:
             {
             aOutParams.iAction = MAgnEntryUi::EMeetingDeleted;
             if ( handlingInstanceOnly )
                 {
                 aOutParams.iAction = MAgnEntryUi::EInstanceDeleted;
                 }
             }
             break;

         default:
             {
             aOutParams.iAction = MAgnEntryUi::ENoAction;
             }
             break;
         }

     if ( handlingInstanceOnly )
         {
         aOutParams.iNewInstanceDate =
         iCalEntry->Entry().StartTimeL();
         }
     else if ( MAgnEntryUi::ENoAction != aOutParams.iAction )
         {
         TCalTime instanceTime = iCalEntry->Entry().StartTimeL();

         if ( MESMRCalEntry::EESMRAllInSeries == iCalEntry->RecurrenceModRule() )
             {
             // For recurrent event there might be exceptions
             // and therefore the first possible start time
             // is set to putput params
             TCalTime end;
             CESMRRecurrenceInfoHandler* recurrenceHandler =
                     CESMRRecurrenceInfoHandler::NewLC( iCalEntry->Entry() );

             recurrenceHandler->GetFirstInstanceTimeL(
                     instanceTime,
                     end );

             CleanupStack::PopAndDestroy( recurrenceHandler );
             recurrenceHandler = NULL;
             aOutParams.iNewInstanceDate = instanceTime;
             }
         else
             {
             aOutParams.iNewInstanceDate.SetTimeLocalL(
                     iCalEntry->Entry().StartTimeL().TimeLocalL() );
             }
         }
     else
         {
         aOutParams.iNewInstanceDate.SetTimeLocalL( Time::NullTTime() );
         }    
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::SetPhoneOwnerL
// ---------------------------------------------------------------------------
//
TBool CMRCalEntryProcessor::IsDataOk() const
    {
    FUNC_LOG;

    TBool retVal( ETrue );

    // Check that all values has been set and there is no
    // undefined values. Policies cannot be resolved, if
    // scenario data is undefined.
    if ( EESMRRoleUndef == iScenData.iRole ||
         EESMRViewUndef == iScenData.iViewMode ||
         EESMRAppUndef == iScenData.iCallingApp )
        {
        retVal = EFalse;
        }

    return retVal;
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::CreateEntryL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::CreateEntryL( CCalEntry& aEntry, 
                                            TBool aSetDefaultValuesToEntry  )
    {
    FUNC_LOG;
    if ( !iCalEntry )
        {
        if ( iParams && aSetDefaultValuesToEntry )
            {
            CCalEntry::TType entryType( aEntry.EntryTypeL() );
            if ( CCalEntry::EAppt == entryType ||
                 CCalEntry::EAnniv == entryType )
                {
                // Setting start and end time for child entry
                ESMREntryHelper::CheckRepeatUntilValidityL(
                        aEntry,
                        iParams->iInstanceDate );

                ESMREntryHelper::SetInstanceStartAndEndL(
                        aEntry,
                        aEntry,
                        iParams->iInstanceDate );
                }
            else if ( CCalEntry::ETodo == entryType )
                {
                TTime start = aEntry.StartTimeL().TimeLocalL();
                TTime end   = aEntry.EndTimeL().TimeLocalL();
                
                if ( Time::NullTTime() == start && 
                     Time::NullTTime() == end )
                    {
                    start.HomeTime();
                    end.HomeTime();
                    
                    TCalTime startCalTime;
                    TCalTime endCalTime;
                    
                    startCalTime.SetTimeLocalL( start );
                    endCalTime.SetTimeLocalL( end );    
                    
                    aEntry.SetStartAndEndTimeL( startCalTime, endCalTime );
                    }
                }
            }        
        
        iCalEntry = CMRCalEntry::NewL( aEntry, iDbMgr );
        }
    }

// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ConvertTextFieldLineFeedsL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ConvertTextFieldLineFeedsL()
    {
    FUNC_LOG;
        
    TInt entryCount( iCalEntries->Count() );
    for (TInt i(0); i < entryCount; ++i )
        {
        ConvertTextFieldLineFeedsL( *(*iCalEntries)[ i ] );
        }
    }


// ---------------------------------------------------------------------------
// CMRCalEntryProcessor::ConvertTextFieldLineFeedsL
// ---------------------------------------------------------------------------
//
void CMRCalEntryProcessor::ConvertTextFieldLineFeedsL( CCalEntry& aEntry )
    {
    TInt ret = aEntry.DescriptionL().Find(KReplaceLineFeedChars);    
    if ( ret != KErrNotFound )
        {
        HBufC* newDescription = 
                ReplaceCharactersFromBufferLC(
                            aEntry.DescriptionL(),       
                            KReplaceLineFeedChars(),
                            KLineFeed() );
        aEntry.SetDescriptionL( *newDescription );
        CleanupStack::PopAndDestroy( newDescription );
        }    
    
    // Location field:
    ret = aEntry.LocationL().Find(KReplaceLineFeedChars);    
    if ( ret != KErrNotFound )
        {
        HBufC* newLocation = 
                ReplaceCharactersFromBufferLC(
                            aEntry.DescriptionL(),       
                            KReplaceLineFeedChars(),
                            KLineFeed() );
        aEntry.SetDescriptionL( *newLocation );
        CleanupStack::PopAndDestroy( newLocation );
        }    
        
    // Subject field:
    ret = aEntry.SummaryL().Find(KReplaceLineFeedChars);    
    if ( ret != KErrNotFound )
        {
        HBufC* newSubject = 
                ReplaceCharactersFromBufferLC(
                        aEntry.DescriptionL(),       
                        KReplaceLineFeedChars(),
                        KLineFeed() );
        aEntry.SetDescriptionL( *newSubject );
        CleanupStack::PopAndDestroy( newSubject );
        }        
    }

// EOF

