/////////////////////////////////////////////////////////////////////////////
// Name:        include/wx/qt/winevent_qt.h
// Purpose:     QWidget to wxWindow event handler
// Author:      Javier Torres, Peter Most
// Modified by:
// Created:     21.06.10
// Copyright:   (c) Javier Torres
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#ifndef _WX_QT_EVENTSIGNALFORWARDER_H_
#define _WX_QT_EVENTSIGNALFORWARDER_H_

#include <QtCore/QEvent>
#include <QtGui/QCloseEvent>

#include "wx/log.h"
#include "wx/window.h"
#include "wx/qt/private/converter.h"
#include "wx/qt/private/utils.h"

#include <QtWidgets/QGestureEvent>
#include <QtGui/QCursor>

class QPaintEvent;

template< typename Handler >
class wxQtSignalHandler
{
protected:
    wxQtSignalHandler( Handler *handler )
    {
        m_handler = handler;
    }

    void EmitEvent( wxEvent &event ) const
    {
        wxWindow *handler = GetHandler();
        event.SetEventObject( handler );
        handler->HandleWindowEvent( event );
    }

    virtual Handler *GetHandler() const
    {
        return m_handler;
    }

private:
    Handler *m_handler;
};

template < typename Widget, typename Handler >
class wxQtEventSignalHandler : public Widget, public wxQtSignalHandler< Handler >
{
public:
    wxQtEventSignalHandler( wxWindow *parent, Handler *handler )
        : Widget( parent != NULL ? parent->GetHandle() : NULL )
        , wxQtSignalHandler< Handler >( handler )
    {
        // Set immediately as it is used to check if wxWindow is alive
        wxWindow::QtStoreWindowPointer( this, handler );

        // Handle QWidget destruction signal AFTER it gets deleted
        QObject::connect( this, &QObject::destroyed, this,
                          &wxQtEventSignalHandler::HandleDestroyedSignal );

        Widget::setMouseTracking(true);
    }

    void HandleDestroyedSignal()
    {
    }

    virtual Handler *GetHandler() const wxOVERRIDE
    {
        // Only process the signal / event if the wxWindow is not destroyed
        if ( !wxWindow::QtRetrieveWindowPointer( this ) )
        {
            return NULL;
        }
        else
            return wxQtSignalHandler< Handler >::GetHandler();
    }

protected:
    /* Not implemented here: wxHelpEvent, wxIdleEvent wxJoystickEvent,
     * wxMouseCaptureLostEvent, wxMouseCaptureChangedEvent,
     * wxPowerEvent, wxScrollWinEvent, wxSysColourChangedEvent */

    //wxActivateEvent
    virtual void changeEvent ( QEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleChangeEvent(this, event) )
            Widget::changeEvent(event);
        else
            event->accept();
    }

    //wxCloseEvent
    virtual void closeEvent ( QCloseEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleCloseEvent(this, event) )
            Widget::closeEvent(event);
        else
            event->ignore();
    }

    //wxContextMenuEvent
    virtual void contextMenuEvent ( QContextMenuEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleContextMenuEvent(this, event) )
            Widget::contextMenuEvent(event);
        else
            event->accept();
    }

    //wxDropFilesEvent
    //virtual void dropEvent ( QDropEvent * event ) { }

    //wxMouseEvent
    virtual void enterEvent ( QEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleEnterEvent(this, event) )
            Widget::enterEvent(event);
        else
            event->accept();
    }

    //wxFocusEvent.
    virtual void focusInEvent ( QFocusEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleFocusEvent(this, event) )
            Widget::focusInEvent(event);
        else
            event->accept();
    }

    //wxFocusEvent.
    virtual void focusOutEvent ( QFocusEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleFocusEvent(this, event) )
            Widget::focusOutEvent(event);
        else
            event->accept();
    }

    //wxShowEvent
    virtual void hideEvent ( QHideEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleShowEvent(this, event) )
            Widget::hideEvent(event);
        else
            event->accept();
    }

    //wxKeyEvent
    virtual void keyPressEvent ( QKeyEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleKeyEvent(this, event) )
            Widget::keyPressEvent(event);
        else
            event->accept();
    }

    //wxKeyEvent
    virtual void keyReleaseEvent ( QKeyEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleKeyEvent(this, event) )
            Widget::keyReleaseEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void leaveEvent ( QEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleEnterEvent(this, event) )
            Widget::leaveEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void mouseDoubleClickEvent ( QMouseEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleMouseEvent(this, event) )
            Widget::mouseDoubleClickEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void mouseMoveEvent ( QMouseEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleMouseEvent(this, event) )
            Widget::mouseMoveEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void mousePressEvent ( QMouseEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleMouseEvent(this, event) )
            Widget::mousePressEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void mouseReleaseEvent ( QMouseEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleMouseEvent(this, event) )
            Widget::mouseReleaseEvent(event);
        else
            event->accept();
    }

    //wxMoveEvent
    virtual void moveEvent ( QMoveEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleMoveEvent(this, event) )
            Widget::moveEvent(event);
        else
            event->accept();
    }

    //wxEraseEvent then wxPaintEvent
    virtual void paintEvent ( QPaintEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandlePaintEvent(this, event) )
            Widget::paintEvent(event);
        else
            event->accept();
    }

    //wxSizeEvent
    virtual void resizeEvent ( QResizeEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleResizeEvent(this, event) )
            Widget::resizeEvent(event);
        else
            event->accept();
    }

    //wxShowEvent
    virtual void showEvent ( QShowEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleShowEvent(this, event) )
            Widget::showEvent(event);
        else
            event->accept();
    }

    //wxMouseEvent
    virtual void wheelEvent ( QWheelEvent * event ) wxOVERRIDE
    {
        if ( !this->GetHandler() )
            return;

        if ( !this->GetHandler()->QtHandleWheelEvent(this, event) )
            Widget::wheelEvent(event);
        else
            event->accept();
    }

    /* Unused Qt events
    virtual void actionEvent ( QActionEvent * event ) { }
    virtual void dragEnterEvent ( QDragEnterEvent * event ) { }
    virtual void dragLeaveEvent ( QDragLeaveEvent * event ) { }
    virtual void dragMoveEvent ( QDragMoveEvent * event ) { }
    virtual void inputMethodEvent ( QInputMethodEvent * event ) { }
    virtual bool macEvent ( EventHandlerCallRef caller, EventRef event ) { }
    virtual bool qwsEvent ( QWSEvent * event ) { }
    virtual void tabletEvent ( QTabletEvent * event ) { }
    virtual bool winEvent ( MSG * message, long * result ) { }
    virtual bool x11Event ( XEvent * event ) { } */

    virtual bool event(QEvent *event)
    {
        if (event->type() == QEvent::Gesture)
        {
            return gestureEvent(static_cast<QGestureEvent*>(event), event);
        }

        return Widget::event(event);
    }

    bool gestureEvent(QGestureEvent *gesture, QEvent *event)
    {
        if (QGesture *tah = gesture->gesture(Qt::TapAndHoldGesture))
        {
            //  Set the policy so that accepted gestures are taken by the first window that gets them
            tah->setGestureCancelPolicy ( QGesture::CancelAllInContext );
            tapandholdTriggered(static_cast<QTapAndHoldGesture *>(tah), event);
        }

        if (QGesture *pan = gesture->gesture(Qt::PanGesture))
        {
            panTriggered(static_cast<QPanGesture *>(pan), event);
        }

        if (QGesture *pinch = gesture->gesture(Qt::PinchGesture))
        {
            pinchTriggered(static_cast<QPinchGesture *>(pinch), event);
        }

        return true;
    }

    void tapandholdTriggered(QTapAndHoldGesture *gesture, QEvent *event)
    {
        wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );

        if (gesture->state() == Qt::GestureFinished)
        {
            if ( win )
            {
                wxLongPressEvent ev(win->GetId());
                ev.SetPosition( wxQtConvertPoint( gesture->position().toPoint() ) );

                ev.SetGestureEnd();
                win->ProcessWindowEvent( ev );
                event->accept();
            }

        }
        else if (gesture->state() == Qt::GestureStarted)
        {
            event->accept();
        }
        else
        {
            event->accept();
        }
    }

    void panTriggered(QPanGesture *gesture, QEvent *event)
    {
        wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );

        if (win)
        {
            wxPanGestureEvent evp(win->GetId());
            QPoint pos = QCursor::pos();
            evp.SetPosition( wxQtConvertPoint( pos ) );

            QPoint offset = gesture->offset().toPoint();
            QPoint offset_last = gesture->lastOffset().toPoint();
            QPoint delta(offset.x() - offset_last.x(), offset.y() - offset_last.y());

            evp.SetDelta( wxQtConvertPoint( delta ) );

            switch(gesture->state())
            {
                case Qt::GestureStarted:
                    evp.SetGestureStart();
                    break;
                case Qt::GestureFinished:
                case Qt::GestureCanceled:
                    evp.SetGestureEnd();
                    break;
                default:
                    break;
            }

            win->ProcessWindowEvent( evp );

            event->accept();
        }
    }

    void pinchTriggered(QPinchGesture *gesture, QEvent *event)
    {
        wxWindow *win = wxWindow::QtRetrieveWindowPointer( this );
        if (win)
        {

            qreal this_sf = gesture->scaleFactor();
            QPoint center_point = gesture->centerPoint().toPoint();

            wxZoomGestureEvent evp(win->GetId());
            evp.SetPosition( wxQtConvertPoint( center_point ) );
            evp.SetZoomFactor( this_sf);

            switch(gesture->state())
            {
                case Qt::GestureStarted:
                    evp.SetGestureStart();
                    break;
                case Qt::GestureFinished:
                case Qt::GestureCanceled:
                    evp.SetGestureEnd();
                    break;
                default:
                    break;
            }

            win->ProcessWindowEvent( evp );

            event->accept();

        }
    }
};

#endif
