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

// singular
#include "plAvBrainCoop.h"

// local
#include "plArmatureMod.h"
#include "plAnimStage.h"
#include "plAvTaskSeek.h"
#include "plAvatarMgr.h"

// other
#include "plScene/plSceneNode.h"
#include "pnNetCommon/plNetApp.h"

// messages
#include "plMessage/plAvatarMsg.h"
#include "plMessage/plAvCoopMsg.h"
#include "plMessage/plPickedMsg.h"
#include "pnMessage/plNotifyMsg.h"

/////////////////////////////////////////////////////////////////////////////////////////
//
// STATIC
//
/////////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////////
//
// CODE
//
/////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////
//
// plAvBrainCoop
//
/////////////////////////////////////////////////////////////////////////////////////////

plAvBrainCoop::plAvBrainCoop()
: fInitiatorID(0),
  fInitiatorSerial(0)
{
}

// plAvBrainCoop ----------------------------------------------------------
// --------------
plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut,
							 MoveMode moveMode, plKey guestKey)
: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode),
  fGuestKey(guestKey)
{
	static UInt16 coopSerial = 0;

	// this particular constructor is only called by the initiator...
	fInitiatorID = plNetClientApp::GetInstance()->GetPlayerID();
	fInitiatorSerial = coopSerial++;
}

// plAvBrainCoop ----------------------------------------------------------
// --------------
plAvBrainCoop::plAvBrainCoop(UInt32 exitFlags, float fadeIn, float fadeOut,
							 MoveMode moveMode,	UInt32 initiatorID, UInt16 initiatorSerial,
							 plKey hostKey)
: plAvBrainGeneric(exitFlags, fadeIn, fadeOut, moveMode),
  fInitiatorID(initiatorID), fInitiatorSerial(initiatorSerial),
  fHostKey(hostKey)
{
}

// MsgReceive ----------------------------------
// -----------
hsBool plAvBrainCoop::MsgReceive(plMessage *msg)
{
	plPickedMsg *pickM = plPickedMsg::ConvertNoRef(msg);
	if(pickM)
	{
		if(fWaitingForClick)
		{
			fWaitingForClick = false;
			// clicks are never network propagated, so we can be sure that the
			// click was performed by the local player.
			plKey localPlayer = plNetClientApp::GetInstance()->GetLocalPlayerKey();

			if(localPlayer == fGuestKey)
			{
				plAvCoopMsg *coopM = TRACKED_NEW plAvCoopMsg(plAvCoopMsg::kGuestAccepted, fInitiatorID, fInitiatorSerial);
				coopM->SetBCastFlag(plMessage::kNetPropagate);
				coopM->Send();

			}
		}
	}
	return plAvBrainGeneric::MsgReceive(msg);
}

// RelayNotifyMsg ----------------------------------
// ---------------
bool plAvBrainCoop::RelayNotifyMsg(plNotifyMsg *msg)
{
	// add a coop event so the receiver will know what coop this message is from
	msg->AddCoopEvent(fInitiatorID, fInitiatorSerial);

	proMultiStageEventData * mtevt = static_cast<proMultiStageEventData *>(msg->FindEventRecord(proEventData::kMultiStage));
	if(mtevt)
		DebugMsg("COOP: Relaying multi-stage event to %d recipients (via plAvBrainCoop)", fRecipients.size());

	if(fRecipients.size() != 0)
	{
		bool foundARecipient = false;
		for (unsigned curRecipient = 0; curRecipient < fRecipients.size(); curRecipient++)
		{
			if (fRecipients[curRecipient])
			{
				foundARecipient = true;
				msg->AddReceiver(fRecipients[curRecipient]);
			}
		}

		if (!foundARecipient)
			return false;

		msg->Send();
		return true;
	}
	else
		return false;
}


void plAvBrainCoop::EnableGuestClick()
{
	fWaitingForClick = true;
}

// GetInitiatorID --------------------
// ---------------
UInt32 plAvBrainCoop::GetInitiatorID()
{
	return fInitiatorID;
}

// GetInitiatorSerial --------------------
// -------------------
UInt16 plAvBrainCoop::GetInitiatorSerial()
{
	return fInitiatorSerial;
}

// Read -------------------------------------------------
// -----
void plAvBrainCoop::Read(hsStream *stream, hsResMgr *mgr)
{
	plAvBrainGeneric::Read(stream, mgr);

	fInitiatorID = stream->ReadSwap32();
	fInitiatorSerial = stream->ReadSwap16();

	if(stream->Readbool())
	{
		fHostKey = mgr->ReadKey(stream);
	}
	if(stream->Readbool())
	{
		fGuestKey = mgr->ReadKey(stream);
	}
	fWaitingForClick = stream->Readbool();

	unsigned numRecipients = stream->ReadSwap16();
	for (unsigned i = 0; i < numRecipients; i++)
		fRecipients.push_back(mgr->ReadKey(stream));
}

// Write -------------------------------------------------
// ------
void plAvBrainCoop::Write(hsStream *stream, hsResMgr *mgr)
{
	plAvBrainGeneric::Write(stream, mgr);

	stream->WriteSwap32(fInitiatorID);
	stream->WriteSwap16(fInitiatorSerial);

	bool hasHostKey = (fHostKey != nil);
	bool hasGuestKey = (fGuestKey != nil);

	stream->Writebool(hasHostKey);
	if(hasHostKey)
		mgr->WriteKey(stream, fHostKey);

	stream->Writebool(hasGuestKey);
	if(hasGuestKey)
		mgr->WriteKey(stream, fGuestKey);

	stream->Writebool(fWaitingForClick);

	stream->WriteSwap16(fRecipients.size());
	for (unsigned i = 0; i < fRecipients.size(); i++)
		mgr->WriteKey(stream, fRecipients[i]);
}

plKey plAvBrainCoop::GetRecipient()
{
	if (fRecipients.size() == 0)
		return nil;
	return fRecipients[0];
}

void plAvBrainCoop::SetRecipient(plKey &recipient)
{
	fRecipients.push_back(recipient);
}