/*
  This file was generated by the Html5 Application wizard of Qt Creator.
  Html5ApplicationViewer is a convenience class containing mobile device specific
  code such as screen orientation handling.
  It is recommended not to modify this file, since newer versions of Qt Creator
  may offer an updated version of it.
*/

#include "webtouchphysics.h"


int fastDistance(const QPoint &p, const QPoint &q)
{
    return (p.x() - q.x()) * (p.x() - q.x()) + (p.y() - q.y()) * (p.y() - q.y());
}

WebTouchPhysics::WebTouchPhysics(QObject *parent)
    : WebTouchPhysicsInterface(parent)
    , m_frame(0)
    , m_inMotion(false)
{
    connect(&m_decelerationTimer, SIGNAL(timeout()), this, SLOT(decelerationTimerFired()));
}

WebTouchPhysics::~WebTouchPhysics()
{
}

bool WebTouchPhysics::inMotion()
{
    return m_inMotion;
}

void WebTouchPhysics::stop()
{
    m_decelerationTimer.stop();
    m_cumulativeDistance = QPoint();
    m_previousPoint = QPoint();
    m_startPressPoint = QPoint();
    m_decelerationPoints.clear();
    m_inMotion = false;
}

void WebTouchPhysics::start(const QPointF &pressPoint, const QWebFrame *frame)
{
    if (!frame)
        return;

    m_frame = frame;
    m_decelerationPoints.push_front(pressPoint);
    m_decelerationTimer.setSingleShot(false);
    m_decelerationTimer.setInterval(KDecelerationTimerSpeed);
    m_previousPoint = pressPoint;
    m_startPressPoint = pressPoint.toPoint();
}

bool WebTouchPhysics::move(const QPointF &pressPoint)
{
    int distance = fastDistance(pressPoint.toPoint(), m_startPressPoint);
    if (isFlick(pressPoint, distance) || isPan(pressPoint, distance)) {
        changePosition(pressPoint);
        return true;
    }
    return false;
}

bool WebTouchPhysics::release(const QPointF &pressPoint)
{
    // use the cumulative distance in this case because it seems like a positive user experience
    if (m_cumulativeDistance.manhattanLength() > KCumulativeDistanceThreshold) {
        m_decelerationSpeed = (m_decelerationPoints.back() - pressPoint) / (m_decelerationPoints.count() + 1);
        m_decelerationTimer.start();
        return true;
    } else {
        m_inMotion = false;
        return false;
    }
}

void WebTouchPhysics::changePosition(const QPointF &point)
{
    m_inMotion = true;
    QPointF diff = m_previousPoint - point;
    emit positionChanged(diff, m_startPressPoint);

    m_cumulativeDistance += (point - m_previousPoint);
    m_previousPoint = point;
    m_decelerationPoints.push_front(point);
    if (m_decelerationPoints.count() > KDecelerationCount)
        m_decelerationPoints.pop_back();
}

void WebTouchPhysics::decelerationTimerFired()
{
    if (!m_frame) {
        m_decelerationTimer.stop();
        return;
    }

    if (qAbs(m_decelerationSpeed.x()) < KDecelerationSlowdownFactor
            && qAbs(m_decelerationSpeed.y()) < KDecelerationSlowdownFactor) {
        m_inMotion = false;
        m_decelerationTimer.stop();
        return;
    }

    m_decelerationSpeed *= KDecelerationSlowdownFactor;
    emit positionChanged(QPoint(m_decelerationSpeed.x(), m_decelerationSpeed.y()), m_startPressPoint);
}

bool WebTouchPhysics::isFlick(const QPointF &pressPoint, int distance) const
{
    Q_UNUSED(pressPoint);
    // this mouse move event is far away from the touch down position in
    // less than xx sec, the user must want to scroll quickly.
    return !m_inMotion && distance > KFlickScrollThreshold;
}

bool WebTouchPhysics::isPan(const QPointF &pressPoint, int distance) const
{
    Q_UNUSED(pressPoint);
    return distance > KJitterBufferThreshold;
}
