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

#include "plModifier/plGameMarkerModifier.h"

#include "plMessage/plLoadCloneMsg.h"
#include "pnMessage/plWarpMsg.h"
#include "pnSceneObject/plCoordinateInterface.h"
#include "plMessage/plAnimCmdMsg.h"
#include "pnMessage/plEnableMsg.h"
#include "pnMessage/plSoundMsg.h"
#include "pnSceneObject/plAudioInterface.h"

// For Init
#include "pnMessage/plClientMsg.h"
#include "pnSceneObject/plSceneObject.h"
#include "plResMgr/plResManager.h"
#include "plResMgr/plKeyFinder.h"

plUoid pfMarkerInfo::fMarkerUoid;

static const int kFreezeLen = 10;       // How long a marker is frozen after you hit it

void pfMarkerInfo::Init()
{
    plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr();

    // Force the client to keep the GlobalMarkers keys loaded, so we don't load them every time we clone
    plClientMsg* loadAgeKeysMsg = TRACKED_NEW plClientMsg(plClientMsg::kLoadAgeKeys);
    loadAgeKeysMsg->SetAgeName("GlobalMarkers");
    loadAgeKeysMsg->Send(resMgr->FindKey(kClient_KEY));

    //
    // Get the Uoid for the markers
    //
    plLocation markerLoc = plKeyFinder::Instance().FindLocation("GlobalMarkers", "Markers");

    if (markerLoc.IsValid())
        fMarkerUoid = plUoid(markerLoc, plSceneObject::Index(), "MarkerRoot");
    else
        fMarkerUoid.Invalidate();
}

pfMarkerInfo::pfMarkerInfo(const hsPoint3& pos, bool isNew) :
    fMod(nil),
    fPosition(pos),
    fType(kMarkerOpen),
    fLastChange(0),
    fVisible(true),
    fIsNew(isNew),
    fSpawned(false)
{
}

void pfMarkerInfo::Spawn(MarkerType type)
{
    if (!fMarkerUoid.IsValid())
    {
        plResManager* resMgr = (plResManager*)hsgResMgr::ResMgr();
        plLocation markerLoc = plKeyFinder::Instance().FindLocation("GlobalMarkers", "Markers");

        if (markerLoc.IsValid())
            fMarkerUoid = plUoid(markerLoc, plSceneObject::Index(), "MarkerRoot");
        else
        {
            hsAssert(false, "Unable to spawn markers because the marker age was not loaded or found");
            return;
        }
    }

    fType = type;
    fLastChange = 0;

    plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(fMarkerUoid, pfMarkerMgr::Instance()->GetKey(), 0);
    cloneMsg->SetBCastFlag(plMessage::kNetPropagate, false);
    fKey = cloneMsg->GetCloneKey();

    cloneMsg->Send();
}

void pfMarkerInfo::InitSpawned(plKey markerKey)
{
    fKey = markerKey;
    fSpawned = true;

    plSceneObject* so = plSceneObject::ConvertNoRef(fKey->GetObjectPtr());
    fMod = (plGameMarkerModifier*)so->GetModifierByType(plGameMarkerModifier::Index());
    hsAssert(fMod, "Couldn't find marker modifier");
    fMod->FixupAnimKeys();

    // Warp it into position
    hsMatrix44 pos;
    pos.Reset();
    pos.SetTranslate(&fPosition);
    plWarpMsg* warpMsg = TRACKED_NEW plWarpMsg(pfMarkerMgr::Instance()->GetKey(), fKey, plWarpMsg::kFlushTransform, pos);
    warpMsg->Send();

    // update its state
    Show(fVisible);
    IPlayColor(true);
    if (fType == kMarkerLocalSelected)
        IPlayBounce(true);
}

void pfMarkerInfo::Show(bool show)
{
    fVisible = show;

    if (fSpawned) {
        plEnableMsg* msg = TRACKED_NEW plEnableMsg;
        msg->SetBCastFlag(plMessage::kPropagateToChildren);
        msg->SetCmd(plEnableMsg::kDrawable);
        msg->SetCmd(show ? plEnableMsg::kEnable : plEnableMsg::kDisable);
        msg->SetSender(pfMarkerMgr::Instance()->GetKey());
        msg->Send(fKey);
    }
}

void pfMarkerInfo::SetFrozen(double freezeStartTime)
{
    fLastChange = freezeStartTime;
    IPlayBounce(true);
}

void pfMarkerInfo::Update(double curTime)
{
    if (fLastChange != 0 && (curTime - fLastChange) > kFreezeLen)
    {
        fLastChange = 0;
        IPlayBounce(false);
    }

    if (fIsNew)
    {
        IPlaySound(true);
        fIsNew = false;
    }
}

void pfMarkerInfo::Remove()
{
    if (fKey)
    {
        plLoadCloneMsg* cloneMsg = TRACKED_NEW plLoadCloneMsg(fKey, pfMarkerMgr::Instance()->GetKey(), 0, false);
        cloneMsg->SetBCastFlag(plMessage::kNetPropagate, false);
        cloneMsg->Send();

        fKey = nil;
        fMod = nil;
    }
}

void pfMarkerInfo::SetType(pfMarkerInfo::MarkerType type)
{
    if (fType == kMarkerLocalSelected)
        IPlayBounce(false);

    IPlayColor(false);
    fType = type;
    IPlayColor(true);

    if (fType == kMarkerLocalSelected)
        IPlayBounce(true);
}

void pfMarkerInfo::IPlayBounce(bool play)
{
    if (fMod && fSpawned)
    {
        // Send anim start/stop msg
        plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg;
        animMsg->SetCmd(play ? plAnimCmdMsg::kContinue : plAnimCmdMsg::kStop);
        animMsg->SetCmd(plAnimCmdMsg::kSetLooping);
        animMsg->SetCmd(plAnimCmdMsg::kGoToBegin);
        animMsg->SetSender(pfMarkerMgr::Instance()->GetKey());
        animMsg->Send(fMod->fBounceAnimKey);
    }
}

void pfMarkerInfo::IPlayColor(bool play)
{
    if (fMod && fSpawned)
    {
        // Play the correct color anim
        plKey key = nil;
        switch (fType)
        {
        case kMarkerOpen:
        case kMarkerLocal:
        case kMarkerLocalSelected:
            key = fMod->fOpenAnimKey;
            break;

        case kMarkerGreen:
            key = fMod->fGreenAnimKey;
            break;

        case kMarkerRed:
            key = fMod->fRedAnimKey;
            break;
        }

        plAnimCmdMsg* animMsg = TRACKED_NEW plAnimCmdMsg;
        animMsg->SetCmd(play ? plAnimCmdMsg::kContinue : plAnimCmdMsg::kStop);
        animMsg->SetCmd(plAnimCmdMsg::kSetLooping);
        animMsg->SetCmd(plAnimCmdMsg::kGoToBegin);
        animMsg->SetSender(pfMarkerMgr::Instance()->GetKey());
        animMsg->AddReceiver(key);
        animMsg->Send();
    }
}

void pfMarkerInfo::IPlaySound(bool place)
{
    if (fMod && fSpawned)
    {
        const plAudioInterface* ai = fMod->GetTarget()->GetAudioInterface();

        plSoundMsg* msg = TRACKED_NEW plSoundMsg;
        msg->fIndex = place ? fMod->fPlaceSndIdx : fMod->fHitSndIdx;
        msg->SetCmd(plSoundMsg::kPlay);
        msg->SetSender(pfMarkerMgr::Instance()->GetKey());
        msg->Send(ai->GetKey());
    }
}