/*
* Copyright (c) 2005-2007 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:  Phonebook 2 add new contact field to a contact dialog.
*
*/


#include <CPbk2AddItemToContactDlg.h>

// Phonebook 2
#include <TPbk2AddItemDialogParams.h>
#include <TPbk2AddItemWrapper.h>
#include <Pbk2UIControls.rsg>
#include <CPbk2IconArray.h>
#include <TPbk2AppIconId.h>
#include <CPbk2UIExtensionManager.h>
#include <MPbk2UIExtensionIconSupport.h>
#include <MPbk2AppUi.h>
#include <CPbk2ServiceManager.h>
#include <MPbk2ApplicationServices.h>
#include <MPbk2ApplicationServices2.h>
#include "CPbk2IconInfo.h"
#include <aknlayoutscalable_avkon.cdl.h>
#include <MPbk2ApplicationServices.h>

// System includes
#include <aknPopup.h>
#include <aknlists.h>
#include <barsread.h>


/// Unnamed namespace for local definitions
namespace {

const TInt KMaxListBoxBufferSize = 128;
_LIT(KListBoxIconFormat, "%d\t");

#ifdef _DEBUG

enum TPanicCode
    {
    EPanicPreCond_ResetWhenDestroyed = 1
    };

void Panic( TInt aReason )
    {
    _LIT( KPanicText, "CPbk2AddItemToContactDlg" );
    User::Panic( KPanicText, aReason );
    }

#endif // _DEBUG


/**
 * Phonebook 2 list box model for add item wrappers.
 */
NONSHARABLE_CLASS(CModel) : public CBase,
                            public MDesCArray
    {
    public: // Construction

        /**
         * Constructor.
         *
         * @param aArray        An array of wrappers.
         * @param aIconArray    Icon array.
         * @return  A new instance of this class.
         */
        CModel(
                RArray<TPbk2AddItemWrapper>& aArray,
                CPbk2IconArray& aIconArray );

    public: // From MDesCArray
        TInt MdcaCount() const;
        TPtrC16 MdcaPoint(
                TInt aIndex ) const;

    private: // Implementation
        static TInt Compare(
                const TPbk2AddItemWrapper& aLhs,
                const TPbk2AddItemWrapper& aRhs );

    private: // Data
        /// Ref: Array of TPbk2AddItemWrapper objects to show
        RArray<TPbk2AddItemWrapper>& iArray;
        /// Own: Buffer for formatting a list box row text
        mutable TBuf16<KMaxListBoxBufferSize> iBuffer;
        /// Ref: Icons for field types
        CPbk2IconArray& iIconArray;
    };

// --------------------------------------------------------------------------
// CModel::CModel
// --------------------------------------------------------------------------
//
CModel::CModel(RArray<TPbk2AddItemWrapper>& aArray,
    CPbk2IconArray& aIconArray) :
        iArray(aArray),
        iIconArray(aIconArray)
    {
    iArray.Sort(TLinearOrder<TPbk2AddItemWrapper>(CModel::Compare));
    }

// --------------------------------------------------------------------------
// CModel::MdcaCount
// --------------------------------------------------------------------------
//
TInt CModel::MdcaCount() const
    {
    return iArray.Count();
    }

// --------------------------------------------------------------------------
// CModel::MdcaPoint
// --------------------------------------------------------------------------
//
TPtrC16 CModel::MdcaPoint( TInt aIndex ) const
    {
    // Insert icon index
    TInt iconIndex = iIconArray.FindIcon(iArray[aIndex].IconId());
    if (iconIndex < 0)
        {
        // If icon is not found by Id fall back to empty icon
        iconIndex = iIconArray.FindIcon(
            TPbk2AppIconId(EPbk2qgn_prop_nrtyp_empty));
        }

    iBuffer.Format(KListBoxIconFormat, iconIndex);
    const TDesC& fieldName = iArray[aIndex].Label();
    const TInt chars = Min(iBuffer.MaxLength() - iBuffer.Length(),
        fieldName.Length());
    iBuffer.Append(fieldName.Left(chars));
    return iBuffer;
    }

// --------------------------------------------------------------------------
// CModel::Compare
// --------------------------------------------------------------------------
//
TInt CModel::Compare(   const TPbk2AddItemWrapper& aLhs,
        const TPbk2AddItemWrapper& aRhs )
    {
    return aLhs.AddItemOrdering() - aRhs.AddItemOrdering();
    }

} /// namespace

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::CPbk2AddItemToContactDlg
// --------------------------------------------------------------------------
//
inline CPbk2AddItemToContactDlg::CPbk2AddItemToContactDlg( MPbk2ApplicationServices* aAppServices ) :
	iAppServices( aAppServices )
    {
    // Do nothing
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::~CPbk2AddItemToContactDlg
// --------------------------------------------------------------------------
//
CPbk2AddItemToContactDlg::~CPbk2AddItemToContactDlg()
    {
    // Tells ExecuteLD this object has been destroyed
    if (iDestroyedPtr)
        {
        *iDestroyedPtr = ETrue;
        }

    if ( iSelfPtr )
        {
        *iSelfPtr = NULL;
        }

    if ( iPopup )
        {
        iPopup->CancelPopup();
        }

    delete iListBox;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2AddItemToContactDlg* CPbk2AddItemToContactDlg::NewL()
    {
    CPbk2AddItemToContactDlg* self = new(ELeave) CPbk2AddItemToContactDlg();
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2AddItemToContactDlg* CPbk2AddItemToContactDlg::NewL(
	MPbk2ApplicationServices* aAppServices )
    {
    CPbk2AddItemToContactDlg* self = new(ELeave) CPbk2AddItemToContactDlg( aAppServices );
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::ExecuteLD
// --------------------------------------------------------------------------
//
EXPORT_C TInt CPbk2AddItemToContactDlg::ExecuteLD
        ( RArray<TPbk2AddItemWrapper>& aAddItems,
          const TPbk2AddItemDialogParams* aParams /*= NULL*/ )
    {
    CleanupStack::PushL( this );

    TBool thisDestroyed = EFalse;
    iDestroyedPtr = &thisDestroyed;

    PreparePopupL( aAddItems, aParams );
    TInt result = ExecutePopupL( aParams );

    if ( thisDestroyed )
        {
        // This object has been destroyed
        CleanupStack::Pop( this );
        }
    else
        {
        CleanupStack::PopAndDestroy( this );
        }

    return result;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::RequestExitL
// --------------------------------------------------------------------------
//
void CPbk2AddItemToContactDlg::RequestExitL( TInt /*aCommandId*/ )
    {
    if ( iPopup )
        {
        iPopup->CancelPopup();
        }
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::ForceExit
// --------------------------------------------------------------------------
//
void CPbk2AddItemToContactDlg::ForceExit()
    {
    delete this;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::ResetWhenDestroyed
// --------------------------------------------------------------------------
//
void CPbk2AddItemToContactDlg::ResetWhenDestroyed
        ( MPbk2DialogEliminator** aSelfPtr )
    {
    __ASSERT_DEBUG(!aSelfPtr || *aSelfPtr == this,
        Panic( EPanicPreCond_ResetWhenDestroyed ) );

    iSelfPtr = aSelfPtr;
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::PreparePopupL
// --------------------------------------------------------------------------
//
void CPbk2AddItemToContactDlg::PreparePopupL
        ( RArray<TPbk2AddItemWrapper>& aAddItems,
          const TPbk2AddItemDialogParams* aParams )
    {
    // Create a list box
    delete iListBox;
    iListBox = NULL;
    iListBox = static_cast<CEikFormattedCellListBox*>
        ( EikControlFactory::CreateByTypeL
                ( EAknCtSingleGraphicPopupMenuListBox ).iControl );

    TInt cbaResource = R_AVKON_SOFTKEYS_OK_CANCEL__OK;
    if (aParams && aParams->iCbaResource)
        {
        cbaResource = aParams->iCbaResource;
        }

    // Create a popup list
    if ( iPopup )
        {
        iPopup->CancelPopup();
        iPopup = NULL;
        }
    iPopup = CAknPopupList::NewL( iListBox,
        cbaResource, AknPopupLayouts::EMenuGraphicWindow );

    if ( aParams && aParams->iTitle && aParams->iTitle->Length() > 0 )
        {
        iPopup->SetTitleL( *aParams->iTitle );
        }

    // Init list box
    iListBox->ConstructL( iPopup, CEikListBox::ELeftDownInViewRect );
    iListBox->CreateScrollBarFrameL( ETrue );
    iListBox->ScrollBarFrame()->SetScrollBarVisibilityL
        ( CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto );

    // Create icon array
    TResourceReader reader;
    CCoeEnv::Static()->CreateResourceReaderLC
        ( reader, R_PBK2_FIELDTYPE_ICONS );
    CPbk2IconArray* iconArray = CPbk2IconArray::NewL(reader);
    CleanupStack::PopAndDestroy(); // reader
    CleanupStack::PushL( iconArray );
    
    //Calculate preferred size for xsp service icons 
    TRect mainPane;
    AknLayoutUtils::LayoutMetricsRect(
        AknLayoutUtils::EMainPane, mainPane );
    TAknLayoutRect listLayoutRect;
    listLayoutRect.LayoutRect(
        mainPane,
        AknLayoutScalable_Avkon::list_single_graphic_pane_g2(0).LayoutLine() );
    TSize size(listLayoutRect.Rect().Size());
    
    MPbk2ApplicationServices2* servicesExtension = NULL ;
    
    /*
	 * Use iAppServices if provided. This is to enable editor use outside from pbk2 context
	 */
	if( iAppServices )
		{
		servicesExtension = reinterpret_cast<MPbk2ApplicationServices2*>
			( iAppServices->MPbk2ApplicationServicesExtension( KMPbk2ApplicationServicesExtension2Uid ) );
		}
	else
		{
		// Add xsp service icons
		servicesExtension =
			reinterpret_cast<MPbk2ApplicationServices2*>
				( Phonebook2::Pbk2AppUi()->ApplicationServices().
					MPbk2ApplicationServicesExtension(
						KMPbk2ApplicationServicesExtension2Uid ) );               
		}
	
    CPbk2ServiceManager& servMan = servicesExtension->ServiceManager();
    const CPbk2ServiceManager::RServicesArray& services = servMan.Services();
    TUid uid;
    uid.iUid = KPbk2ServManId;
    for ( TInt i = 0; i < services.Count(); i++ )
        {
        const CPbk2ServiceManager::TService& service = services[i];
        if ( service.iBitmapId && service.iBitmap )
            {
        	AknIconUtils::SetSize(
        	           service.iBitmap,
        	           size );
        	AknIconUtils::SetSize(
        	           service.iMask,
        	           size );            
            TPbk2IconId id = TPbk2IconId( uid, services[i].iBitmapId );
            CPbk2IconInfo* info = CPbk2IconInfo::NewLC(
            	id, services[i].iBitmap, services[i].iMask, size );
            iconArray->AppendIconL(info);
            CleanupStack::Pop(info);
            }
        }

    // Add icons from UI extensions
    CPbk2UIExtensionManager* extManager =
        CPbk2UIExtensionManager::InstanceL();
    extManager->PushL();
    extManager->IconSupportL().AppendExtensionIconsL( *iconArray );
    CleanupStack::PopAndDestroy(); // extManager
    CleanupStack::Pop( iconArray );

    // Set icon array
    iListBox->ItemDrawer()->ColumnData()->SetIconArray( iconArray );

    // Create listbox model
    CModel* listBoxModel = new(ELeave) CModel( aAddItems, *iconArray );
    iListBox->Model()->SetItemTextArray(listBoxModel);
    iListBox->Model()->SetOwnershipType(ELbmOwnsItemArray);
    }

// --------------------------------------------------------------------------
// CPbk2AddItemToContactDlg::ExecutePopupL
// --------------------------------------------------------------------------
//
TInt CPbk2AddItemToContactDlg::ExecutePopupL
        ( const TPbk2AddItemDialogParams* aParams )
    {
    TInt result = KErrCancel;

    if ( aParams && aParams->iSelectionIndex >= 0 )
        {
        // Silent mode, the selection has already been made
        // Result is the same index as was given in parameters,
        // the aAddItems array got sorted when creating the list box model
        // so the returned index value is valid now
        result = aParams->iSelectionIndex;
        }
    else
        {
        // iDestroyedPtr points to ExecuteLD's thisDestroyed local variable,
        // and we need another destruction check here. We cannot copy the
        // solution from ExecuteLD since making iDestroyedPtr point to a
        // local variable defined in this function rather than the
        // variable of ExecuteLD causes the destructor change the value of
        // a variable that is out of scope and it will lead to a crash
        // eventually. So thisDestroyed now points to the
        // ExecuteLD::thisDestroyed and it is still in scope in destructor.
        TBool* thisDestroyed = iDestroyedPtr;

        TBool popupAccepted = iPopup->ExecuteLD();
        iPopup = NULL;

        if ( popupAccepted && *thisDestroyed == EFalse )
            {
            // Retrieve and return selection
            TInt index = iListBox->CurrentItemIndex();
            if ( index >= KErrNone )
                {
                result = index;
                }
            }
        }

    return result;
    }

// End of File
