/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

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 for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef PL_HK_CALLBACK_ACTION_H
#define PL_HK_CALLBACK_ACTION_H

#include "hsGeometry3.h"
#include "hsMatrix44.h"
#include "hsTemplates.h"
#include "pnKeyedObject/plKey.h"
#include "plPhysical/plSimDefs.h"
#include "pnMessage/plMessage.h"
#include "plPhysicalControllerCore.h"
class plLOSHitMsg;
class plAGApplicator;
class plSceneObject;
class plPhysical;
class plAvatarController;
class plCoordinateInterface;
class plPhysicalControllerCore;
// Used by the other controllers to actually move the avatar.  The actual
// implementation is in the physics system.
/*class plPhysicalController
{
public:
    // Implemented in the physics system.  If you're linking this without that for
    // some reason, just stub this function out.
    //
    // Pass in the key to the root sceneobject for the avatar
    static plPhysicalController* Create(plKey ownerSO, float height, float width);

    virtual ~plPhysicalController() {}

    // A disabled avatar doesn't move or accumulate air time if he's off the ground.
    virtual void Enable(bool enable) = 0;
    virtual bool IsEnabled() const = 0;

    // Set the LOS DB this avatar will be in (only one)
    virtual void SetLOSDB(plSimDefs::plLOSDB losDB) = 0;

    // Call this once per frame with the velocities of the avatar in avatar space.
    virtual void SetVelocities(const hsVector3& linearVel, float angVel) = 0;

    // Gets the actual velocity we achieved in the last step (relative to our subworld)
    virtual const hsVector3& GetLinearVelocity() const = 0;
    virtual void ResetAchievedLinearVelocity() = 0;

    // Get and set the current subworld for the avatar.  Use nil for the main world.
    virtual plKey GetSubworld() const = 0;
    virtual void SetSubworld(plKey world) = 0;

    // If IsOnGround returns false, GetAirTime will tell you how long the avatar
    // has been airborne.  Use ResetAirTime to reset the air time to zero, for
    // cases like when the avatar spawns into a new age.
    virtual bool IsOnGround() const = 0;
    virtual bool IsOnFalseGround() const = 0;
    virtual float GetAirTime() const = 0;
    virtual void ResetAirTime() = 0;

    virtual plPhysical* GetPushingPhysical() const = 0;
    virtual bool        GetFacingPushingPhysical() const = 0;

    // A helper function to get the coordinate interface for the avatars current
    // world.  Handy if you need to convert points to and from that.  This will
    // return nil if the avatar is in the main world (ie, you don't need to do
    // any translation).
    virtual const plCoordinateInterface* GetSubworldCI() const = 0;

    // For the avatar SDL only
    virtual void GetState(hsPoint3& pos, float& zRot) = 0;
    virtual void SetState(const hsPoint3& pos, float zRot) = 0;

    // kinematic stuff .... should be just for when playing a behavior...
    virtual void Kinematic(bool state) = 0;
    virtual bool IsKinematic() = 0;
    virtual void GetKinematicPosition(hsPoint3& pos) = 0;

    virtual const hsMatrix44& GetPrevSubworldW2L() = 0;

    //when seeking no longer want to interact with exclusion regions
    virtual void SetSeek(bool seek)=0;


};
*/
class plAvatarController
{
public:
    virtual ~plAvatarController() {}
};

class plAnimatedController : public plAvatarController
{
public:
    plAnimatedController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller);

    virtual void RecalcVelocity(double timeNow, double timePrev, hsBool useAnim = true);
    void SetTurnStrength(float val) { fTurnStr = val; }
    float GetTurnStrength() { return fTurnStr; }
    virtual void ActivateController()=0;
protected:
    plSceneObject* fRootObject;
    plPhysicalControllerCore* fController;
    plAGApplicator* fRootApp;
    float fAnimAngVel;
    hsVector3 fAnimPosVel;
    float fTurnStr; // Explicit turning, separate from animations
};

class plWalkingController : public plAnimatedController
{
public:
    plWalkingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller);
    virtual ~plWalkingController();
    virtual void RecalcVelocity(double timeNow, double timePrev, hsBool useAnim = true);

    void Reset(bool newAge);
    bool IsControlledFlight() const { return fControlledFlight != 0; }  
    bool IsOnGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnGround() : true; }
    bool IsOnFalseGround() const { return fWalkingStrategy ? fWalkingStrategy->IsOnFalseGround() : true; }
    bool HitGroundInThisAge() const { return fHitGroundInThisAge; }
    bool EnableControlledFlight(bool status);
    float GetAirTime() const { return fWalkingStrategy ? fWalkingStrategy->GetAirTime() : 0.f; }
    void ResetAirTime() { if (fWalkingStrategy) fWalkingStrategy->ResetAirTime(); }
    float GetForwardVelocity() const;
    void ActivateController();
    // Check these after the avatar the avatar hits the ground for his total
    // hangtime and impact velocity.
    float GetImpactTime() const { return fImpactTime; }
    const hsVector3& GetImpactVelocity() const { return fImpactVelocity; }

    plPhysical* GetPushingPhysical() const 
    {
        if(fController)return fController->GetPushingPhysical(); 
        else return nil;
    }
    bool GetFacingPushingPhysical() const 
    {   if(fController)return fController->GetFacingPushingPhysical(); 
        else return false;
    }

    
protected:
    bool        fHitGroundInThisAge;
    bool        fWaitingForGround;      // We've gone airborne. IsOnGround() returns false until we hit ground again.
    float    fControlledFlightTime;
    int         fControlledFlight; // Count of how many are currently forcing flight        
    plWalkingStrategy* fWalkingStrategy;
    float fImpactTime;
    hsVector3 fImpactVelocity;
    bool fClearImpact;
    bool fGroundLastFrame;//used for a test to pass the event of first getting air during a jump
    static const float kControlledFlightThreshold;
};
class plSwimmingController: public plAnimatedController 
{
public :
    plSwimmingController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller);
    virtual ~plSwimmingController();
    void SetSurface(plSwimRegionInterface *region, float surfaceHeight){
        fSwimmingStrategy->SetSurface(region,surfaceHeight);
    }
    float GetBuoyancy() { return fSwimmingStrategy->GetBuoyancy(); }
    hsBool IsOnGround() { return fSwimmingStrategy->IsOnGround(); }
    hsBool HadContacts() { return fSwimmingStrategy->HadContacts();}
    void Enable(bool en){if (fController) fController->Enable(en);}
    plPhysicalControllerCore* GetController(){return fController;}
    virtual void ActivateController(){fSwimmingStrategy->RefreshConnectionToControllerCore();}
protected:
    plSwimStrategy* fSwimmingStrategy;

};
class plRidingAnimatedPhysicalController: public plWalkingController
{
public:
    plRidingAnimatedPhysicalController(plSceneObject* rootObject, plAGApplicator* rootApp, plPhysicalControllerCore* controller);
    virtual ~plRidingAnimatedPhysicalController();
};
#endif // PL_HK_CALLBACK_ACTION_H
