/*==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==*/
/** \file plAGMasterMod.h
    \brief The animation class for the AniGraph animation system

    \ingroup Avatar
    \ingroup AniGraph
*/
#ifndef PLAGMASTERMOD_INC
#define PLAGMASTERMOD_INC

/////////////////////////////////////////////////////////////////////////////////////////
//
// INCLUDES
//
/////////////////////////////////////////////////////////////////////////////////////////
#include "pnModifier/plModifier.h"
#include "plAGChannel.h"
#include "plAvDefs.h"
#include "pnKeyedObject/plMsgForwarder.h"

// templates
#include "hsStlUtils.h"
#include "hsStlSortUtils.h"


class plAGModifier;
class plAGAnimInstance;
class plAGAnim;
class plATCAnim;
class plAGMasterSDLModifier;

////////////////
//
// PLAGMASTERMOD
//
////////////////
/** \class plAGMasterMod
    A modifier which can apply animations to scene objects. 
    Works together with a plAGModifier, which can apply animation to a
    single scene object. A master modifier hooks up to a family of
    ag modifiers to do its job.
    An animation (plAGAnim) can have many different channels, but the
    user can just pass it to a single master mod to apply it to all the
    channels owned by that master. The master will take care of hooking
    each channel up to an ag modifier.
    The goal is to make an animation a conceptually (and practically) simple
    object to work with, whether or not it has multiple channels.

    \sa plAGAnim, plAGAnimInstance, plAGModifier
    */
class plAGMasterMod : public plModifier
{
    friend class plAGMasterSDLModifier;
public:
    /** Default constructor. Primarily for use by the class factory. */
    plAGMasterMod();
    /** Free the name and any other miscellany. */
    virtual ~plAGMasterMod();

    /** Find an individual plAGModifier of the given name under our control. */
    plAGModifier * GetChannelMod(const plString & name, hsBool dontCache = false) const;

    /** \name Managing Animations */
    // \{
    // AGANIM PROTOCOL
    /** Attach the given animation object with the given blend factor.
        If there's no animation already attached to blend with, the
        animation will be attached at full strength. */
    plAGAnimInstance *AttachAnimationBlended(plAGAnim *anim, float blendFactor = 0,
                                             uint16_t blendPriority = kAGMedBlendPriority,
                                             hsBool cache = false);

    /** Look up the given animation by name and attach it
        with the given blend factor. */
    plAGAnimInstance *AttachAnimationBlended(const plString &name, float blendFactor = 0,
                                             uint16_t blendPriority = kAGMedBlendPriority,
                                             hsBool cache = false);

    /** Play a simple anim (one that doesn't affect root) once and auto detach. 
        Intended for Zandi's facial animations that run seperate from the behaviors. */
    void PlaySimpleAnim(const plString &name);
                                             
    /** Detach the given animation instance. Does nothing
        if the instance is not managed by this master mod. */
    void DetachAnimation(plAGAnimInstance *instance);
    void DetachAllAnimations();

    /** Detach the given animation by name. Searches for
        any instances derived from animations with the
        given name and removes them. */
    void DetachAnimation(const plString &name);
    // \}

    /** Print the current animation stack to the console.
        Will list all the animations and their blend strengths.
        Animations later in the list will mask animations earlier
        in the list. */
    void DumpCurrentAnims(const char *header);

    /** Find and return any animation instances with the
        given name on this master modifer. */
    plAGAnimInstance *FindAnimInstance(const plString &name);

    /** Return the Ith animation instance, based on blend
        order. Of dubious utility, but, y'know. */
    plAGAnimInstance *GetAnimInstance(int i);

    /** Attach the animation if it's not already attached. If
        it is attached, return the instance.
        Note that if it's attached by this function, it 
        will be on top of the stack, but if it was already
        attached, it could be anywhere, including buried under
        a bunch of other animations. If it's important that it be
        on top of the stack, you may need to detach it first. */
    plAGAnimInstance *FindOrAttachInstance(const plString &name, float blendFactor);

    /** Return the number of animations available. */
    int GetNumAnimations();
    /** Return the number of animations that are privately
        owned by this modifier.
        Animations may be either shared in a general pool,
        or privately owned by a mastermod. */
    int GetNumPrivateAnimations();

    int GetNumATCAnimations();
    plAGAnimInstance *GetATCAnimInstance(int i);

    /** Apply all our animations to all our parts.
        \param timeNow is the current world time
        \param elapsed is the time since the previous frame */
    void ApplyAnimations(double timeNow, float elapsed);

    /** Runs through our anims and applies them, without
        processing fades. This is used when we load in anim
        state from the server, and need to advance it to a
        certain point before enabling callbacks */
    void AdvanceAnimsToTime(double time);

    /** Change the connectivity in the graph so that inactive animations are bypassed.
        The original connectivity information is kept, so if the activity of different
        animations is changed (such as by changing blend biases or adding new animations,
        the graph can be compiled again to the correct state. */
    void Compile(double time);

    /** We've done something that invalidates the cached connectivity in the graph.
        Mark this for fixup. */
    void SetNeedCompile(bool needCompile);
    
    /** List the animationg graph to stdOut, with a ASCII representation of the tree
        structure. Done by recursively dumping the graph; some types of nodes will have
        more output information than others.
        */
    void DumpAniGraph(const char *channel, bool optimized, double time);

    /** Set whether or not this is the "group master" so grouped animations will only have
        one member getting/setting sdl animation state in order to synch the anims
        */
    void SetIsGrouped(bool grouped);
    void SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder);

    // PLASMA PROTOCOL
    virtual int GetNumTargets() const { return fTarget ? 1 : 0; }
    virtual plSceneObject* GetTarget(int w) const { /* hsAssert(w < GetNumTargets(), "Bad target"); */ return fTarget; }
    virtual void AddTarget(plSceneObject * object);
    virtual void RemoveTarget(plSceneObject * object);

    hsBool MsgReceive(plMessage* msg);

    virtual void Write(hsStream *stream, hsResMgr *mgr);
    virtual void Read(hsStream * stream, hsResMgr *mgr);

    hsBool HasRunningAnims();
    hsBool DirtySynchState(const char* SDLStateName, uint32_t synchFlags);    
    
    CLASSNAME_REGISTER( plAGMasterMod );
    GETINTERFACE_ANY( plAGMasterMod, plModifier );

protected:
    // -- methods --
    plAGModifier * ICacheChannelMod(plAGModifier *mod) const;
    plAGModifier * IFindChannelMod(const plSceneObject *obj, const plString &name) const;

    virtual hsBool IEval(double secs, float del, uint32_t dirty);
    
    virtual void IApplyDynamic() {};    // dummy function required by base class

    // Find markers in an anim for environment effects (footsteps)
    virtual void ISetupMarkerCallbacks(plATCAnim *anim, plAnimTimeConvert *atc) {}

    // -- members
    plSceneObject*  fTarget;

    // a map from channel names to ag modifiers within our domain
    typedef std::map<plString, plAGModifier*> plChannelModMap;
    plChannelModMap fChannelMods;

    // instances which are currently attached to this master
    typedef std::vector<plAGAnimInstance*> plInstanceVector;
    plInstanceVector fAnimInstances;                    // animation instances

    // animations which are owned exclusively by this master
    typedef std::vector<plAGAnim*> plAnimVector;
    plAnimVector fPrivateAnims;

    // animations that require AnimTimeConvert state to be synched
    plInstanceVector fATCAnimInstances;
    
    hsBool fFirstEval;
    hsBool fNeedEval;
    void IRegForEval(hsBool val);

    // SDL modifier which sends/recvs dynamics state
    plAGMasterSDLModifier *fAGMasterSDLMod; 

    bool fNeedCompile;

    bool fIsGrouped;
    bool fIsGroupMaster;
    plMsgForwarder* fMsgForwarder;
    
    enum {
        kPrivateAnim,
        kPublicAnim,
    };
};

#endif // PLAGMASTERMOD_INC
