/*
* Copyright (c) 2009 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: Juha Kauppinen, Mika Hokkanen
* 
* Description: Photo Browser
*
*/

#ifndef GESTURE_H
#define GESTURE_H

// INCLUDES
#include <e32std.h>
#include <e32base.h>
#include <w32std.h>

#undef CURSOR_SIMULATION

const TInt KDefaultThresholdOfTapPixels      = 25;
const TInt KDefaultThresholdOfCursorPixels   = 0;      // Any movement is cursor movement 
const TInt KDefaultStationaryTime            = 100000; // 100ms
const TInt KDefaultLongTapTime               = 400000; // 300ms = 200ms + stationary (100ms)  
const TInt KDefaultMonitoringTime            = 200000; // 200ms
const TInt KDefaultSafetyTime                = 200000; // 200ms
const TInt KDefaultValidityTimeOfFlick       = 300000; // recent 300ms drag to be checked 

enum {
    EGestureNone        = 0x0000,
    EGestureStationary  = 0x0001,  // Send on moved event when user hasn't moved finger
    EGestureDrag        = 0x0002,
    EGestureTap         = 0x0004,
    EGestureLongTapping = 0x0008,
    EGestureLongTap     = 0x0010,
    EGestureCursor      = 0x0100,
    // sub-type for cursor simuation
    EGestureUp          = 0x1000,
    EGestureDown        = 0x2000,
    EGestureLeft        = 0x4000,
    EGestureRight       = 0x8000,
    // 1st gesture type is stored in high 16 bits
    EGestureDragged     = EGestureDrag    << 16,
    EGestureTapped      = EGestureTap     << 16,
    EGestureLongTapped  = EGestureLongTap << 16
};
// Gestures can be combination
typedef TUint32 TGestureType;

#define IS_GESTURE_NONE(t)             ((t) == (EGestureNone))
#define IS_GESTURE_DRAG(t)             ((t) & (EGestureDrag))
#define IS_GESTURE_STATIONARY(t)       ((t) & (EGestureStationary))
#define IS_GESTURE_CURSORSIMULATION(t) ((t) & ((EGestureUp | EGestureDown | EGestureLeft | EGestureRight)))
#define IS_GESTURE_TAP(t)              ((t) & (EGestureTap))
#define IS_GESTURE_TAPPED(t)           ((t) & (EGestureTapped))
#define IS_GESTURE_LONGTAPPING(t)      ((t) & (EGestureLongTapping))
#define IS_GESTURE_LONGTAP(t)          ((t) & (EGestureLongTap))
#define IS_GESTURE_DRAGGED(t)          ((t) & (EGestureDragged))
#define IS_GESTURE_SINGLETAP(t)        (IS_GESTURE_TAP(t) && !IS_GESTURE_TAPPED(t))
#define IS_GESTURE_DOUBLETAP(t)        (IS_GESTURE_TAP(t) && IS_GESTURE_TAPPED(t))
#define IS_GESTURE_TAPnDRAG(t)         (IS_GESTURE_CURSORSIMULATION(t) && IS_GESTURE_TAPPED(t))

enum {
    EGestureModeNone                  = 0x00,
    EGestureModeCursorEmulation       = 0x01, // gives cursor movements when beyond threshold
    EGestureModeSingleCursorEmulation = 0x02, // gives singles cursor movement on each touch
    EGestureModeDrag                  = 0x04, // 
    EGestureModeTap                   = 0x08,
    EGestureModeTapAndDrag            = 0x10,
    EGestureModeDrawCircle            = 0x20
};

// CLASS DECLARATION

class MGestureCallBack
    {
public:
    /**
    * Callback method. Get's called when touch gesture started (when user touches).
    * @param aPos  Point where touch happens
    * @param aType Gesture type bit flag (move_right/left/up/down or tap)
    */
    
    // Called when user touches screen
    virtual void HandleGestureBeganL(const TPoint&      aPos ) = 0;

    // Called when user is dragging
    // Drag event comes with movement delta from previous event (EGestureDrag)
    // Drag event comes with touched position (EGestureUp/Down/Left/Right)
    //   when it exceeds defined threshold
    virtual void HandleGestureMovedL(const TPoint&      aPos,
                                     const TGestureType aType) = 0;

    // Called when user releases screen
    // Movement within defined last moment is given in aPos. (pixels/250ms by default)
    virtual void HandleGestureEndedL(const TPoint&      aPos,
                                     const TGestureType aType) = 0;
    };

/**
 *  CGesture
 * 
 */
class CGesture : public CTimer
    {
public:
    // Constructors and destructor

    /**
     * Destructor.
     */
    ~CGesture();

    /**
     * Two-phased constructor.
     */
    static CGesture* NewL(MGestureCallBack* aOwner);

    /**
     * Two-phased constructor.
     */
    static CGesture* NewLC(MGestureCallBack* aOwner);

private:

    /**
     * Constructor for performing 1st stage construction
     */
    CGesture(MGestureCallBack* aOwner);

    /**
     * EPOC default constructor for performing 2nd stage construction
     */
    void ConstructL();

public: // New functions

    IMPORT_C void PointerEventL(const TPointerEvent& aEvent);
    IMPORT_C void SetThresholdOfTap(const TInt aPixels);
    IMPORT_C void SetThresholdOfCursor(const TInt aPixels);
    IMPORT_C void SetStationaryTime(const TInt aMicroseconds);
    IMPORT_C void SetLongTapTime(const TInt aMicroSeconds);
    IMPORT_C void SetMonitoringTime(const TInt aMicroseconds);
    IMPORT_C void SetSafetyTime(const TInt aMicroseconds);

private:

    enum TGestureState
        {
        EWaiting,          // Waiting for first touch
        EBegan,            // Touch down. Gesture started (or second gesture)
        EStationary,       // Gesture is stationary
        ETravelling,       // Dragging beyond threshold
        EMonitoring,       // Monitoring second gesture
        EEnded             // Touch operation ended. Wait to avoid mis touch
        };

    /**
     * Just check if the movement exceeds thoreshold
     */
    TBool IsMovementWithinThreshold(const TPoint aDelta, const TInt aThreshold);

    /**
     * Map touch movement to 8-directions if volume of movement exceeds threshold
     * Also check if movement is faster than threshold
     */
    TGestureType CheckMovement(const TPoint aPointPrevious, const TPoint aPointCurrent, const TBool aSkipThresholdCheck=EFalse);

    /**
     * Maps touch movements in last defines time, aValidityTime, to 8-directions
     * Movements are recorded in iDragPoints and iDragTicks (RArray of TPoint and TInt)
     */
    TGestureType CheckFlick(const TInt aValidityTime, TPoint& aVector);

    /**
     * Active object callback. Used for timer
     */
    void RunL();

private: // Data

    /**
     * Current state of Gesture
     */
    TGestureState iState;
    
    /**
     * Pointer to owner
     */
    MGestureCallBack* iOwner;

    /**
     * Pointer event received from owner.
     */
    TPointerEvent iPointerEvent;

    /**
     * Point records
     */
    TPoint iPointBegan;         // point where user touched down
    TPoint iPointFirstBegan;    // point where user touched down in 1st gesture
    TPoint iPointLastCursor;    // point where last cursor simulation event occured
    TPoint iPointPreviousEvent; // last point where avkon informed on drag

    /**
     * Remembers gesture in 1st gesture
     */
    TGestureType iFirstGesture;

    /**
     * Thresholds in Pixels
     */
    TInt iThresholdOfTap;       // movement stays within this pixels
    TInt iThresholdOfCursor;    // cursor simulation occurs when exceeding threshold
    
    /**
     * Thresholds in Micro seconds
     */
    TInt iStationaryTime;       // UP should occur within the time for Tap 
    TInt iLongTapTime;          // for long tap
    TInt iMonitoringTime;       // for 2nd gesture. Notify gesture end if expires
    TInt iSafetyTime;           // Ignores gesture after Ended to avoid mis-touch
     
    /**
     * Drag points and the tick time for flick
     */
    RArray<TPoint> iDragPoints;
    RArray<TInt>   iDragTicks;
    };

#endif // GESTURE_H
