/*
* Copyright (c) 2002 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: 
*           Methods for Phonebook Contact editor field array.
*
*/


// INCLUDE FILES
#include "CPbkContactEditorFieldArray.h"  // this class

#include "MPbkContactEditorUiBuilder.h"
#include <MPbkContactEditorField.h>
#include "PbkContactEditorFieldFactory.h"
#include "PbkFieldFormatterFactory.h"
#include "CPbkContactItem.h"
#include "MPbkPhoneNumberFormat.h"
#include "PbkIconInfo.h"
#include "CPbkFieldInfo.h"
#include "CPbkReadingEditorBinderVisitor.h"
#include <PbkView.rsg>

#include <CPbkExtGlobals.h>
#include <MPbkExtensionFactory.h>

#include <PbkDebug.h>

/// Unnamed namespace for local definitons
namespace {

// LOCAL CONSTANTS AND MACROS

const TInt KArrayGranularity = 5;

#ifdef _DEBUG
/// Panic codes for CPbkContactEditorFieldArray
enum TPanicCode
    {
	EPanicPostCond_ConstructL,
	EPanicInvariant_FieldNotFound,
	EPanicInvariant_MembersNotCreated,
	EPanicPreCond_CreateFieldsFromContactL,
	EPanicPreCond_DoAddFieldL,
	EPanicInvariant_CountMismatch
    };
#endif  // _DEBUG

// ==================== LOCAL FUNCTIONS ====================

#ifdef _DEBUG
void Panic(TPanicCode aReason)
    {
    _LIT(KPanicText, "CPbkContactEditorFieldArray");
    User::Panic(KPanicText, aReason);
    }
#endif  // _DEBUG

}  // namespace


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

inline CPbkContactEditorFieldArray::CPbkContactEditorFieldArray
        (CPbkContactItem& aContactItem, 
        MPbkContactEditorUiBuilder& aUiBuilder) :
    iContactItem(aContactItem),
    iUiBuilder(aUiBuilder),
    iFieldArray(KArrayGranularity)
    {
    }

inline void CPbkContactEditorFieldArray::ConstructL()
    {
    iIconInfoContainer = CPbkIconInfoContainer::NewL(R_PBK_ICON_INFO_ARRAY);

    // Get extension factory for setting extension icons
    CPbkExtGlobals* extGlobal = CPbkExtGlobals::InstanceL();
    extGlobal->PushL();
    MPbkExtensionFactory& factory = extGlobal->FactoryL();    
    factory.AddPbkFieldIconsL(iIconInfoContainer);
    CleanupStack::PopAndDestroy(extGlobal);

    // Construct the reading field binder (visitor pattern)
    iReadingFieldBinder = CPbkReadingEditorBinderVisitor::NewL();

    __TEST_INVARIANT;

    //PostCond:
    __ASSERT_DEBUG(iIconInfoContainer, 
            Panic(EPanicPostCond_ConstructL));
    }

CPbkContactEditorFieldArray* CPbkContactEditorFieldArray::NewL
        (CPbkContactItem& aContactItem, 
        MPbkContactEditorUiBuilder& aUiBuilder)
    {
	CPbkContactEditorFieldArray* dlg = new(ELeave) CPbkContactEditorFieldArray(aContactItem, aUiBuilder);
    CleanupStack::PushL(dlg);
    dlg->ConstructL();
    CleanupStack::Pop();  // dlg
    return dlg;
    }

CPbkContactEditorFieldArray::~CPbkContactEditorFieldArray()
    {
	__TEST_INVARIANT;

    PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CPbkContactEditorFieldArray::~CPbkContactEditorFieldArray"));

    // delete member data
    delete iReadingFieldBinder;
    delete iIconInfoContainer;

    iFieldArray.ResetAndDestroy();
    }

void CPbkContactEditorFieldArray::CreateFieldsFromContactL()
    {
	__ASSERT_DEBUG(iFieldArray.Count() == 0, Panic(EPanicPreCond_CreateFieldsFromContactL));

	__TEST_INVARIANT;

    CPbkFieldArray& fieldSet = iContactItem.CardFields();
    const TInt fieldCount = fieldSet.Count();
    for (TInt i=0; i < fieldCount; ++i)
        {
        TPbkContactItemField& contactItemField = fieldSet[i];
        if (contactItemField.FieldInfo().IsEditable())
            {
	        MPbkContactEditorField* field = PbkContactEditorFieldFactory::CreateFieldL(
                    contactItemField, iUiBuilder, *iIconInfoContainer);
            iFieldArray.InsertL(iFieldArray.Count(), field);
            }
        }

    AcceptL(*iReadingFieldBinder);

	__TEST_INVARIANT;
    }

void CPbkContactEditorFieldArray::AcceptL(MPbkFieldEditorVisitor& aVisitor)
    {
    const TInt count = iFieldArray.Count();
    for (TInt i = 0; i < count; ++i)
        {
        iFieldArray[i]->AcceptL(aVisitor);
        }
    }

TBool CPbkContactEditorFieldArray::AreAllFieldsEmpty() const
    {
	__TEST_INVARIANT;

	TBool result = ETrue;
    const CPbkFieldArray& fields = iContactItem.CardFields();
    const TInt fieldCount = fields.Count();
    for (TInt i=0; i < fieldCount; ++i)
        {
        const TPbkContactItemField& contactItemField = fields[i];
        if ( !contactItemField.IsEmptyOrAllSpaces() &&
                // The Synchronization field does not count here
                contactItemField.FieldInfo().FieldId()
                != EPbkFieldIdSyncronization )
            {
			result = EFalse;
			break;
            }
        }

	__TEST_INVARIANT;

    return result;
    }

void CPbkContactEditorFieldArray::AddFieldL
        (TPbkContactItemField& aField)
    {
	__TEST_INVARIANT;

	DoAddFieldL(aField, iFieldArray.Count());

	__TEST_INVARIANT;
    }

void CPbkContactEditorFieldArray::RemoveField
        (MPbkContactEditorField& aField)
    {
	__TEST_INVARIANT;

	const TInt count = iFieldArray.Count();
    for (TInt i = 0; i < count; ++i)
        {
        MPbkContactEditorField* field = iFieldArray[i];
        if (field == &aField)
            {
            iReadingFieldBinder->NotifyEditorDeletion( field->FieldId() );
            iUiBuilder.DeleteControl(field->ControlId());     
            iFieldArray.Delete(i);
			iContactItem.RemoveField(
				iContactItem.FindFieldIndex(aField.ContactItemField()));
			delete field;
			break;
            }
        }

	__TEST_INVARIANT;
    }

TInt CPbkContactEditorFieldArray::ContactItemFieldCount() const
    {
	__TEST_INVARIANT;

    return iContactItem.CardFields().Count();
    }

TInt CPbkContactEditorFieldArray::EditorCount() const
    {
	__TEST_INVARIANT;

    return iFieldArray.Count();
    }

const MPbkContactEditorField& CPbkContactEditorFieldArray::FieldAt
		(TInt aFieldIndex)
	{
	__TEST_INVARIANT;

	return *iFieldArray[aFieldIndex];
	}

MPbkContactEditorField* CPbkContactEditorFieldArray::Find
        (const TPbkContactItemField& aField) const
    {
	__TEST_INVARIANT;

    MPbkContactEditorField* result = NULL;
	const TInt count = iFieldArray.Count();
    for (TInt i = 0; i < count; ++i)
        {
        if (iFieldArray[i]->ContactItemField().IsSame(aField))
            {
            result = iFieldArray[i];
            break;
            }
        }

	__TEST_INVARIANT;
    return result;
    }

MPbkContactEditorField* CPbkContactEditorFieldArray::Find
        (TInt aControlId) const
    {
	__TEST_INVARIANT;

    MPbkContactEditorField* field = NULL;
	const TInt count = iFieldArray.Count();
    for (TInt i = 0; i < count; ++i)
        {
        if (iFieldArray[i]->ControlId() == aControlId)
            {
            field = iFieldArray[i];
            break;
            }
        }

	__TEST_INVARIANT;
    return field;
    }

TInt CPbkContactEditorFieldArray::FindContactItemFieldIndex
        (const TPbkContactItemField& aField) const
    {
	__TEST_INVARIANT;

    return iContactItem.FindFieldIndex(aField);
    }

void CPbkContactEditorFieldArray::SaveFieldsL()
    {
	__TEST_INVARIANT;

    for (TInt i = 0; i < iFieldArray.Count(); ++i)
        {
        iFieldArray[i]->SaveFieldL();
        }

	__TEST_INVARIANT;
	}

TBool CPbkContactEditorFieldArray::FieldsChanged() const
    {
	__TEST_INVARIANT;

	TBool result = EFalse;
    for (TInt i = 0; i < iFieldArray.Count(); ++i)
        {
        if (iFieldArray[i]->FieldDataChanged())
			{
			result = ETrue;
			}
        // can break because at least one field has changed
        if (result)
            {
            break;
            }
        }

	__TEST_INVARIANT;
    return result;
    }

void CPbkContactEditorFieldArray::SetFocusToIndex
		(TInt aFocusIndex)
	{
	__TEST_INVARIANT;

	MPbkContactEditorField* field = Find(iContactItem.CardFields()[aFocusIndex]);
	TInt err = KErrNotFound;
	if (field)
		{
		TRAP(err, iUiBuilder.TryChangeFocusL(field->ControlId()));
		}

	for (TInt i = 0; i < iFieldArray.Count() && err!=KErrNone; ++i)
		{
		if (iUiBuilder.Control(iFieldArray[i]->ControlId()))
			{
			TRAP(err,iUiBuilder.TryChangeFocusL(iFieldArray[i]->ControlId()));
			}
		}

	__TEST_INVARIANT;
	}

MPbkContactEditorField& CPbkContactEditorFieldArray::CreateFieldL
		(CPbkFieldInfo& aFieldInfo)
	{
	__TEST_INVARIANT;

	TPbkContactItemField& newField = iContactItem.AddFieldL(aFieldInfo);
	
	// Find field from iContact item. 
	// Its editable field index is the insertion position
	TInt fieldIndex = 0;
	for (TInt i = 0; i < iContactItem.CardFields().Count(); ++i)
		{
		const TPbkContactItemField& field = iContactItem.CardFields()[i];
		const TBool match = field.IsSame(newField);
		if(field.FieldInfo().IsEditable() && !match)
			{
			fieldIndex++;
			}
		if (match)
			{
			break;
			}
		}
		
	MPbkContactEditorField& addedField = DoAddFieldL(newField, fieldIndex);
    addedField.AcceptL(*iReadingFieldBinder);
    addedField.ActivateL();
	
	__TEST_INVARIANT;

	return addedField;
	}

/**
 * Not class invariant compliant.
 */
MPbkContactEditorField& CPbkContactEditorFieldArray::DoAddFieldL
        (TPbkContactItemField& aField,
		TInt aFieldInsertIndex)
    {
	__ASSERT_DEBUG(aFieldInsertIndex <= iFieldArray.Count(), 
		Panic(EPanicPreCond_DoAddFieldL));

    // create same order in UI
	if (aFieldInsertIndex > 0)
		{
		// Focus must be set to the field preceeding the insertion point.
		// Please note that this means it is not possible to insert to the 
		// first dialog position!
		const TInt controlId = iFieldArray[aFieldInsertIndex-1]->ControlId();
		iUiBuilder.TryChangeFocusL(controlId);
		}

	MPbkContactEditorField* field = PbkContactEditorFieldFactory::CreateFieldL(
            aField, iUiBuilder, *iIconInfoContainer);
    iFieldArray.InsertL(aFieldInsertIndex, field);
	return *field;
    }

void CPbkContactEditorFieldArray::__DbgTestInvariant() const
	{
	#if defined(_DEBUG)

	__ASSERT_DEBUG(iFieldArray.Count() <= iContactItem.CardFields().Count(), 
			Panic(EPanicInvariant_CountMismatch));

	const TInt count = iFieldArray.Count();
	for (TInt i = 0; i < count; ++i)
		{
		TBool result = EFalse;
		for (TInt j = 0; j < iContactItem.CardFields().Count() && !result; ++j)
			{
			TPbkContactItemField& field = iContactItem.CardFields()[j];
			if (&field.ContactItemField() == &iFieldArray[j]->ContactItemField().ContactItemField() &&
				field.FieldInfo().IsEditable())
				{
				result = ETrue;
				}
			}
		__ASSERT_DEBUG(result, Panic(EPanicInvariant_FieldNotFound));
		}

    __ASSERT_DEBUG(iIconInfoContainer, 
            Panic(EPanicInvariant_MembersNotCreated));

	#endif
	}

// End of File
