//------------------------------------------------------------------------------
// emViewAnimator.h
//
// Copyright (C) 2014-2017 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 emViewAnimator_h
#define emViewAnimator_h

#ifndef emView_h
#include <emCore/emView.h>
#endif


//==============================================================================
//=============================== emViewAnimator ===============================
//==============================================================================

class emViewAnimator : public emEngine {

public:

        emViewAnimator(emView & view);
        virtual ~emViewAnimator();

        emView & GetView() const;

        void SetMaster(emViewAnimator * master);
        emViewAnimator * GetMaster() const;
        emViewAnimator * GetActiveSlave() const;

        bool IsActive() const;
        virtual void Activate();
        virtual void Deactivate();

        bool IsDeactivatingWhenIdle() const;
        void SetDeactivateWhenIdle(bool deactivateWhenIdle=true);

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

protected:

        virtual bool Cycle();

        virtual bool CycleAnimation(double dt) = 0;

        void InvalidatePainting();

private:

        emView & View;
        emViewAnimator * Master;
        emViewAnimator * ActiveSlave;
        emViewAnimator * * UpperActivePtr;
        emUInt64 LastTSC;
        emUInt64 LastClk;
        bool DeactivateWhenIdle;
};

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

inline emViewAnimator * emViewAnimator::GetMaster() const
{
        return Master;
}

inline emViewAnimator * emViewAnimator::GetActiveSlave() const
{
        return ActiveSlave;
}

inline bool emViewAnimator::IsActive() const
{
        return *UpperActivePtr==this;
}

inline bool emViewAnimator::IsDeactivatingWhenIdle() const
{
        return DeactivateWhenIdle;
}

inline void emViewAnimator::InvalidatePainting()
{
        View.InvalidatePainting();
}


//==============================================================================
//=========================== emKineticViewAnimator ============================
//==============================================================================

class emKineticViewAnimator : public emViewAnimator {

public:

        emKineticViewAnimator(emView & view);
        virtual ~emKineticViewAnimator();

        virtual void Activate();
        virtual void Deactivate();

        double GetVelocity(int dimension) const;
        double GetAbsVelocity() const;
        void SetVelocity(int dimension, double velocity);

        void CenterZoomFixPoint();
        void SetZoomFixPoint(double zoomFixX, double zoomFixY);

        void SetFrictionEnabled(bool enabled);
        bool IsFrictionEnabled() const;
        double GetFriction() const;
        void SetFriction(double friction);

protected:

        virtual bool CycleAnimation(double dt);

private:

        void UpdateBusyState();
        void UpdateZoomFixPoint();

        double Velocity[3];
        bool ZoomFixPointCentered;
        double ZoomFixX,ZoomFixY;
        bool FrictionEnabled;
        double Friction;
        bool Busy;
};

inline double emKineticViewAnimator::GetVelocity(int dimension) const
{
        return Velocity[dimension];
}

inline bool emKineticViewAnimator::IsFrictionEnabled() const
{
        return FrictionEnabled;
}

inline double emKineticViewAnimator::GetFriction() const
{
        return Friction;
}




//==============================================================================
//=========================== emSpeedingViewAnimator ===========================
//==============================================================================

class emSpeedingViewAnimator : public emKineticViewAnimator {

public:

        emSpeedingViewAnimator(emView & view);

        virtual ~emSpeedingViewAnimator();

        virtual void Activate();
        virtual void Deactivate();

        double GetTargetVelocity(int dimension) const;
        double GetAbsTargetVelocity() const;
        void SetTargetVelocity(int dimension, double targetVelocity);

        double GetAcceleration() const;
        void SetAcceleration(double acceleration);

        double GetReverseAcceleration() const;
        void SetReverseAcceleration(double reverseAcceleration);

protected:

        virtual bool CycleAnimation(double dt);

private:

        void UpdateBusyState();

        double TargetVelocity[3];
        double Acceleration;
        double ReverseAcceleration;
        bool Busy;
};

inline double emSpeedingViewAnimator::GetTargetVelocity(int dimension) const
{
        return TargetVelocity[dimension];
}

inline double emSpeedingViewAnimator::GetAcceleration() const
{
        return Acceleration;
}

inline double emSpeedingViewAnimator::GetReverseAcceleration() const
{
        return ReverseAcceleration;
}


//==============================================================================
//=========================== emSwipingViewAnimator ============================
//==============================================================================

class emSwipingViewAnimator : public emKineticViewAnimator {

public:

        emSwipingViewAnimator(emView & view);
        virtual ~emSwipingViewAnimator();

        virtual void Activate();
        virtual void Deactivate();

        bool IsGripped() const;
        void SetGripped(bool gripped);

        void MoveGrip(int dimension, double distance);

        double GetSpringConstant() const;
        void SetSpringConstant(double springConstant);

        double GetAbsSpringExtension() const;

protected:

        virtual bool CycleAnimation(double dt);

private:

        void UpdateBusyState();

        bool Gripped;
        double SpringExtension[3];
        double InstantaneousVelocity[3];
        double SpringConstant;
        bool Busy;
};

inline bool emSwipingViewAnimator::IsGripped() const
{
        return Gripped;
}

inline double emSwipingViewAnimator::GetSpringConstant() const
{
        return SpringConstant;
}


//==============================================================================
//=========================== emMagneticViewAnimator ===========================
//==============================================================================

class emMagneticViewAnimator : public emKineticViewAnimator {

public:

        emMagneticViewAnimator(emView & view);
        virtual ~emMagneticViewAnimator();

        virtual void Activate();
        virtual void Deactivate();

protected:

        virtual bool CycleAnimation(double dt);

private:

        double CalculateDistance(double * pDX, double * pDY, double * pDZ) const;

        void GetViewRect(double * pX, double * pY,
                         double * pW, double * pH) const;

        emRef<emCoreConfig> CoreConfig;
        bool MagnetismActive;
};


//==============================================================================
//=========================== emVisitingViewAnimator ===========================
//==============================================================================

class emVisitingViewAnimator : public emViewAnimator {

public:

        emVisitingViewAnimator(emView & view);
        virtual ~emVisitingViewAnimator();

        bool IsAnimated() const;
        void SetAnimated(bool animated=true);

        double GetAcceleration() const;
        void SetAcceleration(double acceleration);

        double GetMaxCuspSpeed() const;
        void SetMaxCuspSpeed(double maxCuspSpeed);

        double GetMaxAbsoluteSpeed() const;
        void SetMaxAbsoluteSpeed(double maxAbsoluteSpeed);

        void SetAnimParamsByCoreConfig(const emCoreConfig & coreConfig);

        void SetGoal(const char * identity, bool adherent,
                     const char * subject=NULL);
        void SetGoal(const char * identity, double relX, double relY, double relA,
                     bool adherent, const char * subject=NULL);
        void SetGoalFullsized(const char * identity, bool adherent,
                              bool utilizeView=false, const char * subject=NULL);
        void ClearGoal();

        bool HasGoal() const;
        bool HasReachedGoal() const;
        bool HasGivenUp() const;

        virtual void Activate();
        virtual void Deactivate();
        virtual void Input(emInputEvent & event, const emInputState & state);
        virtual void Paint(const emPainter & painter) const;

protected:

        virtual bool CycleAnimation(double dt);

private:

        enum StateEnum {
                ST_NO_GOAL,
                ST_CURVE,
                ST_DIRECT,
                ST_SEEK,
                ST_GIVING_UP,
                ST_GIVEN_UP,
                ST_GOAL_REACHED
        };

        enum VisitTypeEnum {
                VT_VISIT,
                VT_VISIT_REL,
                VT_VISIT_FULLSIZED
        };

        struct CurvePoint {
                double X;
                double Z;
        };

        void SetGoal(
                VisitTypeEnum visitType, const char * identity, double relX, double relY,
                double relA, bool adherent, bool utilizeView, const char * subject
        );

        void UpdateSpeed(
                double pos, double dist, int panelsAfter, double distFinal, double dt
        );

        emPanel * GetNearestExistingPanel(
                double * pRelX, double * pRelY, double * pRelA, bool * pAdherent,
                int * pDepth, int * pPanelsAfter, double * pDistFinal
        ) const;

        emPanel * GetNearestViewedPanel(emPanel * nearestExistingPanel) const;

        void GetDistanceTo(
                emPanel * panel, double relX, double relY, double relA,
                double * pDirX, double * pDirY, double * pDistXY, double * pDistZ
        ) const;

        void GetViewRect(
                double * pX, double * pY, double * pW, double * pH
        ) const;

        static double GetDirectDist(double x, double z);

        static void GetDirectPoint(
                double x, double z, double d,
                double * pX, double * pZ
        );

        static void GetCurvePosDist(
                double x, double z, double * pCurvePos, double * pCurveDist
        );

        static CurvePoint GetCurvePoint(double d);

        bool Animated;
        double Acceleration;
        double MaxCuspSpeed;
        double MaxAbsoluteSpeed;
        StateEnum State;
        VisitTypeEnum VisitType;
        emString Identity;
        double RelX,RelY,RelA;
        bool Adherent;
        bool UtilizeView;
        emString Subject;
        emArray<emString> Names;
        int MaxDepthSeen;
        double Speed;
        int TimeSlicesWithoutHope;
        emUInt64 GiveUpClock;

        static const double CurveDeltaDist;
        static const CurvePoint CurvePoints[];
        static const int CurveMaxIndex;
};

inline bool emVisitingViewAnimator::IsAnimated() const
{
        return Animated;
}

inline double emVisitingViewAnimator::GetAcceleration() const
{
        return Acceleration;
}

inline double emVisitingViewAnimator::GetMaxCuspSpeed() const
{
        return MaxCuspSpeed;
}

inline double emVisitingViewAnimator::GetMaxAbsoluteSpeed() const
{
        return MaxAbsoluteSpeed;
}

inline bool emVisitingViewAnimator::HasGoal() const
{
        return State!=ST_NO_GOAL;
}

inline bool emVisitingViewAnimator::HasReachedGoal() const
{
        return State==ST_GOAL_REACHED;
}

inline bool emVisitingViewAnimator::HasGivenUp() const
{
        return State==ST_GIVEN_UP;
}


#endif