/*
* 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 the License "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:   Implementation of scrolling decelerator
*
*/


// INCLUDE FILES
#include <../bidi.h>
#include "WebScrollingDecelerator.h"
#include "WebView.h"
#include "WebFrame.h"
#include "WebFrameView.h"
#include "WebPageScrollHandler.h"
#include "PlatformScrollbar.h"

// constants
const int KRecordSize = 4;

// The following deceleration curve is generated by a script
// It lists the timeout dt values in microseconds.
const int KDecelCurveSize = 10;

const int KScrollIntervalTimeout = 40000; // scroll timer interval in microseconds

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

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::NewL
// The two-phase Symbian constructor
// -----------------------------------------------------------------------------
//

WebScrollingDecelerator* WebScrollingDecelerator::NewL(WebView& webView)
{
    WebScrollingDecelerator* self = new (ELeave) WebScrollingDecelerator(webView);

    CleanupStack::PushL(self);
    self->ConstructL();
    CleanupStack::Pop(); //self
    return self;
}

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::WebScrollingDecelerator
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
WebScrollingDecelerator::WebScrollingDecelerator(WebView& webView)
: CTimer(EPriorityHigh), m_webView( webView )
{
}

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::ConstructL
// -----------------------------------------------------------------------------
void WebScrollingDecelerator::ConstructL()
{
    CTimer::ConstructL();
    CActiveScheduler::Add( this );
    m_dragPosition.Reset();
}

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
WebScrollingDecelerator::~WebScrollingDecelerator()
{
    Cancel();
}

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::updatePos
// update record of position
// -----------------------------------------------------------------------------
//
void WebScrollingDecelerator::updatePos()
{
    if (m_dragPosition.Count() >= KRecordSize)
      {
        // pop item from front   
        m_dragPosition.Remove(0);
      }
    
    // and add this one to the end
    TPoint currPosition = m_webView.pageScrollHandler()->currentScrollingFrameView()->contentPos();
    m_dragPosition.Append(currPosition);
}

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::startDecel
// initialize and start deceleration
// -----------------------------------------------------------------------------
//
void WebScrollingDecelerator::startDecelL()
{
    int count = m_dragPosition.Count();
        
    m_normalizedCurrentPosition.iX = m_webView.pageScrollHandler()->currentScrollingFrameView()->contentPos().iX * 100;
    m_normalizedCurrentPosition.iY = m_webView.pageScrollHandler()->currentScrollingFrameView()->contentPos().iY * 100;
    
    if (count > 0)
      {     

      // set the delta based upon the change span of points collected averaged over the span, so long as we have moved
        int dx = (m_dragPosition[count - 1].iX - m_dragPosition[0].iX)* 100;
        int dy = (m_dragPosition[count - 1].iY - m_dragPosition[0].iY) * 100;
 
        if (abs(dx) > 0 || abs(dy) > 0)
            {           
            m_scrolldelta.SetXY( (dx/count), (dy/count) );
            WebFrameView* view = m_webView.pageScrollHandler()->currentScrollingFrameView();
            
            m_scrolldelta = view->toDocCoords(m_scrolldelta);
            
            // init num steps
            m_numscrollsteps = 0;

            // call the RunL function directly, this first time. CTimer will call it from now on.
            m_decelelatorSwitch = ETrue;
            
            RunL();
            }
        }
        m_dragPosition.Reset();
}

// -----------------------------------------------------------------------------
// WebScrollingDecelerator::RunL
// continue deceleration
// -----------------------------------------------------------------------------
//
void WebScrollingDecelerator::RunL()
{
  WebFrameView* scrollingView = m_webView.pageScrollHandler()->currentScrollingFrameView();
    // use a pre-calulated deceleration curve to calculate dt
    if ((m_numscrollsteps >= KDecelCurveSize ) || !m_decelelatorSwitch)
      {
        // done
        Cancel();
        m_webView.setViewIsScrolling(false);
        return;
      }
    
    // step to next
    m_numscrollsteps++;

    // do the scrolling
    
    if (m_webView.pageScrollHandler() && scrollingView) {
        
        TPoint newpos;
        
        /* With each step decrease m_scrolldelta by 50% */
        m_scrolldelta.iX = m_scrolldelta.iX >> 1;
        m_scrolldelta.iY = m_scrolldelta.iY >> 1;
                
        m_normalizedCurrentPosition += m_scrolldelta;
        newpos.iX = m_normalizedCurrentPosition.iX/100;
        newpos.iY = m_normalizedCurrentPosition.iY/100;
        scrollingView->scrollTo(newpos, ETrue);

        if (scrollingView->vScrollbar()) {
            scrollingView->vScrollbar()->setValue(newpos.iY);
        }
        if (scrollingView->hScrollbar()) {
            scrollingView->hScrollbar()->setValue(newpos.iX);
        }

        After(KScrollIntervalTimeout);
    }
}

//  End of File
