/*==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==*/
// singular
#include "plAGMasterMod.h"

// local
#include "plAGAnim.h"
#include "plAGAnimInstance.h"
#include "plAGModifier.h"
// #include "plAvatarAnim.h"
#include "plAGMasterSDLModifier.h"
#include "plMatrixChannel.h"

// global
#include "hsResMgr.h"
#include "plgDispatch.h"

// other
#include "plMessage/plAnimCmdMsg.h"
#include "pnMessage/plSDLModifierMsg.h"
#include "pnMessage/plSDLNotificationMsg.h"
#include "pnMessage/plTimeMsg.h"
#include "pnSceneObject/plSceneObject.h"
#include "pnSceneObject/plCoordinateInterface.h"

////////////////
// PLAGMASTERMOD
////////////////
// Coordinates the activities of a bunch of plAGModifiers
// std::map<char *, plAGMasterMod *, stringISorter> plAGMasterMod::fInstances;

// CTOR
plAGMasterMod::plAGMasterMod()
: fTarget(nil),
  fNeedEval(false),
  fFirstEval(true),
  fAGMasterSDLMod(nil),
  fNeedCompile(false),
  fIsGrouped(false),
  fIsGroupMaster(false),
  fMsgForwarder(nil)
{
}

// DTOR
plAGMasterMod::~plAGMasterMod()
{
}

void plAGMasterMod::Write(hsStream *stream, hsResMgr *mgr)
{
    plModifier::Write(stream, mgr);

    int length = 0;
    stream->WriteLE32(length); // backwards compatability. Nuke on next format change.
    stream->WriteLE32(fPrivateAnims.size());
    int i;
    for (i = 0; i < fPrivateAnims.size(); i++)
    {
        mgr->WriteKey(stream, fPrivateAnims[i]->GetKey());
    }
    stream->Writebool(fIsGrouped);
    stream->Writebool(fIsGroupMaster);
    if (fIsGroupMaster)
        mgr->WriteKey(stream, fMsgForwarder->GetKey());

    // maybe later... WriteCachedMessages(stream, mgr);
}

void plAGMasterMod::Read(hsStream * stream, hsResMgr *mgr)
{
    plModifier::Read(stream, mgr);

    //////////////////////////////////////////
    int nameLength = stream->ReadLE32();  // Unused. Nuke next format change.
    char *junk = new char[nameLength+1];    //
    stream->Read(nameLength, junk);         //
    junk[nameLength] = 0;                   //
    delete [] junk;                         //
    //////////////////////////////////////////

    int numPrivateAnims = stream->ReadLE32();
    fPrivateAnims.reserve(numPrivateAnims);             // pre-allocate for performance
    int i;
    for (i = 0; i < numPrivateAnims; i++)
    {
        plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPrivateAnim);
        mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
    }
    fIsGrouped = stream->Readbool();
    fIsGroupMaster = stream->Readbool();
    if (fIsGroupMaster)
    {
        plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
        mgr->ReadKeyNotifyMe(stream, msg, plRefFlags::kActiveRef);
    }

    // maybe later... ReadCachedMessages(stream, mgr);
}

// ADDTARGET
// Collect all the plAGModifiers from our children and attach private anims.
void plAGMasterMod::AddTarget(plSceneObject * object)
{
    plSynchEnabler p(false);    // turn off dirty tracking while in this function   
    
    fTarget = object;
    int autoIdx = -1;
    int initialIdx = -1;
    int timeIdx = 0;
    int i;

    for (i = 0; i < fPrivateAnims.size(); i++)
    {
        plATCAnim *atcAnim = plATCAnim::ConvertNoRef(fPrivateAnims[i]);
        if (!atcAnim)
            continue;

        if (atcAnim->GetAutoStart())
            autoIdx = i;
        if (atcAnim->GetInitial() != -1)
            initialIdx = i;
        if (atcAnim->GetStart() < fPrivateAnims[timeIdx]->GetStart())
            timeIdx = i;
    }

    int masterIdx;
    if (autoIdx != -1)
        masterIdx = autoIdx;        // If something autostarts, it wins.
    else if (initialIdx != -1)
        masterIdx = initialIdx;     // Otherwise, the fellow with the @initial point wins
    else
        masterIdx = timeIdx;        // Default case: the earliest anim wins

    for (i = 0; i < fPrivateAnims.size(); i++)
    {
        AttachAnimationBlended(fPrivateAnims[i], i == masterIdx ? 1.f : 0.f);
    }

    // Force one eval after we init
    plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());

    if (!fIsGrouped || fIsGroupMaster)
    {
        // add sdl modifier
        delete fAGMasterSDLMod;
        fAGMasterSDLMod = new plAGMasterSDLModifier;
        object->AddModifier(fAGMasterSDLMod);
    }
}

void plAGMasterMod::RemoveTarget(plSceneObject* o)
{
    hsAssert(o == fTarget, "Removing target I don't have");

    DetachAllAnimations();

    // remove sdl modifier
    if (o)
    {
        if (fAGMasterSDLMod)
            o->RemoveModifier(fAGMasterSDLMod);
    }
    delete fAGMasterSDLMod;
    fAGMasterSDLMod=nil;

    fTarget = nil;
}

#include "plProfile.h"

plProfile_CreateTimer("ApplyAnimation", "Animation", ApplyAnimation);
plProfile_CreateTimer("  AffineValue", "Animation", AffineValue);
plProfile_CreateTimer("    AffineInterp", "Animation", AffineInterp);
plProfile_CreateTimer("    AffineBlend", "Animation", AffineBlend);
plProfile_CreateTimer("  AffineCompose", "Animation", AffineCompose);
plProfile_CreateTimer("  AffineApplicator", "Animation", MatrixApplicator);
plProfile_CreateTimer("AnimatingPhysicals", "Animation", AnimatingPhysicals);
plProfile_CreateTimer("StoppedAnimPhysicals", "Animation", StoppedAnimPhysicals);

// IEVAL
hsBool plAGMasterMod::IEval(double secs, float del, uint32_t dirty)
{
    if (fFirstEval)
    {
        int i;
        for (i = 0; i < fAnimInstances.size(); i++)
            fAnimInstances[i]->SearchForGlobals();

        fFirstEval = false;
    }
    ApplyAnimations(secs, del);
    
    // We might get registered for just a single eval. If we don't need to eval anymore, unregister
    if (!fNeedEval) 
        plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());


    return true;
}

// APPLYANIMATIONS
void plAGMasterMod::ApplyAnimations(double time, float elapsed)
{
    plProfile_BeginLap(ApplyAnimation, this->GetKey()->GetUoid().GetObjectName().c_str());

    // update any fades
    for (int i = 0; i < fAnimInstances.size(); i++)
    {
        fAnimInstances[i]->ProcessFade(elapsed);
    }
    
    AdvanceAnimsToTime(time);

    plProfile_EndLap(ApplyAnimation,this->GetKey()->GetUoid().GetObjectName().c_str());
}

void plAGMasterMod::AdvanceAnimsToTime(double time)
{
    if(fNeedCompile)
        Compile(time);
    
    for(plChannelModMap::iterator j = fChannelMods.begin(); j != fChannelMods.end(); j++)
    {
        plAGModifier *mod = (*j).second;
        mod->Apply(time);
    }
}

void plAGMasterMod::SetNeedCompile(bool needCompile)
{
    fNeedCompile = true;
}

void plAGMasterMod::Compile(double time)
{
    plChannelModMap::iterator end = fChannelMods.end();
    fNeedCompile = false;

    for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
    {
        plAGModifier *mod = (*j).second;
        plAGApplicator *app = mod->GetApplicator(kAGPinTransform);

        if(app) {
            plAGChannel *channel = app->GetChannel();
            if(channel)
            {
                plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
                if(topChannel)
                    topChannel->Optimize(time);
            }
        }
    }
}

void plAGMasterMod::DumpAniGraph(const char *justThisChannel, bool optimized, double time)
{
    plChannelModMap::iterator end = fChannelMods.end();
    fNeedCompile = false;

    for(plChannelModMap::iterator j = fChannelMods.begin(); j != end; j++)
    {
        plAGModifier *mod = (*j).second;
        if(!justThisChannel || mod->GetChannelName().Compare(justThisChannel, plString::kCaseInsensitive) == 0)
        {
            plAGApplicator *app = mod->GetApplicator(kAGPinTransform);

            if(app) {
                plAGChannel *channel = app->GetChannel();
                if(channel)
                {
                    plMatrixChannel *topChannel = plMatrixChannel::ConvertNoRef(channel);
                    if(topChannel)
                    {
                        hsStatusMessageF("AGModifier: <%s>", mod->GetChannelName().c_str());
                        topChannel->Dump(1, optimized, time);
                    }
                }
            }
            if(justThisChannel)
                break;
        }
    }
}

// GETCHANNELMOD(name)
// Get the modifier that controls the channel with the given name
plAGModifier * plAGMasterMod::GetChannelMod(const plString & name, hsBool dontCache ) const
{
    plAGModifier * result = nil;
    std::map<plString, plAGModifier *>::const_iterator i = fChannelMods.find(name);

    if (i != fChannelMods.end()) {
        result = (*i).second;
    } else {
        plSceneObject *SO = GetTarget(0);
        if(SO) {
            result = IFindChannelMod(SO, name);
            if(result && !dontCache) {
                ICacheChannelMod(result);
            }
        }
    }
    return result;
}

// CACHECHANNELMOD
plAGModifier * plAGMasterMod::ICacheChannelMod(plAGModifier *mod) const
{
    plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, 0);
    hsgResMgr::ResMgr()->SendRef(mod, msg, plRefFlags::kActiveRef);
    
    return mod;
}

// IFINDAGMOD (sceneObject)
// See if there's an ag modifier on this sceneobject.
// Doesn't check for multiples; just returns the first one.
plAGModifier * plAGMasterMod::IFindChannelMod(const plSceneObject *SO, const plString &name) const
{
    const plCoordinateInterface * CI = SO->GetCoordinateInterface();

    const plAGModifier * constMod = static_cast<const plAGModifier *>(FindModifierByClass(SO, plAGModifier::Index()));
    plAGModifier * mod = const_cast<plAGModifier *>(constMod);

    if(mod)
    {
        plString modName = mod->GetChannelName();
        if(modName.Compare(name, plString::kCaseInsensitive) == 0)
            return mod;
    }

    if(CI)
    {
        int childCount = CI->GetNumChildren();
        for (int i = 0; i < childCount; i++)
        {
            const plSceneObject * subChild = CI->GetChild(i)->GetOwner();
            plAGModifier * mod = IFindChannelMod(subChild, name);

            if(mod)
                return mod;
        }
    }
    return nil;
}

// ATTACHANIMATIONBLENDED(anim, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(plAGAnim *anim,
                                                         float blendFactor /* = 0 */,
                                                         uint16_t blendPriority /* plAGMedBlendPriority */,
                                                         hsBool cache /* = false */)
{
    plAGAnimInstance *instance = nil;
    plAnimVector::iterator i;
    if(anim)
    {
        fNeedCompile = true;    // need to recompile the graph since we're editing it...
        for (i = fPrivateAnims.begin(); i != fPrivateAnims.end(); i++) 
        {
            if (*i == anim)
                break;
        }
        if (i == fPrivateAnims.end()) // Didn't find it. Ref it!
        {
            plGenRefMsg* msg = new plGenRefMsg(GetKey(), plRefMsg::kOnCreate, 0, kPublicAnim);
            hsgResMgr::ResMgr()->SendRef(anim, msg, plRefFlags::kActiveRef);
        }
        instance = new plAGAnimInstance(anim, this, blendFactor, blendPriority, cache, false);
        fAnimInstances.push_back(instance);

        plATCAnim *atcAnim = plATCAnim::ConvertNoRef(anim);
        if (atcAnim)
        {
            fATCAnimInstances.push_back(instance);
            ISetupMarkerCallbacks(atcAnim, instance->GetTimeConvert());
        }
        IRegForEval(HasRunningAnims());
    }
    return instance;
}

// ATTACHANIMATIONBLENDED(name, blend)
plAGAnimInstance * plAGMasterMod::AttachAnimationBlended(const plString &name, float blendFactor /* = 0 */, uint16_t blendPriority, hsBool cache /* = false */)
{
    plAGAnimInstance *instance = nil;
    plAGAnim *anim = plAGAnim::FindAnim(name);

    if(anim)
    {
        instance = AttachAnimationBlended(anim, blendFactor, blendPriority, cache);
    }
    return instance;
}

void plAGMasterMod::PlaySimpleAnim(const plString &name)
{
    plATCAnim *anim = plATCAnim::ConvertNoRef(plAGAnim::FindAnim(name));
    plAGAnimInstance *instance = nil;
    if (anim)
    {
        if (FindAnimInstance(name))
            return;

        instance = AttachAnimationBlended(anim, 1.f, (uint16_t)kAGMaxBlendPriority, false);
    }

    if (instance)
    {
        instance->SetLoop(false);
        instance->Start();

        plAGDetachCallbackMsg *msg = new plAGDetachCallbackMsg(GetKey(), kStop); 
        msg->SetAnimName(name);
        instance->GetTimeConvert()->AddCallback(msg);
        hsRefCnt_SafeUnRef(msg);
    }
}

// FINDANIMINSTANCE
// Look for an animation instance of the given name on the modifier.
// If we need this to be fast, should make it a map rather than a vector
plAGAnimInstance * plAGMasterMod::FindAnimInstance(const plString &name)
{
    plAGAnimInstance *result = nil;

    if (!name.IsNull())
    {
        for (int i = 0; i < fAnimInstances.size(); i++)
        {
            plAGAnimInstance *act = fAnimInstances[i];
            plString eachName = act->GetName();

            if( eachName.Compare(name, plString::kCaseInsensitive) == 0)
            {
                result = act;
                break;
            }
        }
    }
    return result;
}

// FINDORATTACHINSTANCE
plAGAnimInstance * plAGMasterMod::FindOrAttachInstance(const plString &name, float blendFactor)
{
    plAGAnimInstance *result = FindAnimInstance(name);
    if(result)
    {
        // if it's already attached, we need to set the blend
        result->SetBlend(blendFactor);
    } else  {
        result = AttachAnimationBlended(name, blendFactor);
    }
    return result;
}


// GETANIMINSTANCE
plAGAnimInstance * plAGMasterMod::GetAnimInstance(int i)
{
    return fAnimInstances[i];
}

// GETNUMANIMATIONS
int plAGMasterMod::GetNumAnimations()
{
    return fAnimInstances.size();
}

// GETNUMPRIVATEANIMATIONS
int plAGMasterMod::GetNumPrivateAnimations()
{
    return fPrivateAnims.size();
}

int plAGMasterMod::GetNumATCAnimations()
{
    return fATCAnimInstances.size();
}

plAGAnimInstance *plAGMasterMod::GetATCAnimInstance(int i)
{
    return fATCAnimInstances[i];
}

void plAGMasterMod::DetachAllAnimations()
{
    int nInstances = fAnimInstances.size();

    for (int i = nInstances - 1; i >= 0; i--)
    {
        plAGAnimInstance * instance = fAnimInstances[i];
        if(instance)
        {
            DetachAnimation(instance);
            // delete instance;
        }
    }
    fAnimInstances.clear();
    fPrivateAnims.clear();
    fATCAnimInstances.clear();
}

// DETACHANIMATION(plAGAnimInstance *)
void plAGMasterMod::DetachAnimation(plAGAnimInstance *anim)
{
    plInstanceVector::iterator i;
    plAnimVector::iterator j;
    
    fNeedCompile = true;    // need to recompile the graph since we're editing it...

    for ( i = fAnimInstances.begin(); i != fAnimInstances.end(); i++)
    {
        plAGAnimInstance *instance = *i;

        if(instance == anim)
        {
            // DetachAnimation(instance);
            instance->DetachChannels();

            // Need to release it if it's not a private anim
            const plAGAnim *agAnim = instance->GetAnimation();
            for (j = fPrivateAnims.begin(); j != fPrivateAnims.end(); j++) 
            {
                if (*j == agAnim)
                    break;
            }
            if (j == fPrivateAnims.end()) // We didn't find it
                GetKey()->Release(agAnim->GetKey());

            delete instance;
            i = fAnimInstances.erase(i);
            break;
        }
    }
    for ( i = fATCAnimInstances.begin(); i != fATCAnimInstances.end(); i++)
    {
        plAGAnimInstance *instance = *i;

        if(instance == anim)
        {
            i = fATCAnimInstances.erase(i);
            break;
        }
    }
}

// DETACHANIMATION(name)
void plAGMasterMod::DetachAnimation(const plString &name)
{
    plAGAnimInstance *anim = FindAnimInstance(name);
    if(anim) {
        DetachAnimation(anim);
    }
}

void plAGMasterMod::DumpCurrentAnims(const char *header)
{
    if(header)
        hsStatusMessageF("Dumping Armature Anim Stack: %s", header);
    int nAnims = fAnimInstances.size();
    for(int i = nAnims - 1; i >= 0; i--)
    {
        plAGAnimInstance *inst = fAnimInstances[i];
        plString name = inst->GetName();
        float blend = inst->GetBlend();

        hsStatusMessageF("%d: %s with blend of %f\n", i, name.c_str(), blend);
    }
}

// MSGRECEIVE
// receive trigger messages
hsBool plAGMasterMod::MsgReceive(plMessage* msg)
{
    plAnimCmdMsg* cmdMsg;
    plAGCmdMsg* agMsg;
    int i;

    plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg);
    if (nMsg)
    {
        // Force a single eval
        plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
        return true;
    }

    if (cmdMsg = plAnimCmdMsg::ConvertNoRef(msg))
    {
        plString targetName = cmdMsg->GetAnimName();

        if (targetName.IsNull())
            targetName = _TEMP_CONVERT_FROM_LITERAL(ENTIRE_ANIMATION_NAME);

        plAGAnimInstance *inst = FindAnimInstance(targetName);
        if (inst != nil)
        {
            if (cmdMsg->CmdChangesAnimTime())
            {
                for (i = 0; i < GetNumAnimations(); i++)
                {
                    plAGAnimInstance *currInst = GetAnimInstance(i);
                    if (currInst != inst && currInst->GetAnimation()->SharesPinsWith(inst->GetAnimation()))
                        currInst->SetBlend(0);
                }
                inst->SetBlend(1);
            }

            inst->HandleCmd(cmdMsg);
        }

        return true;
    }

    if (agMsg = plAGCmdMsg::ConvertNoRef(msg))
    {
        if (agMsg->Cmd(plAGCmdMsg::kSetAnimTime))
        {
            for (int i = 0; i < fAnimInstances.size(); i++)
            {
                plAGAnimInstance *inst = fAnimInstances[i];
                inst->SetCurrentTime(agMsg->fAnimTime, true);
            }

            return true;
        }

        plAGAnimInstance *inst = FindAnimInstance(agMsg->GetAnimName());
        if (inst != nil)
        {
            if (agMsg->Cmd(plAGCmdMsg::kSetBlend))
                inst->Fade(agMsg->fBlend, agMsg->fBlendRate, plAGAnimInstance::kFadeBlend);
            if (agMsg->Cmd(plAGCmdMsg::kSetAmp))
                inst->Fade(agMsg->fAmp, agMsg->fAmpRate, plAGAnimInstance::kFadeAmp);
        }
        return true;
    }

    plAGInstanceCallbackMsg *agicMsg = plAGInstanceCallbackMsg::ConvertNoRef(msg);
    if (agicMsg != nil)
    {
        if (agicMsg->fEvent == kStart)
        {
            IRegForEval(true);
        }
        else if (agicMsg->fEvent == kStop)
        {
            if (!HasRunningAnims())
                IRegForEval(false);
        }
        else // Just force a single eval
        {
            plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
        }
        return true;
    }

    plAGDetachCallbackMsg *detachMsg = plAGDetachCallbackMsg::ConvertNoRef(msg);
    if (detachMsg != nil)
    {
        DetachAnimation(detachMsg->GetAnimName());
    }

    plGenRefMsg *genRefMsg;
    if (genRefMsg = plGenRefMsg::ConvertNoRef(msg))
    {
        plAGAnim *anim;
        if (anim = plAGAnim::ConvertNoRef(genRefMsg->GetRef()))
        {
            if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
            {
                if (genRefMsg->fType == kPrivateAnim)
                    fPrivateAnims.push_back(anim);
            }
            else
            {
                if (genRefMsg->fType == kPrivateAnim)
                {
                    plAnimVector::iterator i = fPrivateAnims.begin();
                    for ( ; i != fPrivateAnims.end(); i++)
                    {
                        plAGAnim *currAnim = *i;

                        if(currAnim == anim)
                        {
                            i = fPrivateAnims.erase(i);
                            break;
                        }
                    }
                }
            }

            return true;
        }
        plAGModifier *agmod;
        if (agmod = plAGModifier::ConvertNoRef(genRefMsg->GetRef()))
        {
            if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
                fChannelMods[agmod->GetChannelName()] = agmod;
            else
                fChannelMods.erase(agmod->GetChannelName());

            return true;
        }
        plMsgForwarder *msgfwd;
        if (msgfwd = plMsgForwarder::ConvertNoRef(genRefMsg->GetRef()))
        {
            if (genRefMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest))
                fMsgForwarder = msgfwd;
            else
                fMsgForwarder = nil;

            return true;
        }
    }
    plRefMsg* refMsg = plRefMsg::ConvertNoRef(msg);
    if( refMsg )
    {
        if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
            AddTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));
        else
            RemoveTarget(plSceneObject::ConvertNoRef(refMsg->GetRef()));

        return true;
    }

    return plModifier::MsgReceive(msg);
}

void plAGMasterMod::IRegForEval(hsBool val)
{
    if (fNeedEval == val)
        return;

    fNeedEval = val;
    if (val)
        plgDispatch::Dispatch()->RegisterForExactType(plEvalMsg::Index(), GetKey());
    else
        plgDispatch::Dispatch()->UnRegisterForExactType(plEvalMsg::Index(), GetKey());
}

hsBool plAGMasterMod::HasRunningAnims()
{
    int i;
    hsBool needEval = false;
    for (i = 0; i < fAnimInstances.size(); i++)
    {
        if (!fAnimInstances[i]->IsFinished())
        {
            needEval = true;
            break;
        }
    }
    return needEval;
}

//
// Send SDL sendState msg to object's plAGMasterSDLModifier
//
hsBool plAGMasterMod::DirtySynchState(const char* SDLStateName, uint32_t synchFlags)
{
    if(GetNumTargets() > 0 && (!fIsGrouped || fIsGroupMaster))
    {
        plSceneObject *sObj = GetTarget(0);
        if(sObj) 
            return sObj->DirtySynchState(SDLStateName, synchFlags);
    }
    return false;
}

void plAGMasterMod::SetIsGroupMaster(bool master, plMsgForwarder* msgForwarder)
{
    fIsGroupMaster = master;
    fMsgForwarder = msgForwarder;
}

void plAGMasterMod::SetIsGrouped(bool grouped)
{
    fIsGrouped = true;
}
