/****************************************************************************
**
** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (developer.feedback@nokia.com)
**
** This file is part of the HbWidgets module of the UI Extensions for Mobile.
**
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at developer.feedback@nokia.com.
**
****************************************************************************/

#include "hbsliderhandle_p.h"
#include "hbslidercontrol_p.h"
#include "hbslidercontrol_p_p.h"
#include "hbstyle_p.h"
#include <hbwidgetfeedback.h>
#include <hbstyle.h>
#include <hbstyleoptionslider_p.h>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsScene>

#ifdef HB_EFFECTS
#include "hbeffect.h"
#include "hbeffectinternal_p.h"
#define HB_SLIDERHANDLE_TYPE "HB_SLIDERHANDLE"
#endif
#ifdef HB_GESTURE_FW
#include <hbtapgesture.h>
#include <hbpangesture.h>
#include <hbtapandholdgesture.h>
#endif
/*!
    This is internal class for HbSlider
    this created slider handle
    It also has event handling for handle press/move and release

*/


/*!
    Constructs a slider handle for \a slider.
*/
HbSliderHandle::HbSliderHandle(HbSliderControl *slider)
    : HbWidget(slider),
    sliderControl(slider), 
    gItem(0) ,
    handleDragPos(0),
    touchItem(0),
    mHandleItem(0)
{
    // create handle icon item
    gItem = HbStylePrivate::createPrimitive(HbStylePrivate::P_Slider_thumb, this); 
    HbStyle::setItemName(gItem , "icon");
    setFiltersChildEvents(true) ;

    // create touch area for handle
    touchItem = HbStylePrivate::createPrimitive(HbStylePrivate::P_SliderElement_touchhandle, this); 
    HbStyle::setItemName(touchItem , "toucharea");

    setZValue(slider->zValue() + 1);
#ifdef HB_GESTURE_FW    
    grabGesture(Qt::TapGesture);
    grabGesture(Qt::PanGesture);
    grabGesture(Qt::TapAndHoldGesture);
    if(touchItem) {
        if(QGraphicsObject *touchArea = touchItem->toGraphicsObject()) {
            touchArea->grabGesture(Qt::PanGesture);
            touchArea->grabGesture(Qt::TapGesture);
            touchArea->grabGesture(Qt::TapAndHoldGesture);
        }
    }
#endif 
#ifdef HB_EFFECTS
    // horizontal thumb press
   // HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_h_press", "h_thumbpress");
    //horizonal thumb release
    //HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_h_releasel", "h_thumbrelease");
    // vertical thumb press
    //HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_v_press", "v_thumbpress");
    //vertical thumb release
    //HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_v_release", "v_thumbrelease");
    // horizontal out of bound
    //HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_h_outofbound", "h_outofbound");
    //vertical out of bound
    //HbEffectInternal::add(HB_SLIDERHANDLE_TYPE,"sliderhandle_v_outofbound", "v_outofbound");
#endif
}



/*!
    Destroys the slider handle.
*/
HbSliderHandle::~HbSliderHandle()
{
    if(mHandleItem) {
        mHandleItem->removeSceneEventFilter(this);
    }
}


/*!
  reimp

*/
/*void HbSliderHandle::polish( HbStyleParameters& params )
{
    updatePrimitives();
    HbWidget::polish(params);
}*/

/*!
  This Api set handle position and update handle icon to press ot normal
*/
void HbSliderHandle::forceSetPos(const QPointF &pos)
{
    setPos(pos);
    HbStyleOptionSlider opt;
    sliderControl->initStyleOption(&opt);
    if( scene()){
        if (true == sliderControl->isSliderDown()){
            opt.state |= QStyle::State_Sunken;
            HbStylePrivate::updatePrimitive(
                sliderControl->primitive((HbStyle::Primitive)HbStylePrivate::P_Slider_groove), HbStylePrivate::P_Slider_groove, &opt);
          } else {
            opt.state &= ~QStyle::State_Sunken;
        } 
    } else {
        opt.state &= ~QStyle::State_Sunken;
    }

    opt.boundingRect=boundingRect();
    HbStylePrivate::updatePrimitive(gItem, HbStylePrivate::P_Slider_thumb, &opt); 
}

void HbSliderHandle::setHandleItem(QGraphicsItem *item)
{
    if(!item) {
        return;
    }
    if(mHandleItem) {
        delete mHandleItem;
        mHandleItem =0;
    }

    setHandlesChildEvents(false);

    mHandleItem = item;  
    mHandleItem->setParentItem(this);
    HbStyle::setItemName(mHandleItem,"widget");
    //((QGraphicsWidget*)mHandleItem)->setGeometry(gItem->geometry());//TODO:geometry from css

    if(scene()) {
        mHandleItem->removeSceneEventFilter(this);
        mHandleItem->installSceneEventFilter(this);
    }

}

QGraphicsItem *HbSliderHandle::handleItem() const
{
    return mHandleItem;
}

void HbSliderHandle::showEvent(QShowEvent *event)
{
    Q_UNUSED(event);
    if(scene()) {
        if(gItem) {
            //gItem->removeSceneEventFilter(this);
            //gItem->installSceneEventFilter(this);
        }
        if(mHandleItem && scene()) {
            mHandleItem->removeSceneEventFilter(this);
            mHandleItem->installSceneEventFilter(this);
        }
           
    }
}
bool HbSliderHandle::sceneEventFilter(QGraphicsItem *obj,QEvent *event)
{
    //TODO: touch area does not work with the current filtering mechanism. find better solution
    if( obj == mHandleItem) {
        if(event->type() == QEvent::GraphicsSceneMouseMove){
            mouseMoveEvent ((QGraphicsSceneMouseEvent *) event) ;
            return true;
        } else if (event->type() == QEvent::GraphicsSceneMousePress){
            mousePressEvent((QGraphicsSceneMouseEvent *) event);
            return true;
        } else if (event->type() == QEvent::GraphicsSceneMouseRelease){
            mouseReleaseEvent((QGraphicsSceneMouseEvent *) event);
            return true;
        }
    } 
    if( obj == touchItem ) {
        if (!isEnabled() ) {
            return false;
        }
        if (event->type() == QEvent::Gesture){
            gestureEvent( (QGestureEvent *) (event));
            return true;
        }
    }
    return false;
}
 
/*!
  reimp

*/
void HbSliderHandle::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
#ifndef HB_GESTURE_FW
    qreal span = 0;
    HbWidget::mousePressEvent(event);
    QRectF sliderBounds=sliderControl->boundingRect();
    QRectF handleBounds=boundingRect();
    sliderControl->setSliderDown(true);
    HbWidgetFeedback::triggered(sliderControl, Hb::InstantPressed, Hb::ModifierSliderHandle);
    if (scene()){
        HbStyleOptionSlider opt;
        sliderControl->initStyleOption(&opt);
        if (sliderControl->orientation() == Qt::Horizontal) {
#ifdef HB_EFFECTS
            HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_thumbpress");
#endif
            sliderBounds.adjust(0, 0, -handleBounds.width(), 0);
            span = sliderBounds.width();
            handleInitialPos = static_cast<int>(mapToParent(event->pos()).rx());
        } else {
#ifdef HB_EFFECTS
            HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_thumbpress");
#endif
            sliderBounds.adjust(0, 0, 0, -handleBounds.height());
            span = sliderBounds.height();
            handleInitialPos = static_cast<int>(mapToParent(event->pos()).ry());
        }
        handleDragPos= QStyle::sliderPositionFromValue(opt.minimum, opt.maximum,
            sliderControl->sliderPosition(), static_cast<int>(span),opt.upsideDown);
        updatePrimitives();
    }
    event->accept();
#else
    Q_UNUSED(event)
#endif 
}

/*!
  reimp

*/
void HbSliderHandle::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
#ifndef HB_GESTURE_FW
    HbWidget::mouseReleaseEvent(event);
#ifdef HB_EFFECTS
    if( sliderControl->orientation() == Qt::Horizontal ) {
        HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_thumbrelease");
    }
    else {
        HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_thumbrelease");
    }        
#endif
    HbWidgetFeedback::triggered(sliderControl, Hb::InstantReleased, Hb::ModifierSliderHandle);
    sliderControl->setSliderDown(false);
    QRectF controlRect = sliderControl->sceneBoundingRect();
    if(!controlRect.contains(event->scenePos().x() , event->scenePos().y()) ){
        sliderControl->releasedOutside(true);
    }
    if(sliderControl->singleStep() != 0) {
        HbWidgetFeedback::continuousStopped(sliderControl, Hb::ContinuousDragged);
    }
    event->accept();
    if( sliderControl->snappingMode()!= HbSliderControl::NoSnapping ){
        sliderControl->updateSliderPosToTick();
    }
    updatePrimitives();
#else
    Q_UNUSED(event)
#endif 
}

/*!
  reimp

*/
void HbSliderHandle::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
#ifndef HB_GESTURE_FW
    qreal span = 0;
    HbWidget::mouseMoveEvent(event);
    QRectF sliderBounds=sliderControl->boundingRect();
    QRectF handleBounds=boundingRect();
    HbStyleOptionSlider opt;
    sliderControl->initStyleOption(&opt);
    int sliderPos;
    if ( sliderControl->orientation() == Qt::Horizontal ) {
#ifdef HB_EFFECTS
        if( sliderBounds.topLeft().x() > mapToParent(event->pos()).x() ||
            sliderBounds.bottomRight().x() < mapToParent(event->pos()).x()) {
            HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_outofbound");
        }
#endif
        sliderBounds.adjust(0, 0, -handleBounds.width(), 0);
        span = sliderBounds.width();
        sliderPos = static_cast<int>(
            handleDragPos+(mapToParent(event->pos()).rx()-handleInitialPos));
    } else {
#ifdef HB_EFFECTS
        if( sliderBounds.topLeft().y() > mapToParent(event->pos()).y() ||
            sliderBounds.bottomRight().y() < mapToParent(event->pos()).y()) {
            HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_outofbound");
        }
#endif
        sliderBounds.adjust(0, 0, 0, -handleBounds.height());
        span = sliderBounds.height();
        sliderPos = static_cast<int>(
            handleDragPos+(mapToParent(event->pos()).ry()-handleInitialPos));
    }
    int pressValue= QStyle::sliderValueFromPosition(opt.minimum, opt.maximum,
        sliderPos, static_cast<int>(span),opt.upsideDown);
    sliderControl->setSliderPosition(pressValue);
    sliderControl->showToolTip();
#else
    Q_UNUSED(event)
#endif 
}

/*!
  reimp

*/
void HbSliderHandle::gestureEvent(QGestureEvent *event)
{ 
    if(HbTapAndHoldGesture *tapandHold= qobject_cast<HbTapAndHoldGesture *>(event->gesture(Qt::TapAndHoldGesture))) {
        if(tapandHold->state() == Qt::GestureStarted) {
            sliderControl->setToolTip(QString::number(sliderControl->sliderPosition()));
            sliderControl->showToolTip();
        }
    }
    if(HbTapGesture *tap = qobject_cast<HbTapGesture *>(event->gesture(Qt::TapGesture))) {
        switch(tap->state()) {
        case Qt::GestureStarted: {
            if (!sliderControl->isSliderDown( ) ) {
                sliderControl->setSliderDown(true);
                updatePrimitives();
            }
            HbWidgetFeedback::triggered(sliderControl, Hb::InstantPressed, Hb::ModifierSliderHandle);
            if (scene()){
                HbStyleOptionSlider opt;
                sliderControl->initStyleOption(&opt);
                if (sliderControl->orientation() == Qt::Horizontal) {
#ifdef HB_EFFECTS
                    HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_thumbpress");
#endif
                } else {
#ifdef HB_EFFECTS
                    HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_thumbpress");
#endif
                }
                event->accept();
            }
        }
        break;
        case Qt::GestureFinished:
         {
#ifdef HB_EFFECTS
            if( sliderControl->orientation() == Qt::Horizontal ) {
                HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_thumbrelease");
            }  else {
                HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_thumbrelease");
            }
#endif
            HbWidgetFeedback::triggered(sliderControl, Hb::InstantReleased, Hb::ModifierSliderHandle);
            sliderControl->setSliderDown(false);
            updatePrimitives();
            break;
        }
        default:
            break;
                                  
        }
    }
    if (HbPanGesture *panGesture = qobject_cast<HbPanGesture*>(event->gesture(Qt::PanGesture))) {
        switch(panGesture->state( )) {
        case Qt::GestureStarted: 
        case Qt::GestureUpdated:{
            QPointF eventScenePos =panGesture->sceneOffset( )+panGesture->sceneStartPos( );
            QPointF relativePos = mapToParent( mapFromScene(eventScenePos ));
            qreal span = 0;
            QRectF sliderBounds=sliderControl->boundingRect();
            QRectF handleBounds=boundingRect();
            HbStyleOptionSlider opt;
            sliderControl->initStyleOption(&opt);
            int sliderPos;
            if ( sliderControl->orientation() == Qt::Horizontal ) {
#ifdef HB_EFFECTS
                if( sliderBounds.topLeft().x() > relativePos.rx( ) ||
                    sliderBounds.bottomRight().x() < relativePos.rx()) {
                    HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_outofbound");
                }
#endif
                sliderBounds.adjust(0, 0, -handleBounds.width(), 0);
                span = sliderBounds.width();
                sliderPos = static_cast<int>(relativePos.x());
                sliderPos-= static_cast<int>(handleBounds.width()/2);
            } else {
#ifdef HB_EFFECTS
                if( sliderBounds.topLeft().y() > relativePos.ry() ||
                    sliderBounds.bottomRight().y() < relativePos.ry()) {
                    HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_outofbound");
                }
#endif
                sliderBounds.adjust(0, 0, 0, -handleBounds.height());
                span = sliderBounds.height();
                sliderPos = static_cast<int>(relativePos.y());
                sliderPos -= static_cast<int>(handleBounds.height()/2);
                    
            }
            int pressValue= QStyle::sliderValueFromPosition(opt.minimum, opt.maximum,
                sliderPos, static_cast<int>(span),opt.upsideDown);
            sliderControl->setSliderPosition(pressValue);
            sliderControl->setToolTip(QString::number(pressValue));
            sliderControl->showToolTip();
            break;
        }
        case Qt::GestureFinished:
        case Qt::GestureCanceled: {
#ifdef HB_EFFECTS
            if( sliderControl->orientation() == Qt::Horizontal ) {
                HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "h_thumbrelease");
            }  else {
                HbEffect::start(gItem, HB_SLIDERHANDLE_TYPE, "v_thumbrelease");
            }
#endif
            HbWidgetFeedback::triggered(sliderControl, Hb::InstantReleased, Hb::ModifierSliderHandle);
            sliderControl->setSliderDown(false);
            QRectF controlRect = sliderControl->sceneBoundingRect();
            if(sliderControl->singleStep() != 0) {
                HbWidgetFeedback::continuousStopped(sliderControl, Hb::ContinuousDragged);
            }
            if( sliderControl->snappingMode()!= HbSliderControl::NoSnapping ){
                sliderControl->updateSliderPosToTick();
            }
            updatePrimitives();
            break;
            }
            default:
                break;
        }
    }

}

/*!
  reimp

*/
void HbSliderHandle::updatePrimitives()
{
    HbStyleOptionSlider opt;
    sliderControl->initStyleOption(&opt);
    if( scene()){
        if (true == sliderControl->isSliderDown()){
            opt.state |= QStyle::State_Sunken;
            HbStylePrivate::updatePrimitive(
                sliderControl->primitive((HbStyle::Primitive)HbStylePrivate::P_Slider_groove), HbStylePrivate::P_Slider_groove, &opt);
        } else {
            opt.state &= ~QStyle::State_Sunken;
        } 
    } else {
        opt.state &= ~QStyle::State_Sunken;
    }

    opt.boundingRect=boundingRect();
    HbStylePrivate::updatePrimitive(gItem, HbStylePrivate::P_Slider_thumb, &opt); 
    HbStylePrivate::updatePrimitive(touchItem , HbStylePrivate::P_SliderElement_touchhandle ,&opt);
    sliderControl->update(boundingRect());
}

/*!
  reimp

*/
QGraphicsItem * HbSliderHandle::primitive(HbStyle::Primitive primitive) const
 {
       switch (primitive) {
        case HbStylePrivate::P_Slider_thumb:
            return gItem;
        case HbStylePrivate::P_SliderElement_touchhandle:
            return touchItem;
        default:
            return NULL;
    }
 }
