/*==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/>.

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==*/

#include "hsTypes.h"
#include "plSceneObject.h"
#include "plDrawInterface.h"
#include "plSimulationInterface.h"
#include "plCoordinateInterface.h"
#include "plAudioInterface.h"
#include "pnDispatch/plDispatch.h"
#include "pnModifier/plModifier.h"
#include "pnMessage/plMessage.h"
#include "pnMessage/plRefMsg.h"
#include "plDrawable.h"
#include "plPhysical.h"
#include "plAudible.h"
#include "pnMessage/plTimeMsg.h"
#include "pnMessage/plCorrectionMsg.h"
#include "pnMessage/plWarpMsg.h"
#include "pnMessage/plSoundMsg.h"
#include "pnMessage/plEnableMsg.h"
#include "pnMessage/plAttachMsg.h"
#include "pnMessage/plObjRefMsg.h"
#include "pnMessage/plNodeRefMsg.h"
#include "pnMessage/plIntRefMsg.h"
#include "pnMessage/plSimulationSynchMsg.h"
#include "pnMessage/plSimulationMsg.h"
#include "pnMessage/plNodeChangeMsg.h"
#include "pnMessage/plSelfDestructMsg.h"
#include "pnKeyedObject/plKey.h"

#include "hsStream.h"
#include "hsResMgr.h"
#include "plCreatableIndex.h" // For plLightInfo::Index(), so we don't have to include plLightInfo.h



int dbgCurrentTest = 0;

plSceneObject::plSceneObject()
:   fDrawInterface(nil),
    fSimulationInterface(nil),
    fCoordinateInterface(nil),
    fAudioInterface(nil),
    fSceneNode(nil)
{
}

plSceneObject::~plSceneObject()
{
    SetDrawInterface(nil);
    SetSimulationInterface(nil);
    SetCoordinateInterface(nil);
    SetAudioInterface(nil);

    IRemoveAllGenerics();

    int i;
    int knt;

    knt = fModifiers.GetCount();
    for( i = 0; i < knt; i++ )
    {
        if( fModifiers[i] )
            fModifiers[i]->RemoveTarget(this);
    }
}

void plSceneObject::Read(hsStream* stream, hsResMgr* mgr)
{
    plSynchedObject::Read(stream, mgr);

    // Interfaces will attach themselves to us on read.
    // DI
    mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);

    // SI
    mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);

    // CI
    mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);

    // AI
    mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);

    int i;

    int nGen = stream->ReadSwap32();
    fGenerics.SetCount(0);
    for( i = 0; i < nGen; i++ )
    {
        mgr->ReadKeyNotifyMe(stream, TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, 0, plObjRefMsg::kInterface), plRefFlags::kActiveRef);
    }

    plObjRefMsg* refMsg;

    int nOldMods=fModifiers.GetCount();     // existng modifiers created during interface loading
    int nNewMods = stream->ReadSwap32();
    fModifiers.ExpandAndZero(nOldMods+nNewMods);    // reserve space for new modifiers+existing modifiers
    for( i = nOldMods; i < nOldMods+nNewMods; i++ )
    {
        refMsg = TRACKED_NEW plObjRefMsg(GetKey(), plRefMsg::kOnCreate, i, plObjRefMsg::kModifier);
        mgr->ReadKeyNotifyMe(stream,refMsg, plRefFlags::kActiveRef);
    }

    plKey nodeKey = mgr->ReadKey(stream);
//  SetSceneNode(nodeKey);
    fSceneNode = nodeKey;

}

void plSceneObject::Write(hsStream* stream, hsResMgr* mgr)
{
    plSynchedObject::Write(stream, mgr);

    mgr->WriteKey(stream, fDrawInterface);
    mgr->WriteKey(stream, fSimulationInterface);
    mgr->WriteKey(stream, fCoordinateInterface);
    mgr->WriteKey(stream, fAudioInterface);

    int i;

    stream->WriteSwap32(fGenerics.GetCount());
    for( i = 0; i < fGenerics.GetCount(); i++ )
        mgr->WriteKey(stream, fGenerics[i]);

    for( i = fModifiers.GetCount() - 1; i >= 0; i--)
        if (fModifiers[i]->GetKey() == nil)
            RemoveModifier(fModifiers[i]);

    stream->WriteSwap32(fModifiers.GetCount());
    for( i = 0; i < fModifiers.GetCount(); i++ )
        mgr->WriteKey(stream,fModifiers[i]);

    mgr->WriteKey(stream, fSceneNode);
}

//// ReleaseData //////////////////////////////////////////////////////////////
//  Called by SceneViewer to release the data for this sceneObject (really
//  just a switchboard).

void    plSceneObject::ReleaseData( void )
{
    if( fDrawInterface )
        fDrawInterface->ReleaseData();
    if( fSimulationInterface )
        fSimulationInterface->ReleaseData();
    if( fCoordinateInterface )
        fCoordinateInterface->ReleaseData();
    if( fAudioInterface )
        fAudioInterface->ReleaseData();

    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] )
            fGenerics[i]->ReleaseData();
    }
}

void plSceneObject::FlushTransform()
{
    if( fCoordinateInterface )
        fCoordinateInterface->FlushTransform();
}

#include "plProfile.h"

plProfile_CreateTimer("SOTrans", "Object", SOTrans);
plProfile_CreateTimer("   SODITrans", "Object", SODITrans);
plProfile_CreateTimer("   SOSITrans", "Object", SOSITrans);
plProfile_CreateTimer("   SOAITrans", "Object", SOAITrans);
plProfile_CreateTimer("   SOGITrans", "Object", SOGITrans);
plProfile_CreateTimer("   SOMOTrans", "Object", SOMOTrans);


void plSceneObject::ISetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
    plProfile_BeginTiming(SOTrans);
    plProfile_BeginTiming(SODITrans);
    if( fDrawInterface )
        fDrawInterface->SetTransform(l2w, w2l);
    plProfile_EndTiming(SODITrans);

    plProfile_BeginTiming(SOSITrans);
    if( fSimulationInterface )
    {
        if(fCoordinateInterface)
        {
            UInt16 whyTransformed = fCoordinateInterface->GetReasons();
            if(whyTransformed != plCoordinateInterface::kReasonPhysics)
            {
                // if we were transformed by anything but physics, let physics know
                // otherwise we're not even going to tell physics
                fSimulationInterface->SetTransform(l2w, w2l);
            } else {
                int moreSunshine = 10;
            }
            fCoordinateInterface->ClearReasons();
        } else {
            int somethingToBreakOn = 10;
            // if there's not coordinate interface, there's no reason to move the simulation interface
        }
    }
    plProfile_EndTiming(SOSITrans);

    plProfile_BeginTiming(SOAITrans);
    if( fAudioInterface )
        fAudioInterface->SetTransform(l2w, w2l);
    plProfile_EndTiming(SOAITrans);

    plProfile_BeginTiming(SOGITrans);
    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] )
            fGenerics[i]->SetTransform(l2w, w2l);
    }
    plProfile_EndTiming(SOGITrans);


    plProfile_BeginTiming(SOMOTrans);
    for( i = 0; i < fModifiers.GetCount(); i++ )
    {
        if( fModifiers[i] )
            fModifiers[i]->SetTransform(l2w, w2l);
    }
    plProfile_EndTiming(SOMOTrans);
    plProfile_EndTiming(SOTrans);
}

void plSceneObject::SetTransform(const hsMatrix44& l2w, const hsMatrix44& w2l)
{
    if( fCoordinateInterface )
        fCoordinateInterface->SetTransform(l2w, w2l);
}

hsMatrix44 plSceneObject::GetLocalToWorld() const
{
    hsMatrix44 l2w;
    if( fCoordinateInterface )
        l2w = fCoordinateInterface->GetLocalToWorld();
    else
        l2w.Reset();
    return l2w;
}

hsMatrix44 plSceneObject::GetWorldToLocal() const
{
    hsMatrix44 w2l;
    if( fCoordinateInterface )
        w2l = fCoordinateInterface->GetWorldToLocal();
    else
        w2l.Reset();
    return w2l;
}

hsMatrix44 plSceneObject::GetLocalToParent() const
{
    hsMatrix44 l2p;
    if( fCoordinateInterface )
        l2p = fCoordinateInterface->GetLocalToParent();
    else
        l2p.Reset();
    return l2p;
}

hsMatrix44 plSceneObject::GetParentToLocal() const
{
    hsMatrix44 p2l;
    if( fCoordinateInterface )
        p2l = fCoordinateInterface->GetParentToLocal();
    else
        p2l.Reset();
    return p2l;
}

void plSceneObject::IAddModifier(plModifier* mo, int i)
{
    if( !mo )
        return;

    if( i < 0 )
        i = fModifiers.GetCount();
    fModifiers.ExpandAndZero(i+1);
    fModifiers.Set(i, mo);
    mo->AddTarget(this);
}

void plSceneObject::IRemoveModifier(plModifier* mo)
{
    if( !mo )
        return;

    int idx = fModifiers.Find(mo);
    if( idx != fModifiers.kMissingIndex )
    {
        mo->RemoveTarget(this);
        fModifiers.Remove(idx);
    }
}

void plSceneObject::ISetInterface(plObjInterface* iface)
{
    hsAssert(iface, "Setting nil interface");
    if( plDrawInterface::ConvertNoRef(iface) )
        ISetDrawInterface(plDrawInterface::ConvertNoRef(iface));
    else if( plSimulationInterface::ConvertNoRef(iface) )
        ISetSimulationInterface(plSimulationInterface::ConvertNoRef(iface));
    else if( plCoordinateInterface::ConvertNoRef(iface) )
        ISetCoordinateInterface(plCoordinateInterface::ConvertNoRef(iface));
    else if( plAudioInterface::ConvertNoRef(iface) )
        ISetAudioInterface(plAudioInterface::ConvertNoRef(iface));
    else
        IAddGeneric(iface);

    if( iface )
        iface->ISetSceneNode(GetSceneNode());
}

void plSceneObject::IRemoveInterface(Int16 idx, plObjInterface* who)
{
    if( plFactory::DerivesFrom(plDrawInterface::Index(), idx) )
        ISetDrawInterface(nil);
    else if( plFactory::DerivesFrom(plSimulationInterface::Index(), idx) )
        ISetSimulationInterface(nil);
    else if( plFactory::DerivesFrom(plCoordinateInterface::Index(), idx) )
        ISetCoordinateInterface(nil);
    else if( plFactory::DerivesFrom(plAudioInterface::Index(), idx) )
        ISetAudioInterface(nil);
    else
        IRemoveGeneric(who);
}

hsBool plSceneObject::IPropagateToModifiers(plMessage* msg)
{
    hsBool retVal = false;
    int i;
    int nMods = fModifiers.GetCount();

    for( i = 0; i < nMods; i++ )
    {
        if( fModifiers[i] )
        {
            plModifier *mod = fModifiers[i];
            hsBool modRet = mod->MsgReceive(msg);
            retVal |= modRet;
        }
    }
    return retVal;
}

hsBool plSceneObject::Eval(double secs, hsScalar delSecs)
{
    UInt32 dirty = ~0L;
    hsBool retVal = false;
    int i;
    for( i = 0; i < fModifiers.GetCount(); i++ )
    {
        if( fModifiers[i] )
            retVal |= fModifiers[i]->IEval(secs, delSecs, dirty);
    }
    return retVal;
}

void plSceneObject::SetSceneNode(plKey newNode)
{
    plKey curNode=GetSceneNode();
    if( curNode == newNode )
        return;
    if( fDrawInterface )
        fDrawInterface->ISetSceneNode(newNode);
    if( fSimulationInterface )
        fSimulationInterface->ISetSceneNode(newNode);
    if( fAudioInterface )
        fAudioInterface->ISetSceneNode(newNode);
    if( fCoordinateInterface )
        fCoordinateInterface->ISetSceneNode(newNode);

    int i;
    for( i = 0; i < GetNumGenerics(); i++ )
    {
        if( fGenerics[i] )
            fGenerics[i]->ISetSceneNode(newNode);
    }

    if( newNode )
    {
        plNodeRefMsg* refMsg = TRACKED_NEW plNodeRefMsg(newNode, plNodeRefMsg::kOnRequest, -1, plNodeRefMsg::kObject);
        plKey key = GetKey();   // for linux build
        hsgResMgr::ResMgr()->AddViaNotify(key, refMsg, plRefFlags::kActiveRef);
    }
    if( curNode)
    {
        curNode->Release(GetKey());
    }
    fSceneNode = newNode;
}

plKey plSceneObject::GetSceneNode() const
{
    return fSceneNode;
}

void plSceneObject::SetNetGroup(plNetGroupId netGroup)
{
    if( !fCoordinateInterface )
        plSynchedObject::SetNetGroup(netGroup);
    else
        fCoordinateInterface->ISetNetGroupRecur(netGroup);
}

const plModifier* plSceneObject::GetModifierByType(UInt16 classIdx) const
{
    int i;
    for (i = 0; i < fModifiers.GetCount(); i++)
    {
        plModifier * mod = fModifiers[i];
        if (mod && plFactory::DerivesFrom(classIdx, mod->ClassIndex()))
            return mod;
    }

    return nil;
}

hsBool plSceneObject::MsgReceive(plMessage* msg)
{

#if 0   // objects are only in the nil room when they are being paged out
    // TEMP - until we have another way to neutralize objects
    // for an object in the 'nil' room, ignore most msgs    
    if (GetSceneNode()==nil && !plNodeChangeMsg::ConvertNoRef(msg) &&
        !plRefMsg::ConvertNoRef(msg)&&
        !plSelfDestructMsg::ConvertNoRef(msg))
        return false;
#endif

    hsBool retVal = false;
    // If it's a bcast, let our own dispatcher find who's interested.
    plTransformMsg* trans;
    plEvalMsg* eval = plEvalMsg::ConvertNoRef(msg);
    plAttachMsg* att = nil;
    if( eval )
    {
        // Switched things over so that modifiers register for the eval message themselves,
        // and can be smart about not evaluating if they know it's unneccessary.

        //Eval(eval->DSeconds(), eval->DelSeconds());
        return true;
    }
    else
    if( trans = plTransformMsg::ConvertNoRef(msg) ) // also catches the derived plDelayedTransformMsg
    {
        if( fCoordinateInterface )
        {
            // flush any dirty transforms
            fCoordinateInterface->ITransformChanged(false, 0, trans->ClassIndex() == plTransformMsg::Index());
        }
        return true;
    }
    else
    if( att = plAttachMsg::ConvertNoRef(msg) )
    {
        if( fCoordinateInterface )
        {
            plSceneObject *child = plSceneObject::ConvertNoRef(att->GetRef());
            
            if( child )
            {
                if( !fCoordinateInterface )
                {
                    // If we have no coordinate interface, we could make ourselves one here,
                    // but for now it's an error.
                    hsAssert(false, "Trying to attach a child when we have no coordinateInterface");
                    return true;
                }
                if( !child->GetVolatileCoordinateInterface() )
                {
                    // If the child has no coordinate interface, we could add one to it here,
                    // but for now it's an error.
                    hsAssert(false, "Trying to attach a child who has no coordinateInterface");
                    return true;
                }
                plIntRefMsg* intRefMsg = TRACKED_NEW plIntRefMsg(fCoordinateInterface->GetKey(), att->GetContext(), -1, plIntRefMsg::kChildObject);
                intRefMsg->SetRef(child);
                hsgResMgr::ResMgr()->AddViaNotify(intRefMsg, plRefFlags::kPassiveRef);
            }
        }
    }
    else // Am I the final destination?
    {
        retVal = IMsgHandle(msg);
    }

    if( msg->HasBCastFlag(plMessage::kPropagateToModifiers) )
    {
        retVal |= IPropagateToModifiers(msg);
    }
        
    if (msg->HasBCastFlag(plMessage::kPropagateToChildren))
    {
        const plCoordinateInterface* ci = GetCoordinateInterface();
        for (int i = 0; i < ci->GetNumChildren(); i++)
        {
            plSceneObject* child = (plSceneObject*)ci->GetChild(i)->GetOwner();
            if (child)
            {
                hsBool modRet = child->MsgReceive(msg);
                retVal |= modRet;
            }
        }
    }
    
    // Might want an option to propagate messages to children here.

    return plSynchedObject::MsgReceive(msg);
}

hsBool plSceneObject::IMsgHandle(plMessage* msg)
{
    // To start with, plSceneObject only handles messages to add or remove
    // references. Current references are other plSceneObjects and plModifiers
    plObjRefMsg* refMsg = plObjRefMsg::ConvertNoRef(msg);
    if( refMsg )
    {
        switch( refMsg->fType )
        {
        case plObjRefMsg::kModifier:
            {
                if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
                {
                    plModifier* mod = plModifier::ConvertNoRef(refMsg->GetRef());
                    IAddModifier(mod, refMsg->fWhich);
                }
                else if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
                {
                    plModifier* mod = (plModifier*)refMsg->GetRef();
                    IRemoveModifier(mod);
                }
            }
            return true;
        case plObjRefMsg::kInterface:
            {
                if( refMsg->GetContext() & (plRefMsg::kOnCreate|plRefMsg::kOnRequest|plRefMsg::kOnReplace) )
                {
                    plObjInterface* oi = plObjInterface::ConvertNoRef(refMsg->GetRef());
                    ISetInterface(oi);
                }
                else
                if( refMsg->GetContext() & (plRefMsg::kOnDestroy|plRefMsg::kOnRemove) )
                {
                    plObjInterface* oi = (plObjInterface*)refMsg->GetRef();
                    // TODO - This will crash if oi's already been deleted
                    IRemoveInterface(oi->ClassIndex(), oi);
                }
            }
            return true;
        }


        return false;
    }
    plNodeChangeMsg* nodeChange = plNodeChangeMsg::ConvertNoRef(msg);
    if( nodeChange )
    {
        SetSceneNode(nodeChange->GetNodeKey());
        return true;
    }
    
    if( plIntRefMsg::ConvertNoRef(msg) )
    {
        if (fCoordinateInterface)
            fCoordinateInterface->MsgReceive(msg);
        if (fAudioInterface)
            fAudioInterface->MsgReceive(msg);
        return true;
    }

    if (plCorrectionMsg::ConvertNoRef(msg))
    {
        hsAssert(fCoordinateInterface, "Unimplemented, also need to register this one we just made with the resource manager");
        
        if (fCoordinateInterface)
            fCoordinateInterface->MsgReceive(msg);

        return true;
    }
    
    // check for generic enable/disable message (passed to interfaces)

    plEnableMsg* pEnableMsg = plEnableMsg::ConvertNoRef( msg );
    if (pEnableMsg)
    {
        if( pEnableMsg->Cmd(plEnableMsg::kDrawable) )
        {
            plDrawInterface* di = GetVolatileDrawInterface();
            if( di )
                di->MsgReceive(msg);
            plObjInterface* li = GetVolatileGenericInterface(CLASS_INDEX_SCOPED(plLightInfo));
            if( li )
                li->MsgReceive(msg);
        }
        if ( pEnableMsg->Cmd(plEnableMsg::kAll) && GetDrawInterface() )
            GetVolatileDrawInterface()->MsgReceive(msg);
        
        if ( pEnableMsg->Cmd( plEnableMsg::kPhysical ) || pEnableMsg->Cmd( plEnableMsg::kAll) )
        {
            if ( GetSimulationInterface() )
            {
                GetVolatileSimulationInterface()->MsgReceive(msg);
            }
            else
            {
                // if someone is trying to disable the physics on a sceneobject that doesn't have a physics interface...
                // they might be trying to disable the avatar when the PhysX controller is being used
                // ...so, look to see if this is an avatar object, and tell the avatar to disable the physics
                IPropagateToModifiers(msg);
            }
        }
        
        if ( (pEnableMsg->Cmd( plEnableMsg::kAudible ) || pEnableMsg->Cmd( plEnableMsg::kAll )) && GetAudioInterface() )
            GetVolatileAudioInterface()->MsgReceive(msg);
        
        if( pEnableMsg->Cmd( plEnableMsg::kAll ) )
        {   
            IPropagateToGenerics(pEnableMsg);
        }
        else if( pEnableMsg->Cmd( plEnableMsg::kByType ) )
        {   
            IPropagateToGenerics(pEnableMsg->Types(), pEnableMsg);
        }
        return true;
    }
    
    // warp message
    if( plWarpMsg::ConvertNoRef(msg) )
    {
        // if there's a simulation interface, it needs to know about the warp
        // *** it would probably be better if it got this from the coordinate interface, as
        // *** only the coordinate interface knows to propagate it to children.
    //  if(fSimulationInterface)
    //  {
    //      fSimulationInterface->MsgReceive(msg);
    //  } 

        // the coordinate interface always gets the warp 
        if (fCoordinateInterface) 
        {
            fCoordinateInterface->MsgReceive(msg);
        }
        return true;
    }

    if ( plSimulationMsg::ConvertNoRef(msg) )
    {
        if(fSimulationInterface)
        {
            fSimulationInterface->MsgReceive(msg);
        }
        return true;
    }

    // audio message
    if (plSoundMsg::ConvertNoRef(msg) )
    {
        if( fAudioInterface )
            return(GetVolatileAudioInterface()->MsgReceive(msg));
        return true;
    }

    return false;
}

void plSceneObject::IPropagateToGenerics(const hsBitVector& types, plMessage* msg)
{
    hsBitIterator iter(types);
    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] )
        {
            for( iter.Begin(); !iter.End(); iter.Advance() )
            {
                if( plFactory::DerivesFrom(iter.Current(), fGenerics[i]->ClassIndex()) )
                {
                    fGenerics[i]->MsgReceive(msg);
                    break;
                }
            }
        }
    }
}

void plSceneObject::IPropagateToGenerics(plMessage* msg)
{
    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] )
            fGenerics[i]->MsgReceive(msg);
    }
}

plObjInterface* plSceneObject::GetVolatileGenericInterface(UInt16 classIdx) const
{
    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] && plFactory::DerivesFrom(classIdx, fGenerics[i]->ClassIndex()) )
            return fGenerics[i];
    }
    return nil;
}

void plSceneObject::IAddGeneric(plObjInterface* gen)
{
    if( gen )
    {
        if( fGenerics.kMissingIndex == fGenerics.Find(gen) )
        {
            fGenerics.Append(gen);
            gen->ISetOwner(this);
        }
    }
}

void plSceneObject::IRemoveGeneric(plObjInterface* gen)
{
    if( gen )
    {
        int idx = fGenerics.Find(gen);
        if( fGenerics.kMissingIndex != idx )
        {
            gen->ISetOwner(nil);
            fGenerics.Remove(idx);
        }
    }
}

void plSceneObject::IRemoveAllGenerics()
{
    int i;
    for( i = 0; i < fGenerics.GetCount(); i++ )
    {
        if( fGenerics[i] )
            fGenerics[i]->ISetOwner(nil);
    }
    fGenerics.Reset();
}

void plSceneObject::ISetDrawInterface(plDrawInterface* di) 
{ 
    if( fDrawInterface != di )
    {
        if( fDrawInterface )
            fDrawInterface->ISetOwner(nil);

        fDrawInterface = di;
        if( di )
            di->ISetOwner(this);
    }
}

void plSceneObject::ISetSimulationInterface(plSimulationInterface* si) 
{ 
    if( fSimulationInterface != si )
    {
        if( fSimulationInterface )
            fSimulationInterface->ISetOwner(nil);

        fSimulationInterface = si;
        if( si )
            si->ISetOwner(this);
    }
}

void plSceneObject::ISetAudioInterface(plAudioInterface* ai) 
{ 
    if( fAudioInterface != ai )
    {
        if( fAudioInterface )
            fAudioInterface->ISetOwner(nil);
        fAudioInterface = ai;
        if( ai )
            ai->ISetOwner(this);
    }
}

void plSceneObject::ISetCoordinateInterface(plCoordinateInterface* ci) 
{ 
    if( fCoordinateInterface != ci )
    {
        if( fCoordinateInterface )
            fCoordinateInterface->ISetOwner(nil);

        fCoordinateInterface = ci;
        if( ci )
            ci->ISetOwner(this);
    }
}

//
// "is ready to process Loads"?  Check base class and modifiers.
//
hsBool  plSceneObject::IsFinal()
{
    if (!plSynchedObject::IsFinal())
        return false;

    int i;
    for(i=0;i<GetNumModifiers(); i++)
        if (fModifiers[i] && !fModifiers[i]->IsFinal())
            return false;

    return true;
}

// Export only. Interfaces perm on object.
void plSceneObject::SetDrawInterface(plDrawInterface* di) 
{ 
    ISetDrawInterface(di);
}

void plSceneObject::SetSimulationInterface(plSimulationInterface* si) 
{ 
    ISetSimulationInterface(si);
}

void plSceneObject::SetAudioInterface(plAudioInterface* ai) 
{ 
    ISetAudioInterface(ai);
}

void plSceneObject::SetCoordinateInterface(plCoordinateInterface* ci) 
{ 
    ISetCoordinateInterface(ci);
}

void plSceneObject::AddModifier(plModifier* mo)
{
    IAddModifier(mo, fModifiers.GetCount());
}

void plSceneObject::RemoveModifier(plModifier* mo)
{
    IRemoveModifier(mo);
}