/*
* 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:  
*     Container control class that will contain a Bio Control.
*  %version : %
*
*/



// INCLUDE FILES
#include <msgbiocontrol.h>
#include <MsgBioUids.h>
#include <msgbiocontrolfactory.h>
#include <bva.rsg>                  // resouce identifiers
#include "BvaAppUi.h"
#include "BvaContainer.h"
#include "bvalog.h"

#include <AknsBasicBackgroundControlContext.h>
#include <AknsDrawUtils.h>
#include <featmgr.h>				// Feature manager

#include <AknUtils.h>                   // AknLayoutUtils
#include <aknlayoutscalable_apps.cdl.h> // LAF
#include <MsgEditorCommon.h>


//  LOCAL CONSTANTS AND MACROS

_LIT8(KBeginVCard,          "BEGIN:VCARD");
_LIT8(KBeginVCalendar,      "BEGIN:VCALENDAR");
_LIT8(KICalVersion,      	"VERSION:2.0");

_LIT(KBvaContainer,"CBvaContainer");

const TInt KNumberOfControls = 1;

// This is an estimate based on observed userid lengths in iCal messages
const TInt KICalLengthNeeded = 255;

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

// ---------------------------------------------------------
// Symbian OS two phased constructor
// ---------------------------------------------------------
//
void CBvaContainer::ConstructL(const TRect& aRect)
    {
    LOG("CBvaContainer::ConstructL begin");
    FeatureManager::InitializeLibL();

    CreateWindowL();
    iBgContext = CAknsBasicBackgroundControlContext::NewL(
            KAknsIIDQsnBgAreaMainMessage, aRect, ETrue);

    SetSizeWithoutNotification(aRect.Size());
    SetBlank();
    CreateScrollBarL();
    iLineHeight = MsgEditorCommons::MsgBaseLineDelta();
    iBaseLineOffset = MsgEditorCommons::MsgBaseLineOffset();
    LOG("CBvaContainer::ConstructL end");
    }

void CBvaContainer::LoadBioControlL( const RFile& aFile )
    {
    LOG("CBvaContainer::LoadBioControlL begin");
    SetRect(iEikonEnv->EikAppUi()->ClientRect());
    ActivateL();
    delete iFactory;
    iFactory = NULL;
    iFactory = CMsgBioControlFactory::NewL();

    delete iBioControl;
    iBioControl = NULL;

    TUid bioUid = BioUidL( aFile );

    TRAPD(ret,iBioControl = iFactory->CreateControlL(
    	*static_cast<CBvaAppUi*>(iEikonEnv->EikAppUi()),
        bioUid,
        EMsgBioViewerMode,
        aFile ));

    if(ret == KErrNotFound && bioUid == KMsgBioUidICalendar)
        {
        iBioControl = iFactory->CreateControlL(
        *static_cast<CBvaAppUi*>(iEikonEnv->EikAppUi()),
        KMsgBioUidVCalendar,
        EMsgBioViewerMode,
        aFile );
        }
    else if(ret != KErrNone)
        {
        User::Leave(ret);
        }


    iBioControl->SetContainerWindowL(*this);
    iBioControl->ActivateL();
    UpdateScrollBarL();
    TSize size = Size();
    TInt scrollBarWidth = iScrollBar->ScrollBarBreadth(CEikScrollBar::EVertical);
    size.iWidth -= scrollBarWidth;
    iBioControl->SetAndGetSizeL(size);
    iBioControl->SetExtent(TPoint(0,0),iBioControl->Size());

    UpdateScrollBarL();
    DrawNow();
    LOG("CBvaContainer::LoadBioControlL end");
    }

// Destructor
CBvaContainer::~CBvaContainer()
    {
    FeatureManager::UnInitializeLib();
    delete iBioControl;
    delete iFactory;
    delete iScrollBar;
    delete iBgContext;
    }

void CBvaContainer::SizeChanged()
    {
    if ( iBgContext )
        {
        iBgContext->SetRect( Rect() );
        iBgContext->SetParentPos( PositionRelativeToScreen() );
       }
    if( iBioControl )
    	{
    	//the scrollbar width is deducted from the viewer width
    	//this ensures that the scrollbar is not blocked by it
    	TSize size = iEikonEnv->EikAppUi()->ClientRect().Size();
    	TInt scrollBarWidth = iScrollBar->ScrollBarBreadth(CEikScrollBar::EVertical);
    	size.iWidth -= scrollBarWidth;
    	TRAP_IGNORE(iBioControl->SetAndGetSizeL(size));
    	iBioControl->SetExtent(TPoint(0,0), iBioControl->Size());
    	TRAP_IGNORE( UpdateScrollBarL() );
    	}
    }

TInt CBvaContainer::CountComponentControls() const
    {
    TInt countScrollBarComponents( 0 );
    if ( iScrollBar )
        {
        countScrollBarComponents = iScrollBar->CountComponentControls();
        }
    if (!iBioControl)
        {
        return countScrollBarComponents;
        }
    return countScrollBarComponents + KNumberOfControls ;
    }

CCoeControl* CBvaContainer::ComponentControl(TInt aIndex) const
    {
    TInt countScrollBarComponents( 0 );
    if ( iScrollBar )
        {
        countScrollBarComponents = iScrollBar->CountComponentControls();
        }

    switch ( aIndex )
        {
        case 0:
            {
            if( iBioControl )
                {
                return iBioControl;
                }
            else if( iScrollBar )
                {
                return iScrollBar->ComponentControl( aIndex );
                }
            }
        default:
            if (    iScrollBar
                &&  aIndex >= KNumberOfControls
                &&  aIndex < countScrollBarComponents + KNumberOfControls )
                {
                return iScrollBar->ComponentControl( aIndex - KNumberOfControls );
                }
            else
                {
                }
            return NULL;
         }
    }

TTypeUid::Ptr CBvaContainer::MopSupplyObject(TTypeUid aId)
	{
    if (aId.iUid == MAknsControlContext::ETypeId)
        {
        return MAknsControlContext::SupplyMopObject( aId, iBgContext );
        }
    return CCoeControl::MopSupplyObject(aId);
	}

TKeyResponse CBvaContainer::OfferKeyEventL(
    const TKeyEvent& aKeyEvent,
    TEventCode aType)
    {
    TKeyResponse keyResp( EKeyWasNotConsumed );
    if (!iBioControl)
        {
        return EKeyWasNotConsumed;
        }
    if(aKeyEvent.iCode == EKeyEnter	)
    {
    iEikonEnv->EikAppUi()->HandleCommandL(EAknSoftkeyContextOptions);	
    return EKeyWasConsumed;
    }
    keyResp = iBioControl->OfferKeyEventL(aKeyEvent, aType);
    UpdateScrollBarL();
    return keyResp;
    }

CMsgBioControl& CBvaContainer::BioControl()
    {
    __ASSERT_DEBUG(iBioControl, Panic(EBioControlNotExist));
    return *iBioControl;
    }

TUid CBvaContainer::BioUidL(const TFileName& aFileName, RFs& aFs )
    {
    RFile file;

    User::LeaveIfError(file.Open(aFs,aFileName,EFileRead));
    CleanupClosePushL(file); // file on CS
    return BioUidL( file );
    }


 TUid CBvaContainer::BioUidL( const RFile& aFile )
 	{
 	LOG("CBvaContainer::BioUidL begin");
 	TInt size;
    User::LeaveIfError(aFile.Size(size));
    if (!size)
        {
        // File reading failed.
        User::Leave(KErrNotFound);
        }

    HBufC8* dataBuf = HBufC8::NewLC(KICalLengthNeeded); // dataBuf on CS
    TPtr8 data(dataBuf->Des());
    User::LeaveIfError(aFile.Read(data, KICalLengthNeeded));
	TInt lengthNeeded = KBeginVCalendar().Length();

    // Compare file length to text string
    TInt length = dataBuf->Length();
    if (length < lengthNeeded)
        {
        User::Leave(KErrCorrupt);
        }

    // Compare beginning of read buffer with text strings.
    if ( dataBuf->Left( KBeginVCard().Length() ).CompareF(KBeginVCard) == 0)
        {
        CleanupStack::PopAndDestroy(dataBuf); // dataBuf
        return KMsgBioUidVCard;
        }
    else if (
    		dataBuf->FindF(KICalVersion) != KErrNotFound &&
        	dataBuf->FindF(KBeginVCalendar) != KErrNotFound
        	)
        {
        CleanupStack::PopAndDestroy(dataBuf); // dataBuf

        if ( FeatureManager::FeatureSupported( KFeatureIdMeetingRequestSupport ) )
        	{
          	return KMsgBioUidICalendar;	// we use ICalBC to launch the Meeting Request Viewers
        	}
      	else
        	{
          	return KMsgBioUidVCalendar;	// VCalBC is used to open iCal message
          	}
        }
    else if (
        dataBuf->FindF(KICalVersion) == KErrNotFound &&
        dataBuf->FindF(KBeginVCalendar) != KErrNotFound
        )
        {
        CleanupStack::PopAndDestroy(dataBuf); // dataBuf
        return KMsgBioUidVCalendar;
        }
    CleanupStack::PopAndDestroy(dataBuf); // dataBuf
    LOG("CBvaContainer::BioUidL end");
    User::Leave(KErrNotSupported);
    // Never reached.  A compilation warning is avoided if null TUid returned.
    return TUid::Null();
    }

void CBvaContainer::Panic(TInt aCode) const
    {
    User::Panic(KBvaContainer, aCode);
    }

void CBvaContainer::Draw(const TRect& aRect) const
	{
	CWindowGc& gc = SystemGc();

    MAknsSkinInstance* skin = AknsUtils::SkinInstance();
    MAknsControlContext* cc = AknsDrawUtils::ControlContext( this );

    if( !AknsDrawUtils::Background( skin, cc, this, gc, aRect ) )
    	{
        // Same as CCoeControl draw for blank controls
        CGraphicsContext& gcBlank = SystemGc();
        gcBlank.SetPenStyle( CGraphicsContext::ENullPen );
        gcBlank.SetBrushStyle( CGraphicsContext::ESolidBrush );
        gcBlank.DrawRect( aRect );
      }
	}

void CBvaContainer::HandleResourceChange( TInt aType )
	{
    CCoeControl::HandleResourceChange( aType );
    
    switch( aType )
		{
        case KEikDynamicLayoutVariantSwitch:
        	{
        	iLineHeight = MsgEditorCommons::MsgBaseLineDelta();
            iBaseLineOffset = MsgEditorCommons::MsgBaseLineOffset();

        	SetRect(iEikonEnv->EikAppUi()->ClientRect());

        	AknLayoutUtils::LayoutVerticalScrollBar(
                iScrollBar,
                TRect( TPoint( 0, 0 ), iEikonEnv->EikAppUi()->ClientRect().Size() ),
                AknLayoutScalable_Apps::scroll_pane_cp017().LayoutLine() );
    		SizeChanged();
			break;
			}
        default:
            // fall through
            break;
		}
	}

TBool CBvaContainer::IsBioControl()
    {
    if(iBioControl)
        return ETrue;
    else
        return EFalse;
    }

void CBvaContainer::CreateScrollBarL()
    {
    iScrollBar = new ( ELeave ) CEikScrollBarFrame(
        this,  // CCoeControl* aParentWindow
        this,  // MEikScrollBarObserver* aObserver
        ETrue  // TBool aPreAlloc=EFalse
        );

    // Check which type of scrollbar is to be shown
    CAknAppUiBase* appUi = static_cast<CAknAppUiBase*>( iEikonEnv->EikAppUi() );

    if ( AknLayoutUtils::DefaultScrollBarType( appUi ) ==
        CEikScrollBarFrame::EDoubleSpan )
        {
        // For EDoubleSpan type scrollbar
        // non-window owning scrollbar
        iScrollBar->CreateDoubleSpanScrollBarsL( EFalse, EFalse, ETrue, EFalse );
        iScrollBar->SetTypeOfVScrollBar( CEikScrollBarFrame::EDoubleSpan );
        AknLayoutUtils::LayoutVerticalScrollBar(
            iScrollBar,
            TRect( TPoint( 0, 0 ), iEikonEnv->EikAppUi()->ClientRect().Size() ),
            AknLayoutScalable_Apps::scroll_pane_cp017().LayoutLine() );
        }
    else
        {
        // For EArrowHead type scrollbar
        iScrollBar->SetTypeOfVScrollBar( CEikScrollBarFrame::EArrowHead );
        }

    iScrollBar->SetScrollBarVisibilityL(
        CEikScrollBarFrame::EOff, CEikScrollBarFrame::EAuto );
    }

void CBvaContainer::UpdateScrollBarL()
    {
    TEikScrollBarModel horzModel;
    TEikScrollBarModel vertModel;
    TInt height;
    TInt pos;

    GetVirtualFormHeightAndPos( height, pos );

#ifdef _DEBUG
    // CAknScrollIndicator::SetPosition has an __ASSERT_DEBUG
    // for range check even if the control handles out-of-range
    // values properly.
    if ( pos > height ) pos = height;
    if ( pos < -1 )     pos = -1;
#endif

    vertModel.iScrollSpan = height;
    vertModel.iThumbSpan  = iEikonEnv->EikAppUi()->ClientRect().Height();

    if ( height > iEikonEnv->EikAppUi()->ClientRect().Height() )
        {
        vertModel.iThumbPosition = pos;
        }
    else
        {
        vertModel.iThumbPosition = 0;
        }

    TEikScrollBarFrameLayout layout;

    if ( iScrollBar && iScrollBar->TypeOfVScrollBar() == CEikScrollBarFrame::EDoubleSpan )
        {
        // For EDoubleSpan type scrollbar
        if ( vertModel.iScrollSpan == vertModel.iThumbPosition )
            {
            vertModel.iThumbPosition--;
            }

        TAknDoubleSpanScrollBarModel hDsSbarModel( horzModel );
        TAknDoubleSpanScrollBarModel vDsSbarModel( vertModel );
        layout.iTilingMode = TEikScrollBarFrameLayout::EInclusiveRectConstant;

        // It seems to be important that we have separate
        // variable for "inclusiveRect" and "clientRect"
        TRect inclusiveRect( Rect() );
        TRect clientRect( Rect() );

        iScrollBar->TileL( &hDsSbarModel, &vDsSbarModel, clientRect, inclusiveRect, layout );
        iScrollBar->SetVFocusPosToThumbPos( vDsSbarModel.FocusPosition() );
        }
    else
        {
        TRect rect( Rect() );
        iScrollBar->TileL( &horzModel, &vertModel, rect, rect, layout );
        iScrollBar->SetVFocusPosToThumbPos( vertModel.iThumbPosition );
        }
    }

void CBvaContainer::GetVirtualFormHeightAndPos( TInt& aHeight, TInt& aPos )
    {
    aHeight = iBioControl->VirtualHeight();
    aPos = iBioControl->VirtualVisibleTop();
    }

#ifdef RD_SCALABLE_UI_V2
void CBvaContainer::HandleScrollEventL(CEikScrollBar* aScrollBar, TEikScrollEvent aEventType)
    {
    switch ( aEventType )
        {
        case EEikScrollUp:
            {
            //ensure that we scroll 2 lines
            ScrollViewL(iLineHeight*2 , EMsgScrollUp, ETrue );
            break;
            }
        case EEikScrollDown:
            {
            //ensure that we scroll 2 lines
            ScrollViewL( iLineHeight*2, EMsgScrollDown, ETrue );
            break;
            }
        case EEikScrollTop:
            {
            // Not supported yet.
            break;
            }
        case EEikScrollBottom:
            {
            // Not supported yet.
            break;
            }
        case EEikScrollThumbReleaseVert:
            {
            // Not implemented yet
            break;
            }
        case EEikScrollPageUp:
        case EEikScrollPageDown:
        case EEikScrollThumbDragVert:
            {
            TInt scrolledPixels(iBioControl->VirtualVisibleTop() - aScrollBar->ThumbPosition());

			//the scrolled amount have to be manipulated to be atleast
			//one line height of pixels
			if(aScrollBar->ThumbPosition() + iBioControl->VirtualVisibleTop() <=
				iBioControl->VirtualHeight() - iLineHeight)
				{
            	if(Abs(scrolledPixels) >= iLineHeight)
                	{
                	scrolledPixels += ( scrolledPixels % (iLineHeight));
                	}
				else
					{
                	scrolledPixels -= iLineHeight;
                	}
				}
			else
				{
				scrolledPixels -= (iLineHeight);
				}

            if ( scrolledPixels != 0 )
                {
                ScrollViewL( Abs( scrolledPixels ),
                             scrolledPixels > 0 ? EMsgScrollUp :
                                                  EMsgScrollDown,
                             EFalse );
				UpdateScrollBarL();
                }
            break;
            }
        default:
            {
            break;
            }
        }
    }
#else
void CBvaContainer::HandleScrollEventL( CEikScrollBar* /*aScrollBar*/,
                                        TEikScrollEvent /*aEventType*/)
    {
    }
#endif //RD_SCALABLE_UI_V2

const TAknDoubleSpanScrollBarModel* CBvaContainer::AknScrollBarModel() const
    {
    return static_cast<const TAknDoubleSpanScrollBarModel*>( iScrollBar->VerticalScrollBar()->Model() );
    }

#ifdef RD_SCALABLE_UI_V2
void CBvaContainer::ScrollViewL( TInt aPixelsToScroll,
                                  TMsgScrollDirection aDirection,
                                  TBool aMoveThumb )
    {
    if ( aMoveThumb )
        {
        const TAknDoubleSpanScrollBarModel* model = AknScrollBarModel();

        TInt directionMultiplier( -1 );
        if ( aDirection == EMsgScrollDown )
            {
            directionMultiplier = 1;
            }
        // Scroll bar thumb is moved only if caller requests it.
        iScrollBar->SetVFocusPosToThumbPos( model->FocusPosition() - 1 +
                                                directionMultiplier * aPixelsToScroll );
        }

    iBioControl->ScrollL( aPixelsToScroll, aDirection );
    }
#else
void CBvaContainer::ScrollViewL( TInt /*aPixelsToScroll*/,
                                  TMsgScrollDirection /*aDirection*/,
                                  TBool /*aMoveThumb*/ )
    {
    }
#endif //RD_SCALABLE_UI_V2

// End of File
