/*==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 "hsTimer.h"
#include "plLayerAnimation.h"
#include "pnKeyedObject/plKey.h"

#include "plInterp/plController.h"
#include "plMessage/plAnimCmdMsg.h"
#include "plMessage/plLinkToAgeMsg.h"
#include "pnMessage/plSDLModifierMsg.h"
#include "plModifier/plLayerSDLModifier.h"
#include "pnMessage/plCameraMsg.h"
#include "plNetClient/plLinkEffectsMgr.h"
#include "plgDispatch.h"
#include "hsResMgr.h"
#include "plModifier/plSDLModifier.h"
#include "plSDL/plSDL.h"
#include "pnMessage/plSDLNotificationMsg.h"
#include "plMessage/plAvatarMsg.h"

plLayerAnimationBase::plLayerAnimationBase()
: 
    fPreshadeColorCtl(nil), 
    fRuntimeColorCtl(nil),
    fAmbientColorCtl(nil), 
    fSpecularColorCtl(nil),
    fOpacityCtl(nil), 
    fTransformCtl(nil),
    fEvalTime(-1.0),
    fCurrentTime(-1.f),
    fSegmentID(nil)
{
}

plLayerAnimationBase::~plLayerAnimationBase()
{
    delete fPreshadeColorCtl;
    delete fRuntimeColorCtl;
    delete fAmbientColorCtl;
    delete fSpecularColorCtl;
    delete fOpacityCtl;
    delete fTransformCtl;
    delete [] fSegmentID;
}

void plLayerAnimationBase::Read(hsStream* s, hsResMgr* mgr)
{
    plLayerInterface::Read(s, mgr);

    fPreshadeColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
    fRuntimeColorCtl = plController::ConvertNoRef( mgr->ReadCreatable( s ) );
    fAmbientColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
    fSpecularColorCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
    fOpacityCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));
    fTransformCtl = plController::ConvertNoRef(mgr->ReadCreatable(s));

    if( fOpacityCtl )
    {
        fOwnedChannels |= kOpacity;
        fOpacity = TRACKED_NEW hsScalar;
    }
    if( fPreshadeColorCtl )
    {
        fOwnedChannels |= kPreshadeColor;
        fPreshadeColor = TRACKED_NEW hsColorRGBA;
    }
    if( fRuntimeColorCtl )
    {
        fOwnedChannels |= kRuntimeColor;
        fRuntimeColor = TRACKED_NEW hsColorRGBA;
    }
    if( fAmbientColorCtl )
    {
        fOwnedChannels |= kAmbientColor;
        fAmbientColor = TRACKED_NEW hsColorRGBA;
    }
    if( fSpecularColorCtl )
    {
        fOwnedChannels |= kSpecularColor;
        fSpecularColor = TRACKED_NEW hsColorRGBA;
    }
    if( fTransformCtl )
    {
        fOwnedChannels |= kTransform;
        fTransform = TRACKED_NEW hsMatrix44;
    }
    fLength = IMakeUniformLength();
}

void plLayerAnimationBase::Write(hsStream* s, hsResMgr* mgr)
{
    plLayerInterface::Write(s, mgr);

    mgr->WriteCreatable(s, fPreshadeColorCtl);
    mgr->WriteCreatable(s, fRuntimeColorCtl);
    mgr->WriteCreatable(s, fAmbientColorCtl);
    mgr->WriteCreatable(s, fSpecularColorCtl);
    mgr->WriteCreatable(s, fOpacityCtl);
    mgr->WriteCreatable(s, fTransformCtl);
}

plLayerInterface* plLayerAnimationBase::Attach(plLayerInterface* prev)
{
    return plLayerInterface::Attach(prev);
}

void plLayerAnimationBase::IEvalConvertedTime(hsScalar secs, UInt32 passChans, UInt32 evalChans, UInt32 &dirty)
{
    if( evalChans & kPreshadeColor )
    {
        fPreshadeColorCtl->Interp(fCurrentTime, fPreshadeColor);
        dirty |= kPreshadeColor;
    }
    else if( passChans & kPreshadeColor )
    {
        *fPreshadeColor = fUnderLay->GetPreshadeColor();
    }

    if( evalChans & kRuntimeColor )
    {
        fRuntimeColorCtl->Interp( fCurrentTime, fRuntimeColor );
        dirty |= kRuntimeColor;
    }
    else if( passChans & kRuntimeColor )
    {
        *fRuntimeColor = fUnderLay->GetRuntimeColor();
    }

    if( evalChans & kAmbientColor )
    {
        fAmbientColorCtl->Interp(fCurrentTime, fAmbientColor);
        dirty |= kAmbientColor;
    }
    else if( passChans & kAmbientColor )
    {
        *fAmbientColor = fUnderLay->GetAmbientColor();
    }

    if( evalChans & kSpecularColor )
    {
        fSpecularColorCtl->Interp( fCurrentTime, fSpecularColor );
        dirty |= kSpecularColor;
    }
    else if( passChans & kSpecularColor )
    {
        *fSpecularColor = fUnderLay->GetSpecularColor();
    }

    if( evalChans & kOpacity )
    {
        fOpacityCtl->Interp(fCurrentTime, fOpacity);
        *fOpacity *= 1.e-2f;
        dirty |= kOpacity;
    }
    else if( passChans & kOpacity )
    {
        *fOpacity = fUnderLay->GetOpacity();
    }

    if( evalChans & kTransform )
    {
        fTransformCtl->Interp(fCurrentTime, fTransform);
        dirty |= kTransform;
    }
    else if( passChans & kTransform )
    {
        *fTransform = fUnderLay->GetTransform();
    }
    fPassThruChannels = 0; // already handled, don't need to keep passing them through.
}

hsBool plLayerAnimationBase::MsgReceive(plMessage* msg)
{
    return plLayerInterface::MsgReceive(msg);
}

void plLayerAnimationBase::SetPreshadeColorCtl(plController* colCtl)
{
    if( fPreshadeColorCtl )
        delete fPreshadeColorCtl;
    else
        fPreshadeColor = TRACKED_NEW hsColorRGBA;

    fOwnedChannels |= kPreshadeColor;
    fPreshadeColorCtl = colCtl;
}

void plLayerAnimationBase::SetRuntimeColorCtl(plController* colCtl)
{
    if( fRuntimeColorCtl )
        delete fRuntimeColorCtl;
    else
        fRuntimeColor = TRACKED_NEW hsColorRGBA;

    fOwnedChannels |= kRuntimeColor;
    fRuntimeColorCtl = colCtl;
}

void plLayerAnimationBase::SetAmbientColorCtl(plController* ambCtl)
{
    if( fAmbientColorCtl )
        delete fAmbientColorCtl;
    else
        fAmbientColor = TRACKED_NEW hsColorRGBA;

    fOwnedChannels |= kAmbientColor;
    fAmbientColorCtl = ambCtl;
}

void plLayerAnimationBase::SetSpecularColorCtl(plController* ambCtl)
{
    if( fSpecularColorCtl )
        delete fSpecularColorCtl;
    else
        fSpecularColor = TRACKED_NEW hsColorRGBA;

    fOwnedChannels |= kSpecularColor;
    fSpecularColorCtl = ambCtl;
}

void plLayerAnimationBase::SetOpacityCtl(plController* opaCtl)
{
    if( fOpacityCtl )
        delete fOpacityCtl;
    else
        fOpacity = TRACKED_NEW hsScalar;

    fOwnedChannels |= kOpacity;
    fOpacityCtl = opaCtl;
}

void plLayerAnimationBase::SetTransformCtl(plController* xfmCtl)
{
    if( fTransformCtl )
        delete fTransformCtl;
    else
        fTransform = TRACKED_NEW hsMatrix44;

    fOwnedChannels |= kTransform;
    fTransformCtl = xfmCtl;
}

hsScalar plLayerAnimationBase::IMakeUniformLength()
{
    fLength = 0;
    if( fPreshadeColorCtl && (fPreshadeColorCtl->GetLength() > fLength) )
        fLength = fPreshadeColorCtl->GetLength();
    if( fRuntimeColorCtl && (fRuntimeColorCtl->GetLength() > fLength) )
        fLength = fRuntimeColorCtl->GetLength();
    if( fAmbientColorCtl && (fAmbientColorCtl->GetLength() > fLength) )
        fLength = fAmbientColorCtl->GetLength();
    if( fSpecularColorCtl && (fSpecularColorCtl->GetLength() > fLength) )
        fLength = fSpecularColorCtl->GetLength();
    if( fOpacityCtl && (fOpacityCtl->GetLength() > fLength) )
        fLength = fOpacityCtl->GetLength();
    if( fTransformCtl && (fTransformCtl->GetLength() > fLength) )
        fLength = fTransformCtl->GetLength();

    return fLength;
}

/////////////////////////////////////////////////////////////////////////////////

plLayerAnimation::plLayerAnimation()
:
    plLayerAnimationBase(),
    fLayerSDLMod(nil)
{
    fTimeConvert.SetOwner(this);
}

plLayerAnimation::~plLayerAnimation()
{
    delete fLayerSDLMod;
}

void plLayerAnimation::Read(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimationBase::Read(s, mgr);

    fTimeConvert.Read(s, mgr);  
    if (!(fTimeConvert.IsStopped()))
    {   
        plSynchEnabler ps(true);    // enable dirty tracking so that we send state about
                                    // the anim resetting to start now.
        fTimeConvert.SetCurrentAnimTime(0, true);
    }
    Eval(hsTimer::GetSysSeconds(),0,0);

    // add sdl modifier
    delete fLayerSDLMod;
    fLayerSDLMod = TRACKED_NEW plLayerSDLModifier;
    fLayerSDLMod->SetLayerAnimation(this);
}


void plLayerAnimation::Write(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimationBase::Write(s, mgr);

    fTimeConvert.Write(s, mgr);
}

plLayerInterface* plLayerAnimation::Attach(plLayerInterface* prev)
{
    fCurrentTime = fTimeConvert.CurrentAnimTime()-1.f;

    return plLayerAnimationBase::Attach(prev);
}

UInt32 plLayerAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore)
{
    UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore);
    if( wSecs != fEvalTime )
    {
        UInt32 evalChans = 0;
        UInt32 passChans = dirty | fPassThruChannels;
        hsScalar secs = fTimeConvert.WorldToAnimTime(wSecs);
        if( secs != fCurrentTime )
        {
            evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels;
            fCurrentTime = secs;
        }

        IEvalConvertedTime(secs, passChans, evalChans, dirty);
    }
    fEvalTime = wSecs;
    return dirty;
}

hsBool plLayerAnimation::MsgReceive(plMessage* msg)
{
    // pass sdl msg to sdlMod
    plSDLModifierMsg* sdlMsg = plSDLModifierMsg::ConvertNoRef(msg);
    if (sdlMsg && fLayerSDLMod)
    {
        if (fLayerSDLMod->MsgReceive(sdlMsg))
            return true;    // msg handled
    }

    hsBool retVal = false;
    plAnimCmdMsg* cmdMsg = plAnimCmdMsg::ConvertNoRef(msg);
    if( cmdMsg )
    {
        // Evaluate first, so we'll be transitioning from our
        // real current state, whether we've been evaluated (in view)
        // lately or not.
        TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0);
        retVal = fTimeConvert.HandleCmd(cmdMsg);
        DirtySynchState(kSDLLayer, 0);
    }

    if( retVal )
    {
        if( !fTimeConvert.IsStopped() || fTimeConvert.GetFlag(plAnimTimeConvert::kForcedMove) )
        {
            ClaimChannels(fOwnedChannels);
            fCurrentTime = -1.f; // force an eval
        }
    }
    else
    {
        retVal = plLayerAnimationBase::MsgReceive(msg);
    }
    
    return retVal;
}

void plLayerAnimation::DefaultAnimation()
{
    IMakeUniformLength();
    fTimeConvert.SetBegin(0);
    fTimeConvert.SetEnd(fLength);
    fTimeConvert.SetLoopPoints(0,fLength);
    fTimeConvert.Loop();
    fTimeConvert.Start();
}

///////////////////////////////////////////////////////////////////////////////////////

plLayerLinkAnimation::plLayerLinkAnimation() : 
    fLinkKey(nil), 
    fLeavingAge(true),
    fEnabled(true), 
    fFadeFlags(0), 
    fLastFadeFlag(0),
    fFadeFlagsDirty(false) 
{ 
    fIFaceCallback = TRACKED_NEW plEventCallbackMsg();
    fIFaceCallback->fEvent = kTime;
    fIFaceCallback->fRepeats = 0;           
}

plLayerLinkAnimation::~plLayerLinkAnimation() 
{ 
    hsRefCnt_SafeUnRef(fIFaceCallback);
}


void plLayerLinkAnimation::Read(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimation::Read(s, mgr);
    
    fLinkKey = mgr->ReadKey(s);
    fLeavingAge = s->ReadBool();
    plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectBCMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plLinkEffectPrepBCMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plCameraTargetFadeMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plAvatarStealthModeMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plIfaceFadeAvatarMsg::Index(), GetKey());
    plgDispatch::Dispatch()->RegisterForExactType(plPseudoLinkAnimTriggerMsg::Index(), GetKey());
    
    fIFaceCallback->AddReceiver(GetKey());  
}


void plLayerLinkAnimation::Write(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimation::Write(s, mgr);
    
    mgr->WriteKey(s, fLinkKey);
    s->WriteBool(fLeavingAge);
}

UInt32 plLayerLinkAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore)
{
    UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore);
    if (wSecs != fEvalTime)
    {
        UInt32 evalChans = 0;
        UInt32 passChans = dirty | fPassThruChannels;
        hsScalar oldAnimTime = fTimeConvert.CurrentAnimTime();
        hsScalar secs = oldAnimTime;
        
        if (fFadeFlagsDirty)
        {
            hsScalar goal = 0.f;

            if (fFadeFlags & kFadeLinkPrep)
                secs = goal = fLength;
            else
            {
                hsScalar rate = 0.f;
                hsScalar delta = (hsScalar)(wSecs - fEvalTime);
                
                if (fFadeFlags & kFadeLinking)
                {
                    goal = fLength;
                    rate = 1.f;
                }
                else if (fFadeFlags & kFadeCamera)
                {
                    goal = fLength;
                    rate = 10.f;
                }
                else if (fFadeFlags & (kFadeIFace | kFadeCCR))
                {
                    goal = fLength * 0.4f;
                    rate = 10.f;
                }
                else if (fFadeFlags == 0)
                {
                    goal = 0.f;
                    if (fLastFadeFlag == kFadeLinking)
                        rate = 1.f;
                    else
                        rate = 10.f;
                }

                if (fabs(oldAnimTime - goal) < delta * rate || rate == 0)
                    secs = goal;
                else if (goal > oldAnimTime)
                    secs = oldAnimTime + delta * rate;
                else
                    secs = oldAnimTime - delta * rate;
            }

            if (secs == goal)
                fFadeFlagsDirty = false;
        }
        
        if( secs != fCurrentTime )
        {
            fTimeConvert.SetCurrentAnimTime(secs);
            if (secs == 0.f || oldAnimTime == 0.f)
            {
                // Either we're going opaque, or we were opaque and now we're fading.
                // Tell the armature to re-eval its opacity settings.
                plAvatarOpacityCallbackMsg *opacityMsg = TRACKED_NEW plAvatarOpacityCallbackMsg(fLinkKey, kStop);
                opacityMsg->SetBCastFlag(plMessage::kPropagateToModifiers);
                opacityMsg->Send();
            }               
            evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels;
            fCurrentTime = secs;
        }
        IEvalConvertedTime(secs, passChans, evalChans, dirty);
    }
    fEvalTime = wSecs;
    return dirty;
}

void plLayerLinkAnimation::SetFadeFlag(UInt8 flag, hsBool val)
{
    if (val)
        fFadeFlags |= flag;
    else
        fFadeFlags &= ~flag;

    if (fFadeFlags == 0)
        fLastFadeFlag = flag;
    
    TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0);
    ClaimChannels(fOwnedChannels);
    fCurrentTime = -1; // force eval
    fFadeFlagsDirty = true;
}

hsBool plLayerLinkAnimation::MsgReceive( plMessage* pMsg )
{
    plLinkEffectPrepBCMsg *bcpMsg = plLinkEffectPrepBCMsg::ConvertNoRef(pMsg);
    if (bcpMsg != nil)
    {
        if (bcpMsg->fLinkKey != fLinkKey || bcpMsg->fLeavingAge)
            return true;
    
        SetFadeFlag(kFadeLinkPrep, true);
        return true;
    }
        
        
    plLinkEffectBCMsg *msg = plLinkEffectBCMsg::ConvertNoRef(pMsg);
    if (msg != nil)
    {
        if (msg->fLinkKey == fLinkKey)
        {
            SetFadeFlag(kFadeLinkPrep, false);
            if (msg->HasLinkFlag(plLinkEffectBCMsg::kLeavingAge))
                SetFadeFlag(kFadeLinking, true);
            else
                SetFadeFlag(kFadeLinking, false);

            if (msg->HasLinkFlag(plLinkEffectBCMsg::kSendCallback))
            {
                plLinkEffectsMgr *mgr;
                if (mgr = plLinkEffectsMgr::ConvertNoRef(msg->GetSender()->ObjectIsLoaded()))
                    mgr->WaitForEffect(msg->fLinkKey, fTimeConvert.GetEnd() - fTimeConvert.GetBegin());
            }           
        }
        return true;
    }

    plPseudoLinkAnimTriggerMsg* pSeudoMsg = plPseudoLinkAnimTriggerMsg::ConvertNoRef(pMsg);
    if (pSeudoMsg)
    {
        if (fLinkKey != pSeudoMsg->fAvatarKey)
            return true;

        if (pSeudoMsg->fForward)
            SetFadeFlag(kFadeLinking, true);
        else
            SetFadeFlag(kFadeLinking, false);
        
        // add a callback for when it's done if it's in forward
        plLinkEffectsMgr *mgr;
        if (mgr = plLinkEffectsMgr::ConvertNoRef(pMsg->GetSender()->ObjectIsLoaded()))
            if (pSeudoMsg->fForward)
                mgr->WaitForPseudoEffect(fLinkKey, fTimeConvert.GetEnd() - fTimeConvert.GetBegin());

        return true;
    }
    // used to fade the player in or out when entering / exiting first person mode
    // or when distance between camera and player is too small...
    plCameraTargetFadeMsg* fMsg = plCameraTargetFadeMsg::ConvertNoRef(pMsg);
    if (fMsg)
    {
        if (fLinkKey != fMsg->GetSubjectKey())
            return true;

        if (fMsg->FadeOut())
            SetFadeFlag(kFadeCamera, true);
        else
            SetFadeFlag(kFadeCamera, false);    

        return true;
    }
    plIfaceFadeAvatarMsg* iMsg = plIfaceFadeAvatarMsg::ConvertNoRef(pMsg);
    if (iMsg)
    {
        if (fLinkKey != iMsg->GetSubjectKey())
            return true;

        if (iMsg->GetEnable())
        {
            Enable(true);
        }
        else if (iMsg->GetDisable())
        {
            Enable(false); // disable and un-fade
            SetFadeFlag(kFadeIFace, false);
        }
        else
        if (fEnabled)
        {
            if (iMsg->FadeOut())
                SetFadeFlag(kFadeIFace, true);
            else
                SetFadeFlag(kFadeIFace, false);
        }
        return true;
    }

    plAvatarStealthModeMsg *sMsg = plAvatarStealthModeMsg::ConvertNoRef(pMsg);
    if (sMsg)
    {
        if (sMsg->GetSender() == fLinkKey)
        {
            if (sMsg->fMode == plAvatarStealthModeMsg::kStealthCloakedButSeen)
            {
                SetFadeFlag(kFadeCCR, true);
            }
            else if (sMsg->fMode == plAvatarStealthModeMsg::kStealthVisible)
            {
                SetFadeFlag(kFadeCCR, false);
            }
            // Don't need to set opacity if we're fully cloaked, since we won't
            // even be drawing the spans (due to plEnableMsg() on the sceneObject)
        }
        return true;
    }

    return plLayerAnimation::MsgReceive( pMsg );
}

///////////////////////////////////////////////////////////////////////////////////////////////

plLayerSDLAnimation::plLayerSDLAnimation() : plLayerAnimationBase(), fVar(nil), fVarName(nil) {}

plLayerSDLAnimation::~plLayerSDLAnimation()
{
    delete [] fVarName;
}

UInt32 plLayerSDLAnimation::Eval(double wSecs, UInt32 frame, UInt32 ignore)
{
    UInt32 dirty = plLayerInterface::Eval(wSecs, frame, ignore);
    if( wSecs != fEvalTime )
    {
        UInt32 evalChans = 0;
        UInt32 passChans = dirty | fPassThruChannels;

        if (fEvalTime < 0)
        {
            if (fVarName != nil)
            {
                extern const plSDLModifier *ExternFindAgeSDL();
                const plSDLModifier *sdlMod = ExternFindAgeSDL();
                if (sdlMod)
                {
                    fVar = sdlMod->GetStateCache()->FindVar(fVarName);
                    if (fVar)
                        sdlMod->AddNotifyForVar(GetKey(), fVarName, 0);
                }
            }
        }
        hsScalar secs;
        if (fVar)
            fVar->Get(&secs);
        else
            secs = 0.f;

        // We're guaranteed a 0-1 time. Scale that to our animation length.
        secs *= GetLength();

        if( secs != fCurrentTime )
        {
            evalChans = fOwnedChannels & ~ignore & ~fPassThruChannels;
            fCurrentTime = secs;
        }

        IEvalConvertedTime(secs, passChans, evalChans, dirty);
    }
    fEvalTime = wSecs;
    return dirty;
}

hsBool plLayerSDLAnimation::MsgReceive(plMessage* msg)
{
    plSDLNotificationMsg* nMsg = plSDLNotificationMsg::ConvertNoRef(msg);
    if (nMsg)
    {
        TopOfStack()->Eval(hsTimer::GetSysSeconds(), 0, 0);
        ClaimChannels(fOwnedChannels);
        return true;
    }

    return plLayerAnimationBase::MsgReceive(msg);
}

void plLayerSDLAnimation::Read(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimationBase::Read(s, mgr);

    fVarName = s->ReadSafeString();
}

void plLayerSDLAnimation::Write(hsStream* s, hsResMgr* mgr)
{
    plLayerAnimationBase::Write(s, mgr);

    s->WriteSafeString(fVarName);
}

void plLayerSDLAnimation::SetVarName(char *name) 
{ 
    delete [] fVarName; 
    fVarName = hsStrcpy(name); 
}