/*
* Copyright (c) 2008 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:  Converts vCalendars PIM <-> Versit
 *
*/


// CLASS HEADER
#include "cpimcalendarconverter.h"

// INTERNAL INCLUDES
#include "pimcommon.h" // tpimfield
#include "pimpanics.h"
#include "pimversit.h"
#include "cpimitem.h"
#include "cpimeventitem.h"
#include "cpimtodoitem.h"
#include "cpimtodopropertyconverter.h"
#include "cpimeventpropertyconverter.h"
#include "fs_methodcall.h"
#include "logger.h"
#include "cleanupresetanddestroy.h"

// EXTERNAL INCLUDES
#include <vcal.h>

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

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CPIMCalendarConverter* CPIMCalendarConverter::NewL(
    const CPIMEventValidator& aEventValidator,
    const CPIMToDoValidator& aToDoValidator,
    java::util::FunctionServer* aFuncServer)
{
    JELOG2(EPim);
    CPIMCalendarConverter* self = new(ELeave) CPIMCalendarConverter(
        aEventValidator, aToDoValidator, aFuncServer);

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

    return self;
}

// Destructor
CPIMCalendarConverter::~CPIMCalendarConverter()
{
    JELOG2(EPim);
    delete iToDoConverter;
    delete iEventConverter;
}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::ItemToStreamL
// Converts Event/ToDo Items to vCards.
// -----------------------------------------------------------------------------
//
void CPIMCalendarConverter::ItemToStreamL(RWriteStream& aStream, // RWriteStream to write the vCard to.
        const CPIMItem& aItem, // CPIMItem which is to be converted.
        const Versit::TVersitCharSet aCharset) // charSet used in versit objects
{
    JELOG2(EPim);
    CParserVCal* calParser = CParserVCal::NewL();
    CleanupStack::PushL(calParser);
    calParser->SetDefaultCharSet(aCharset);

    CParserVCalEntity* parser = CParserVCalEntity::NewL();
    // NOTE: parser is not put into the cleanup stack since AddEntityL
    // pushes it right from the start. So it MUST not be pushed here
    // to avoid duplicate cleanup stack pointers
    calParser->AddEntityL(parser);
    // No need to pop here

    CArrayFix<TPIMField>* fields = aItem.GetFieldsL();
    CleanupStack::PushL(fields);
    TInt fieldCount = fields->Count();
    switch (aItem.ItemType())
    {
    case EPIMEventList:
    {
        TPtrC vEvent(KVersitVarTokenVEVENT);
        parser->SetEntityNameL(vEvent);
        for (TInt i = 0; i < fieldCount; i++)
        {
            TPIMEventField eventField =
                static_cast<TPIMEventField>(fields->At(i));
            CallMethodL(iEventConverter,
                        &CPIMEventPropertyConverter::ConvertEventFieldL, aItem,
                        *parser, eventField, iFuncServer);
        }
        // Priority is a mandatory property for vCals so add it here...
        // Since event items do not have priority field, use unspecified
        // priority value which is zero
        CParserPropertyValue* value = new(ELeave) CParserPropertyValueInt(0);
        // value is pushed to the cleanup stack in AddPropertyToParserL
        // and parser takes the ownership of the added value
        iEventConverter->AddPropertyToParserL(value, KVersitTokenPRIORITY(),
                                              *parser);
        // Add repeat rule vCal property if the item is repeating
        const CPIMEventItem& eventItem =
            static_cast<const CPIMEventItem&>(aItem);
        if (eventItem.IsRepeating())
        {
            iEventConverter->ConvertRepeatRuleL(eventItem.GetRepeat(), *parser);
        }
        break;
    }
    case EPIMToDoList:
    {
        // Priority field neesds to be added if not present in the item
        if (aItem.CountValuesL(EPIMToDoCompleted) == 0)
        {
            CParserPropertyValue* propertyValue =
                CParserPropertyValueHBufC::NewL(KPIMStatusUncompleted);
            // value is pushed to the cleanup stack in AddPropertyToParserL
            // and parser takes the ownership of the added value
            iToDoConverter->AddPropertyToParserL(propertyValue,
                                                 KVersitTokenSTATUS(), *parser);
        }
        // Convert other fields
        TPtrC vToDo(KVersitVarTokenVTODO);
        parser->SetEntityNameL(vToDo);
        for (TInt i = 0; i < fieldCount; i++)
        {
            TPIMToDoField todoField =
                static_cast<TPIMToDoField>(fields->At(i));
            CallMethodL(iToDoConverter,
                        &CPIMToDoPropertyConverter::ConvertToDoFieldL, aItem, *parser,
                        todoField, iFuncServer);
        }
        break;
    }
    default:
    {
        User::Leave(KErrArgument);
    }
    }
    calParser->ExternalizeL(aStream);
    aStream.CommitL();
    CleanupStack::PopAndDestroy(2, calParser);  // fields, parser
}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::StreamToItemL
// Converts vCards to Event Items
// -----------------------------------------------------------------------------
//
void CPIMCalendarConverter::StreamToItemL(RReadStream& aStream, // RReadStream to be converted.
        RPointerArray<CPIMItem>& aItemArray, Versit::TVersitCharSet aCharset)
{
    JELOG2(EPim);


    CParserVCal* parser = CParserVCal::NewL();
    CleanupStack::PushL(parser);
    parser->SetDefaultCharSet(aCharset);
    parser->InternalizeL(aStream);

    CArrayPtr<CVersitParser>* eventArray = parser->EntityL(
                                               KVersitVarTokenVEVENT, EFalse);
    CleanupStack::PushL(eventArray);
    CArrayPtr<CVersitParser>* todoArray = parser->EntityL(KVersitVarTokenVTODO,
                                          EFalse);
    CleanupStack::PushL(todoArray);

    if (eventArray)
    {
        TInt eventCount = eventArray->Count();
        for (TInt i = 0; i < eventCount; i++)
        {
            CParserVCalEntity* entity =
                static_cast<CParserVCalEntity*>(eventArray->At(i));
            ParserToEventL(*entity, aItemArray);
        }
    }

    if (todoArray)
    {
        TInt todoCount = todoArray->Count();
        for (TInt i = 0; i < todoCount; i++)
        {
            CParserVCalEntity* entity =
                static_cast<CParserVCalEntity*>(todoArray->At(i));
            ParserToToDoL(*entity, aItemArray);
        }
    }
    CleanupStack::PopAndDestroy(3, parser); // parser, eventArray, todoArray


}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::CPIMCalendarConverter
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CPIMCalendarConverter::CPIMCalendarConverter(
    const CPIMEventValidator& aEventValidator,
    const CPIMToDoValidator& aToDoValidator,
    java::util::FunctionServer* aFuncServer) :
        iEventValidator(aEventValidator), iToDoValidator(aToDoValidator),
        iFuncServer(aFuncServer)
{
    JELOG2(EPim);
}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CPIMCalendarConverter::ConstructL()
{
    JELOG2(EPim);
    iToDoConverter = CPIMToDoPropertyConverter::NewL();
    iEventConverter = CPIMEventPropertyConverter::NewL();
}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::ParserToEventL
// Converts vEvents to Event Items
// -----------------------------------------------------------------------------
//
void CPIMCalendarConverter::ParserToEventL(CParserVCalEntity& aParser,
        RPointerArray<CPIMItem>& aItemArray)
{
    JELOG2(EPim);
    CleanupResetAndDestroyPushL(aItemArray);
    CPIMEventItem* item = CPIMEventItem::NewLC(iEventValidator);
    TPIMDate alarm(TInt64(0));
    // We don't take the ownership of the propertyArray, so the properties
    // are deleted when the parser is
    CArrayPtr<CParserProperty>* propertyArray = aParser.ArrayOfProperties(
                EFalse);
    if (!propertyArray)
    {
        User::Leave(KErrCorrupt);
    }
    TInt propertyCount = propertyArray->Count();
    for (TInt i = 0; i < propertyCount; i++)
    {
        iEventConverter->ConvertPropertyL(*(propertyArray->At(i)), *item, alarm);
    }
    if (alarm.Int64() != TInt64(0))
    {
        // Count the alarm value
        if (item->CountValuesL(EPIMEventStart) > 0)
        {
            const TPIMDate startDate = item->GetDateL(EPIMEventStart, 0);
            TTimeIntervalSeconds interval(0);
            User::LeaveIfError(startDate.SecondsFrom(alarm, interval));
            item->addInt(EPIMEventAlarm, KPIMAttrNone, interval.Int());
        }
    }
    User::LeaveIfError(aItemArray.Append(item));
    CleanupStack::Pop(item); // item
    CleanupStack::Pop(&aItemArray);
}

// -----------------------------------------------------------------------------
// CPIMCalendarConverter::ParserToToDoL
// Converts vTodos to Todo Items
// -----------------------------------------------------------------------------
//
void CPIMCalendarConverter::ParserToToDoL(CParserVCalEntity& aParser,
        RPointerArray<CPIMItem>& aItemArray)
{
    JELOG2(EPim);
    CleanupResetAndDestroyPushL(aItemArray);
    CPIMToDoItem* item = CPIMToDoItem::NewLC(iToDoValidator);
    TPIMDate alarm(TInt64(0));
    // We don't take the ownership of the propertyArray, so the properties
    // are deleted when the parser is
    CArrayPtr<CParserProperty>* propertyArray = aParser.ArrayOfProperties(
                EFalse);
    if (!propertyArray)
    {
        User::Leave(KErrCorrupt);
    }
    TInt propertyCount = propertyArray->Count();
    for (TInt i = 0; i < propertyCount; i++)
    {
        iToDoConverter->ConvertPropertyL(*(propertyArray->At(i)), *item, alarm);
    }
    if (alarm.Int64() != TInt64(0))
    {
        // Count alarm value if possible
        if (item->CountValuesL(EPIMToDoDue) > 0)
        {
            const TPIMDate dueDate = item->GetDateL(EPIMToDoDue, 0);
            TTimeIntervalSeconds alarmOffset(0);
            User::LeaveIfError(dueDate.SecondsFrom(alarm, alarmOffset));
            item->AddIntL(EPIMToDoExtAlarm, KPIMAttrNone, alarmOffset.Int());
        }
    }
    if (item->CountValuesL(EPIMToDoCompletionDate) == 0)
    {
        item->AddBooleanL(EPIMToDoCompleted, KPIMAttrNone, EFalse);
    }
    else
    {
        item->AddBooleanL(EPIMToDoCompleted, KPIMAttrNone, ETrue);
    }
    User::LeaveIfError(aItemArray.Append(item));
    CleanupStack::Pop(item); // item
    CleanupStack::Pop(&aItemArray);
}

//  End of File
