/*==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 "plAGMasterSDLModifier.h"
#include "plSDL/plSDL.h"
#include "plInterp/plAnimTimeConvert.h"
#include "pnSceneObject/plSceneObject.h"
#include "plAGMasterMod.h"
#include "plAGAnimInstance.h"
#include "plgDispatch.h"
#include "pnMessage/plTimeMsg.h"
#include "hsTimer.h"
#include "plMessage/plAnimCmdMsg.h"

// static vars
char plAGMasterSDLModifier::AGMasterVarNames::kStrAtcs[]="atcs";
char plAGMasterSDLModifier::AGMasterVarNames::kStrBlends[]="blends";

UInt32 plAGMasterSDLModifier::IApplyModFlags(UInt32 sendFlags)
{
    // ugly hack so bug light animation state isn't stored on the server
    if (stricmp(GetTarget()->GetKeyName(), "RTOmni-BugLightTest") == 0)
        return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState);
    // ditto for the KI light
    if (stricmp(GetTarget()->GetKeyName(), "RTOmniKILight") == 0)
        return (sendFlags | plSynchedObject::kDontPersistOnServer | plSynchedObject::kIsAvatarState);

    return sendFlags;
}

//
// copy blends values from current state into sdl
//
void plAGMasterSDLModifier::IPutBlends(plStateDataRecord* state, plAGMasterMod* agMaster)
{
    int numBlends = agMaster->GetNumPrivateAnimations();    // each private anim has a blend value
    plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends);
    if (blendsVar->GetCount() != numBlends)
        blendsVar->Alloc(numBlends);

    // sdl copy
    int i;
    for(i=0;i<numBlends; i++)
    {
        blendsVar->Set((UInt8)(agMaster->GetAnimInstance(i)->GetBlend() * 255), i);
    }
}


//
// Copy atcs from current state into sdl
//
void plAGMasterSDLModifier::IPutCurrentStateIn(plStateDataRecord* dstState)
{
    plSceneObject* sobj=GetTarget();
    hsAssert(sobj, "plAGMasterSDLModifier, nil target");
    
    plAGMasterMod* agMaster=IGetObjectsAGMasterMod(sobj);
    hsAssert(agMaster, "nil AGMasterMod");

    if (agMaster)
    {
        IPutBlends(dstState, agMaster);

        int numAnims = agMaster->GetNumATCAnimations();     
        plSDStateVariable* atcsVar = dstState->FindSDVar(AGMasterVarNames::kStrAtcs);
        if (atcsVar->GetCount() != numAnims)
            atcsVar->Resize(numAnims);

        // copy atcs to sdl 
        int i;
        for(i=0;i<numAnims; i++)
        {
            plStateDataRecord* atcStateDataRec = atcsVar->GetStateDataRecord(i);
            plAnimTimeConvert* animTimeConvert = agMaster->GetATCAnimInstance(i)->GetTimeConvert();

            IPutATC(atcStateDataRec, animTimeConvert);
        }
    }
}

//
// Given a scene object, find and return it's AGMasterMod
//
plAGMasterMod* plAGMasterSDLModifier::IGetObjectsAGMasterMod(plSceneObject* obj)
{
    int count = obj->GetNumModifiers();

    for (int i = 0; i < count; i++)
    {
        plAGMasterMod * avMod = const_cast<plAGMasterMod*>(plAGMasterMod::ConvertNoRef(obj->GetModifier(i)));
        if(avMod)
            return avMod;
    }

    return nil;
}

//
// Apply state in SDL record to current animation state 
//
void plAGMasterSDLModifier::ISetCurrentBlends(const plStateDataRecord* state, plAGMasterMod* objAGMaster)
{
    // Check Blends
    plSimpleStateVariable* blendsVar = state->FindVar(AGMasterVarNames::kStrBlends);
    if (blendsVar->IsUsed())
    {       
        int i;
        if (blendsVar->GetCount() != objAGMaster->GetNumPrivateAnimations())
            return; // bogus state

        for (i=0;i<blendsVar->GetCount();i++)
        {
            UInt8 blend;
            blendsVar->Get(&blend, i);
            objAGMaster->GetAnimInstance(i)->SetBlend(blend / 255.f);
        }
    }
}

//
// Change the object's animation state to reflect what is specified in the 
// stateDataRecord.
//
void plAGMasterSDLModifier::ISetCurrentStateFrom(const plStateDataRecord* srcState)
{
    plSceneObject* sobj=GetTarget();
    hsAssert(sobj, "plAGMasterSDLModifier, nil target");
    
    plAGMasterMod* objAGMaster=IGetObjectsAGMasterMod(sobj);
    hsAssert(objAGMaster, "can't find object's AGMasterSDLState");
    
    ISetCurrentBlends(srcState, objAGMaster);

    plSDStateVariable* atcsVar = srcState->FindSDVar(AGMasterVarNames::kStrAtcs);
    if (atcsVar->IsUsed())
    {
        if (objAGMaster->GetNumATCAnimations() != atcsVar->GetCount())
            return;

        int i;
        for(i=0;i<atcsVar->GetCount(); i++)
        {
            plStateDataRecord* atcStateDataRec = atcsVar->GetStateDataRecord(i);
            plAnimTimeConvert* objAtc = objAGMaster->GetATCAnimInstance(i)->GetTimeConvert();   // dst                  
            ISetCurrentATC(atcStateDataRec, objAtc);
            objAtc->EnableCallbacks(false);
        }
        objAGMaster->IRegForEval(objAGMaster->HasRunningAnims());

        // Force one eval, then re-enable all the callbacks
        double time = (hsTimer::GetSysSeconds() - hsTimer::GetDelSysSeconds());

        if (objAGMaster->fIsGrouped && objAGMaster->fMsgForwarder)
        {
            hsScalar animTimeFromWorldTime = (objAGMaster->GetNumATCAnimations() > 0) ? objAGMaster->GetATCAnimInstance(0)->GetTimeConvert()->WorldToAnimTimeNoUpdate(time) : 0.0f;

            plAGCmdMsg *msg = TRACKED_NEW plAGCmdMsg();
            msg->SetCmd(plAGCmdMsg::kSetAnimTime);
            msg->fAnimTime = animTimeFromWorldTime;
            msg->AddReceiver(objAGMaster->fMsgForwarder->GetKey());
            plgDispatch::MsgSend(msg);
        }
        else
        {
            objAGMaster->AdvanceAnimsToTime(time);
        }

        for (i = 0; i < objAGMaster->GetNumATCAnimations(); i++)
            objAGMaster->GetATCAnimInstance(i)->GetTimeConvert()->EnableCallbacks(true);
    }
}