//------------------------------------------------------------------------------
// emViewInputFilter.h
//
// Copyright (C) 2011-2012,2014,2016,2022 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emViewInputFilter_h
#define emViewInputFilter_h

#ifndef emViewAnimator_h
#include <emCore/emViewAnimator.h>
#endif


//==============================================================================
//============================= emViewInputFilter ==============================
//==============================================================================

class emViewInputFilter : public emEngine {

public:

        // Base class for an input event filter of an emView. Each view can have
        // a list of filters. Such a filter typically eats certain events for
        // zooming and scrolling the view.

        emViewInputFilter(emView & view, emViewInputFilter * next=NULL);
                // Construct a view input filter and insert it to the list of
                // filters of a view.
                // Arguments:
                //   view - The view.
                //   next - The next filter in the list. This filter is
                //          inserted before that filter. If next is NULL, this
                //          filter is appended to the end of the list.

        virtual ~emViewInputFilter();
                // Destruct this view input filter. This also removes the filter
                // from the list.

        emView & GetView() const;
                // Get the view.

        emViewInputFilter * GetPrev() const;
        emViewInputFilter * GetNext() const;
                // Get the previous or next filter in the list. NULL means this
                // is the first or last filter.

        virtual double GetTouchEventPriority(double touchX, double touchY) const;
                // Get the maximum touch event priority of this view input
                // filter and all its successors and all the panels of the view
                // for a certain touch position. The default implementation
                // calls GetForwardTouchEventPriority. This should usually also
                // be made by overloaded implementations. See the comments on
                // emPanel::GetTouchEventPriority for more.
                // Arguments:
                //   touchX, touchY - Position of a first touch in view
                //                    coordinates.

protected:

        virtual void Input(emInputEvent & event, const emInputState & state);
                // Process input form keyboard, mouse, and touch. The default
                // implementation calls ForwardInput. This should usually also
                // be made by overloaded implementations.
                // Arguments:
                //   event  - An input event. It may be eaten by calling
                //            event.Eat(). The event reference in non-const only
                //            for that.
                //   state  - The current input state.

        virtual bool Cycle();
                // emViewInputFilter has been derived from emEngine for
                // convenience. This default implementation does nothing and
                // returns false.

        void ForwardInput(emInputEvent & event, const emInputState & state);
                // Forward input to succeeding filters and the panels. Actually
                // this calls Input on the next filter, or on the view if this
                // is the last filter.
                // Arguments:
                //   event  - An input event. It may be eaten by calling
                //            event.Eat(). The event reference in non-const only
                //            for that.
                //   state  - The current input state.

        double GetForwardTouchEventPriority(double touchX, double touchY) const;
                // Get the maximum touch event priority of succeeding filters
                // and the panels. Actually this calls GetTouchEventPriority on
                // the next filter, or on the view if this is the last filter.
                // Arguments:
                //   touchX, touchY - Position of a first touch in view
                //                    coordinates.

private:
        friend class emViewPort;
        emView & View;
        emViewInputFilter * Prev;
        emViewInputFilter * Next;
};

inline emView & emViewInputFilter::GetView() const
{
        return View;
}

inline emViewInputFilter * emViewInputFilter::GetPrev() const
{
        return Prev;
}

inline emViewInputFilter * emViewInputFilter::GetNext() const
{
        return Next;
}

inline void emViewInputFilter::ForwardInput(
        emInputEvent & event, const emInputState & state
)
{
        if (!Next) View.Input(event,state);
        else Next->Input(event,state);
}

inline double emViewInputFilter::GetForwardTouchEventPriority(
        double touchX, double touchY
) const
{
        if (!Next) return View.GetTouchEventPriority(touchX,touchY,true);
        else return Next->GetTouchEventPriority(touchX,touchY);
}


//==============================================================================
//============================ emMouseZoomScrollVIF ============================
//==============================================================================

class emMouseZoomScrollVIF : public emViewInputFilter {

public:

        // This view input filter eats some mouse events for zooming and
        // scrolling.

        emMouseZoomScrollVIF(emView & view, emViewInputFilter * next=NULL);
        virtual ~emMouseZoomScrollVIF();

protected:

        virtual void Input(emInputEvent & event, const emInputState & state);

        virtual bool Cycle();

private:

        void EmulateMiddleButton(emInputEvent & event, emInputState & state);
        bool MoveMousePointerBackIntoView(double * pmx, double * pmy);
        bool MoveMousePointer(double dx, double dy);
        double GetMouseZoomSpeed(bool fine=false) const;
        double GetMouseScrollSpeed(bool fine=false) const;
        void UpdateWheelZoomSpeed(bool down, bool fine);
        void SetMouseAnimParams();
        void SetWheelAnimParams();
        void InitMagnetismAvoidance();
        void UpdateMagnetismAvoidance(double dmx, double dmy);

        emSwipingViewAnimator MouseAnim;
        emSwipingViewAnimator WheelAnim;
        emRef<emCoreConfig> CoreConfig;
        double LastMouseX,LastMouseY,ZoomFixX,ZoomFixY;
        emUInt64 EmuMidButtonTime;
        int EmuMidButtonRepeat;
        double WheelZoomSpeed;
        emUInt64 WheelZoomTime;
        bool MagnetismAvoidance;
        double MagAvMouseMoveX,MagAvMouseMoveY;
        emUInt64 MagAvTime;
};


//==============================================================================
//========================== emKeyboardZoomScrollVIF ===========================
//==============================================================================

class emKeyboardZoomScrollVIF : public emViewInputFilter {

public:

        // This view input filter eats some keyboard events for zooming and
        // scrolling.

        emKeyboardZoomScrollVIF(emView & view, emViewInputFilter * next=NULL);
        virtual ~emKeyboardZoomScrollVIF();

protected:

        virtual void Input(emInputEvent & event, const emInputState & state);

private:

        void NavigateByProgram(emInputEvent & event, const emInputState & state);
        double GetZoomSpeed(bool fine=false) const;
        double GetScrollSpeed(bool fine=false) const;
        void SetAnimatorParameters();

        emSpeedingViewAnimator Animator;
        emRef<emCoreConfig> CoreConfig;
        bool Active;
        int NavByProgState;
};


//==============================================================================
//================================= emCheatVIF =================================
//==============================================================================

class emCheatVIF : public emViewInputFilter {

public:

        // This view input filter implements some chat codes.

        emCheatVIF(emView & view, emViewInputFilter * next=NULL);
        virtual ~emCheatVIF();

protected:

        virtual void Input(emInputEvent & event, const emInputState & state);

private:

        emRef<emCoreConfig> CoreConfig;
        char CheatBuffer[64];
};


//==============================================================================
//============================= emDefaultTouchVIF ==============================
//==============================================================================

class emDefaultTouchVIF : public emViewInputFilter {

public:

        // This view input filter eats touch events for zooming and scrolling
        // and for emulating mouse events.

        emDefaultTouchVIF(emView & view, emViewInputFilter * next=NULL);
        virtual ~emDefaultTouchVIF();

        virtual double GetTouchEventPriority(double touchX, double touchY) const;

protected:

        virtual void Input(emInputEvent & event, const emInputState & state);

        virtual bool Cycle();

private:

        void DoGesture();
        void ResetTouches();
        void NextTouches();
        void RemoveTouch(int index);
        bool IsAnyTouchDown() const;
        double GetTouchMoveX(int index) const;
        double GetTouchMoveY(int index) const;
        double GetTouchMove(int index) const;
        double GetTotalTouchMoveX(int index) const;
        double GetTotalTouchMoveY(int index) const;
        double GetTotalTouchMove(int index) const;

        struct Touch {
                emUInt64 Id;
                int MsTotal;
                int MsSincePrev;
                bool Down;
                double X;
                double Y;
                bool PrevDown;
                double PrevX;
                double PrevY;
                double DownX;
                double DownY;
        };

        enum { MAX_TOUCH_COUNT=16 };

        emInputState InputState;
        emInputEvent InputEvent;
        Touch Touches[MAX_TOUCH_COUNT];
        int TouchCount;
        emUInt64 TouchesTime;
        int GestureState;
};


#endif