/*
* 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:  Taskswitcher Fast Swap area UI
 *
*/

#include <w32std.h>
#include <eikenv.h>
#include <layoutmetadata.cdl.h>
#include <touchfeedback.h>
#include <akntransitionutils.h>
#include <akntranseffect.h>
#include <aknlayoutscalable_apps.cdl.h>
#include <bitmaptransforms.h>


#include "tsfastswapareautils.h"
#include "tsfastswaparea.h"
#include "tsappui.h"
#include "tsfswentry.h"


const TInt KLayoutItemCount = 4;

 /* ================================================================================
  * CTsFastSwapTimer
  * ================================================================================
  */

// -----------------------------------------------------------------------------
// CTsFastSwapTimer::CTsFastSwapTimer
// -----------------------------------------------------------------------------
//
CTsFastSwapTimer::CTsFastSwapTimer( MTsFastSwapTimerObserver& aObserver )
: CTimer( EPriorityStandard ),
  iObserver( &aObserver )
    {
    }


// -----------------------------------------------------------------------------
// CTsFastSwapTimer::CTsGridHighlightTimer
// -----------------------------------------------------------------------------
//
CTsFastSwapTimer::~CTsFastSwapTimer()
    {
    Cancel();
    }


// -----------------------------------------------------------------------------
// CTsFastSwapTimer::ConstructL
// -----------------------------------------------------------------------------
//
void CTsFastSwapTimer::ConstructL()
    {
    CTimer::ConstructL();
    CActiveScheduler::Add( this );
    }


// -----------------------------------------------------------------------------
// CTsFastSwapTimer::CTsGridHighlightTimer
// -----------------------------------------------------------------------------
//
void CTsFastSwapTimer::RunL()
    {
    iObserver->TimerCompletedL(this);
    }
 


/* ================================================================================
 * CTsBitmapScaleTask
 * ================================================================================
 */

// -----------------------------------------------------------------------------
// CTsBitmapScaleTask::
// -----------------------------------------------------------------------------
//
CTsBitmapScaleTask::CTsBitmapScaleTask( CTsFastSwapAreaExtension& aObserver )
: CActive(EPriorityStandard),
  iObserver( aObserver )
    {
    CActiveScheduler::Add( this );
    }


// -----------------------------------------------------------------------------
// CTsBitmapScaleTask::
// -----------------------------------------------------------------------------
//
CTsBitmapScaleTask::~CTsBitmapScaleTask()
    {
    Cancel();
    delete iScaler;
    delete iSourceBmp;
    }


// -----------------------------------------------------------------------------
// CTsBitmapScaleTask::
// -----------------------------------------------------------------------------
//
void CTsBitmapScaleTask::StartLD( CFbsBitmap* aSourceBitmap,
                                  CFbsBitmap* aTargetBitmap )
    {
    if ( !iScaler )
        {
        iSourceBmp = aSourceBitmap;
        iScaler = CBitmapScaler::NewL();
        iScaler->Scale( &iStatus, *aSourceBitmap, *aTargetBitmap );
        SetActive();
        }
    }


// -----------------------------------------------------------------------------
// CTsBitmapScaleTask::
// -----------------------------------------------------------------------------
//
void CTsBitmapScaleTask::RunL()
    {
    iObserver.ScaleFinished( this );
    delete this;
    }


// -----------------------------------------------------------------------------
// CTsBitmapScaleTask::
// -----------------------------------------------------------------------------
//
void CTsBitmapScaleTask::DoCancel()
    {
    iScaler->Cancel();
    }


/* ================================================================================
 * CTsFastSwapAreaExtension
 * ================================================================================
 */

// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::NewL
// -----------------------------------------------------------------------------
//
CTsFastSwapAreaExtension* CTsFastSwapAreaExtension::NewL( CTsFastSwapArea& aFastSwapArea,
                                                  CEikonEnv& aEnv )
    {
    CTsFastSwapAreaExtension* self = CTsFastSwapAreaExtension::NewLC(
            aFastSwapArea, aEnv );
    CleanupStack::Pop( self );
    return self;
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::NewLC
// -----------------------------------------------------------------------------
//
CTsFastSwapAreaExtension* CTsFastSwapAreaExtension::NewLC( CTsFastSwapArea& aFastSwapArea,
                                                   CEikonEnv& aEnv )
    {
    CTsFastSwapAreaExtension* self = new (ELeave) CTsFastSwapAreaExtension(
            aFastSwapArea, aEnv );
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::~CTsFastSwapAreaExtension
// -----------------------------------------------------------------------------
//
CTsFastSwapAreaExtension::~CTsFastSwapAreaExtension()
    {
    iScaleTasks.ResetAndDestroy();
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::ConstructL
// -----------------------------------------------------------------------------
//
void CTsFastSwapAreaExtension::ConstructL()
    {
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::CTsFastSwapAreaExtension
// -----------------------------------------------------------------------------
//
CTsFastSwapAreaExtension::CTsFastSwapAreaExtension( CTsFastSwapArea& aFastSwapArea,
                                            CEikonEnv& aEnv )
: iFastSwapArea( aFastSwapArea ),
  iEnv( aEnv )
    {
    }

// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::GetCurrentScreenOrientation
// -----------------------------------------------------------------------------
//
TInt CTsFastSwapAreaExtension::GetCurrentScreenOrientation()
    {
    TPixelsAndRotation availableRect;
    iEnv.ScreenDevice()->GetDefaultScreenSizeAndRotation(availableRect);
    return availableRect.iPixelSize.iWidth > availableRect.iPixelSize.iHeight;
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::GetVariety
// -----------------------------------------------------------------------------
//
TBool CTsFastSwapAreaExtension::GetVariety( TInt& aVariety )
    {
    aVariety = Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0;
    TBool foreground = static_cast<CTsAppUi*>(iEnv.AppUi())->IsForeground();
    if ( foreground )
        {
        TInt screenOrientation = GetCurrentScreenOrientation();
        if ( aVariety != screenOrientation )
            {
            aVariety = screenOrientation;
            return ETrue;
            }
        }
    return EFalse;
    }


// --------------------------------------------------------------------------
// CTsFastSwapAreaExtension::GetFastSwapAreaRects
// --------------------------------------------------------------------------
//
void CTsFastSwapAreaExtension::GetFastSwapAreaRects( RArray<TAknLayoutRect>& aRects )
    {
    TAknLayoutRect gridAppPane;
    TAknLayoutRect gridItem;
    TAknLayoutRect gridImage;
    TInt variety;
    TBool disable = GetVariety(variety);
    if ( disable )
        {
        TRAP_IGNORE(static_cast<CTsAppUi*>(iEnv.AppUi())->RequestPopUpL());
        }
    variety = Layout_Meta_Data::IsLandscapeOrientation() ? 1 : 0; // double check to avoid layout panic
    gridAppPane.LayoutRect( iFastSwapArea.Rect(), 
            AknLayoutScalable_Apps::tport_appsw_pane_g1( variety ) );
    aRects.Append(gridAppPane);
    
    gridItem.LayoutRect( gridAppPane.Rect(),
            AknLayoutScalable_Apps::cell_tport_appsw_pane( variety, 0, 0 ) );
    aRects.Append(gridItem);
    
    gridImage.LayoutRect( gridItem.Rect(),
            AknLayoutScalable_Apps::cell_tport_appsw_pane_g1( variety ) ); 
    aRects.Append(gridImage);
    
    if ( disable )
        {
        TRAP_IGNORE(static_cast<CTsAppUi*>(iEnv.AppUi())->DisablePopUpL());
        }
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::ItemPosition
// -----------------------------------------------------------------------------
//
TPoint CTsFastSwapAreaExtension::ItemViewPosition( TInt aItemIdx )
    {
    TPoint retVal = iFastSwapArea.Rect().iTl;
    TInt maxItemsOnScreen = iFastSwapArea.GetGridItemData( CTsFastSwapArea::EMaxItemsOnScreen );
    TInt gridItemWidth = iFastSwapArea.GetGridItemData( CTsFastSwapArea::EGridItemWidth );
    TInt gridItemGap = iFastSwapArea.GetGridItemData( CTsFastSwapArea::EGridItemGap );
    
    if ( aItemIdx < 0 )
        {
        // No items
        retVal.iX = 0;
        }
    else if ( aItemIdx == 0 )
        {
        // First item
        if( AknLayoutUtils::LayoutMirrored() )
            {
            if ( iFastSwapArea.GridItemCount() > maxItemsOnScreen )
                {
                retVal.iX = iFastSwapArea.GridWorldSize().iWidth - iFastSwapArea.Rect().Width();
                }
            else
                {
                retVal.iX = 0;
                }
            }
        else // normal layout
            {
            retVal.iX = 0;
            }
        }
    else if ( aItemIdx == iFastSwapArea.GridItemCount() - 1 )
        {
        // Last item selected
        if( AknLayoutUtils::LayoutMirrored() )
            {
            retVal.iX = 0;
            }
        else // normal layout
            {
            if ( iFastSwapArea.GridItemCount() > maxItemsOnScreen )
                {
                retVal.iX = iFastSwapArea.GridWorldSize().iWidth - iFastSwapArea.Rect().Width();
                }
            else
                {
                retVal.iX = 0;
                }
            }
        }
    else
        {
        // Middle item
        TInt screenMiddleItemOffset = ( iFastSwapArea.Rect().Width() - gridItemWidth ) / 2;
        if( AknLayoutUtils::LayoutMirrored() )
            {
            retVal.iX = gridItemWidth * ( iFastSwapArea.GridItemCount() - 1 - aItemIdx ) - screenMiddleItemOffset;
            retVal.iX += ( iFastSwapArea.GridItemCount() - 1 - aItemIdx ) * gridItemGap;
            }
        else // normal layout
            {
            retVal.iX = gridItemWidth * aItemIdx - screenMiddleItemOffset;
            retVal.iX += gridItemGap * aItemIdx;
            }
        if ( retVal.iX < 0 )
            {
            retVal.iX = 0;
            }
        // aligin to grid end edge in case
        // (item's position + draw area) extends over grid area and
        // (all items cover more than grid area or penultimate item)
        else if ( retVal.iX + iFastSwapArea.Rect().Width() > iFastSwapArea.GridWorldSize().iWidth &&
              (iFastSwapArea.GridWorldSize().iWidth > gridItemWidth * iFastSwapArea.GridItemCount() ||
               (aItemIdx > 1 &&   aItemIdx == iFastSwapArea.GridItemCount() - 2 )) )
            {
            retVal.iX = iFastSwapArea.GridWorldSize().iWidth - iFastSwapArea.Rect().Width();
            }
        }
    
    // Return middle of the view rectangle
    retVal.iX += iFastSwapArea.Rect().Width() / 2;
    
    return retVal;
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::ViewToVisibleItem
// -----------------------------------------------------------------------------
//
TInt CTsFastSwapAreaExtension::ViewToVisibleItem( const TPoint aViewPos )
    {
    TInt retVal(0);
    TPoint absViewPos = aViewPos;
    absViewPos.iX -= iFastSwapArea.Rect().Width() / 2;
    if ( absViewPos.iX < 0 )
        {
        if ( AknLayoutUtils::LayoutMirrored() )
            {
            // View crossed left border of grid world rect, last item selected
            retVal = iFastSwapArea.GridItemCount() - 1;
            }
        else // normal layout
            {
            // View crossed left border of grid world rect, first item selected
            retVal = 0;
            }
        }
    else if ( absViewPos.iX + iFastSwapArea.Rect().Width() > iFastSwapArea.GridWorldSize().iWidth )
        {
        if ( AknLayoutUtils::LayoutMirrored() )
            {
            // View crossed right border of grid world rect, first item selected
            retVal = 0;
            }
        else // normal layout
            {
            // View crossed right border of grid world rect, last item selected
            retVal = iFastSwapArea.GridItemCount() - 1;
            }
        }
    else
        {
        TInt retItemPosX(0);
        TInt offsetCheck = iFastSwapArea.GridWorldSize().iWidth;
        // View inside of grid world rect
        for ( TInt i = 0 ; i < iFastSwapArea.GridItemCount(); i++ )
            {
            TInt itemPosX = ItemViewPosition( i ).iX;
            TInt offset = aViewPos.iX - itemPosX;
            if ( Abs( offset ) <= offsetCheck )
                {
                offsetCheck = Abs( offset );
                retVal = i;
                retItemPosX = itemPosX;
                }
            else
                {
                break;
                }
            }
        // Check if item is fully visible. If not
        // return next one if possible
        TInt gridItemWidth = iFastSwapArea.GetGridItemData( CTsFastSwapArea::EGridItemWidth );
        if ( retItemPosX - gridItemWidth / 2 < absViewPos.iX &&
             retVal + 1 < iFastSwapArea.GridItemCount() )
            {
            retVal++;
            }
        }
    
    return retVal;
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::LaunchPopupFeedback
// -----------------------------------------------------------------------------
//
void CTsFastSwapAreaExtension::LaunchPopupFeedback()
    {
    if ( AknLayoutUtils::PenEnabled() )
        {
        MTouchFeedback* feedback = MTouchFeedback::Instance();
        if ( feedback )
            {
            TTouchLogicalFeedback fbLogicalType = ETouchFeedbackPopUp;
            if ( CAknTransitionUtils::TransitionsEnabled(
                 AknTransEffect::EComponentTransitionsOff ) )
                {
                fbLogicalType = ETouchFeedbackIncreasingPopUp;
                }
            feedback->InstantFeedback( &iFastSwapArea,
                                       fbLogicalType,
                                       ETouchFeedbackVibra,
                                       TPointerEvent() );
            }
        }
    }


// -----------------------------------------------------------------------------
// CTsFastSwapAreaExtension::PreferredImageSize
// -----------------------------------------------------------------------------
//
TSize CTsFastSwapAreaExtension::PreferredImageSize()
    {
    TAknLayoutRect gridImage;
    TRAP_IGNORE(
        RArray<TAknLayoutRect> rects;
        CleanupClosePushL(rects);
        rects.ReserveL(KLayoutItemCount);
        GetFastSwapAreaRects(rects);
        gridImage = rects[2];
        CleanupStack::PopAndDestroy(&rects);
        );
    return gridImage.Rect().Size();
    }

// --------------------------------------------------------------------------
// CTsFastSwapAreaExtension::CalculateSizePreserveRatio
// --------------------------------------------------------------------------
//
TSize CTsFastSwapAreaExtension::CalculateSizePreserveRatio(
        const TSize& aTargetAreaSize,
        const TSize& aSourceSize )
    {
    const TReal 
      resizedAspectRatio(aTargetAreaSize.iWidth/(TReal)aTargetAreaSize.iHeight);
    const TReal 
      orginalAspectRatio(aSourceSize.iWidth/(TReal)aSourceSize.iHeight);
    //this condition avoid empty margins ( bigger output ). to realy fit area change it
    const TReal scaleFactor = 
        ( orginalAspectRatio > resizedAspectRatio ) ? 
        (aTargetAreaSize.iHeight /(TReal)aSourceSize.iHeight) ://scale by height
        (aTargetAreaSize.iWidth /(TReal)aSourceSize.iWidth) ;//scale by width
    return TSize(aSourceSize.iWidth * scaleFactor, aSourceSize.iHeight * scaleFactor);
    }


// --------------------------------------------------------------------------
// CTsFastSwapAreaExtension::CopyBitmapL
// Copy and scale.
// --------------------------------------------------------------------------
//
CFbsBitmap* CTsFastSwapAreaExtension::CopyBitmapL( TInt aFbsHandle, TSize aSize, TBool aIsScreenshot  )
    {
    CFbsBitmap* ret = new (ELeave) CFbsBitmap();
    CleanupStack::PushL( ret );

    CFbsBitmap* bmp = new (ELeave) CFbsBitmap();
    CleanupStack::PushL( bmp );
    User::LeaveIfError( bmp->Duplicate( aFbsHandle ) );
    
    // First scale using normal method, to show "draft" screenshots.
    // Do not always use aSize, preserving the aspect ratio is quite
    // important when showing app icons instead of screenshots
    TSize sz = CalculateSizePreserveRatio( aSize, bmp->SizeInPixels() );
    User::LeaveIfError( ret->Create( sz, bmp->DisplayMode() ) );
    AknIconUtils::ScaleBitmapL( sz, ret, bmp );
    
    // Second, if it is screenshot, perform quality improvement scaling
    if ( aIsScreenshot )
        {
        CTsBitmapScaleTask* rotaTask = new (ELeave) CTsBitmapScaleTask( *this );
        CleanupStack::PushL( rotaTask );
        iScaleTasks.AppendL( rotaTask );
        rotaTask->StartLD( bmp, ret ); // ownership of bmp transferred
        CleanupStack::Pop( rotaTask );
        CleanupStack::Pop( bmp );
        }
    else
        {
        CleanupStack::PopAndDestroy( bmp );
        }
    
    CleanupStack::Pop( ret );
    return ret;
    }


// --------------------------------------------------------------------------
// CTsFastSwapAreaExtension::CancelScaleTasks
// --------------------------------------------------------------------------
//
void CTsFastSwapAreaExtension::CancelScaleTasks()
    {
    iScaleTasks.ResetAndDestroy();
    }


// --------------------------------------------------------------------------
// CTsFastSwapAreaExtension::ScaleFinished
// --------------------------------------------------------------------------
//
void CTsFastSwapAreaExtension::ScaleFinished( CTsBitmapScaleTask* iFinishedTask )
    {
    TInt task = iScaleTasks.Find( iFinishedTask );
    if ( task != KErrNotFound )
        {
        iScaleTasks.Remove( task );
        }
    iFastSwapArea.DrawDeferred();
    }

 // End of file
