/*==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==*/
#ifndef _plNotifyMsg_h_
#define _plNotifyMsg_h_

#include "plMessage.h"
#include "hsResMgr.h"
#include "../pnModifier/plSingleModifier.h"
#include "hsUtils.h"
#include "hsGeometry3.h"


//
//  repeatable Event data in Notify message
//
// Updated 4.13.2002 mcn - Unions are all well and good, but only work on simple data types, and since
// plKey now isn't so simple...

class proEventData
{
public:
	// keep this enum synchronized with the list at the top of PlasmaTypes.py
	enum eventType
	{
		kCollision=1,
		kPicked =2,
		kControlKey=3,
		kVariable=4,
		kFacing=5,
		kContained=6,
		kActivate=7,
		kCallback=8,
		kResponderState=9,
		kMultiStage=10,
		kSpawned=11,
		kClickDrag=12,
		kCoop=13,
		kOfferLinkingBook=14,
		kBook=15,
		kClimbingBlockerHit=16,
		kNone
	};

	proEventData( Int32 evtType = kNone ) : fEventType( evtType )
	{
	}
	virtual ~proEventData() {}

	enum dataType
	{
		kNumber=1,
		kKey,
		kNotta
	};

	// what the multstage event types are for multistage.fEvent
	enum multiStageEventType
	{
		kEnterStage=1,
		kBeginingOfLoop,
		kAdvanceNextStage,
		kRegressPrevStage,
		kNothing
	};

	Int32 fEventType;		// what type of event (evenType enum)

	static proEventData* ICreateEventDataType(Int32 type);

	static proEventData* Read(hsStream* stream, hsResMgr* mgr);
	void Write(hsStream* stream, hsResMgr* mgr);

	static proEventData* ReadVersion(hsStream* s, hsResMgr* mgr);
	void WriteVersion(hsStream* s, hsResMgr* mgr);

protected:
	virtual void IInit() {}
	virtual void IDestruct() {}
	virtual void IRead(hsStream* stream, hsResMgr* mgr) {}
	virtual void IWrite(hsStream* stream, hsResMgr* mgr) {}

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr) {}
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr) {}
};

// The following macro makes it easy (or easier) to define new event data types
#define proEventType(type) class pro##type##EventData : public proEventData { public: pro##type##EventData() : proEventData( k##type ) {IInit();} virtual ~pro##type##EventData(){IDestruct();}

proEventType(Collision)
	hsBool	fEnter;		// entering? (false is exit)
	plKey	fHitter;	// collision hitter (other probably player)
	plKey	fHittee;	// collision hittee (probably us)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Picked)
	plKey	fPicker;	// who picked it (problably player)
	plKey	fPicked;	// object that was picked
	hsBool	fEnabled;	// pick (true) or un-pick (false)
	hsPoint3 fHitPoint;	// where on the picked object that it was picked on

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Spawned)
	plKey	fSpawner;	// who spawned it (typicall plNPCSpawnMod)
	plKey	fSpawnee;	// what was spawned (typically a scene object with an armature mod)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(ControlKey)
	Int32	fControlKey;	// what control key was hit
	hsBool	fDown;			// was the key going down (false if going up)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Variable)
	char*	fName;			// name of variable
	Int32	fDataType;		// type of data

	// Can't be a union, sadly, but it isn't that much of a waste of space...
	hsScalar	fNumber;	// if its a number
	plKey		fKey;		// if its a plKey (pointer to something)


protected:
	virtual void IInit();
	virtual void IDestruct();
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Facing)
	plKey		fFacer; // what was facing
	plKey		fFacee; // what was being faced
	hsScalar	dot;	 // the dot prod of their view vectors
	hsBool		enabled; // Now meets facing requirement (true) or no longer meets requirement (false)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Contained)
	plKey	fContained;	// who's inside of 
	plKey	fContainer;	// this containing volume
	hsBool	fEntering;	// entering volume (true) or exiting (false)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Activate)
	hsBool fActive;		// Whether or not to use the data in this field
	hsBool fActivate;	// the data

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};		

proEventType(Callback)
	Int32	fEventType;  // enumerated in plEventCallbackMsg.h

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(ResponderState)
	Int32	fState;		// what state the responder should be switched to before triggering

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(MultiStage)
	Int32	fStage;	
	Int32	fEvent;
	plKey	fAvatar;	// who was running the stage

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Coop)
	UInt32	fID;		// player ID of the initiator
	UInt16	fSerial;	// serial number for the initiator
protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};


proEventType(ClickDrag)
	plKey picker; // always the local avatar in this case
	plKey picked;
	hsScalar animPos; // 0.0 to 1.0 animation percentage
};

proEventType(OfferLinkingBook)
	plKey offerer; // the avatar offering the linking book to you
	int targetAge; // the age the book is for - taken from konstant list of age buttons in xLinkingBookPopupGUI.py
	int offeree; // who we are offering the book to
protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(Book)
	UInt32	fEvent;		// The type of event. See pfJournalBook.h for enumsu
	UInt32	fLinkID;	// The link ID of the image clicked, if an image link event, otherwise unused
protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};

proEventType(ClimbingBlockerHit)
	plKey	fBlockerKey;	// collision hittee (probably us)

protected:
	virtual void IRead(hsStream* stream, hsResMgr* mgr);
	virtual void IWrite(hsStream* stream, hsResMgr* mgr);

	virtual void IReadVersion(hsStream* s, hsResMgr* mgr);
	virtual void IWriteVersion(hsStream* s, hsResMgr* mgr);
};


/////////////////////////////////////////////////////////////////////////////
//
//  MESSAGE    : plNotifyMsg
//  PARAMETERS : none
//
//  PURPOSE    : This is the message that notifies someone (either a responder or activator)
//             : that some event or transition of state has happened
//
//
class plNotifyMsg : public plMessage
{
protected:
	virtual void IInit();

public:
	plNotifyMsg() { IInit(); }
	plNotifyMsg(const plKey &s, const plKey &r);
	~plNotifyMsg();

	CLASSNAME_REGISTER( plNotifyMsg );
	GETINTERFACE_ANY( plNotifyMsg, plMessage );

	// data area
	enum notificationType
	{
		kActivator=0,
		kVarNotification,
		kNotifySelf,

		kResponderFF,			// Fast forward
		kResponderChangeState,	// Change state without triggering
	};
	Int32		fType;				// what type of notification
	hsScalar	fState;				// state of the notifier 0.0=false, 1.0=true
	Int32		fID;				// special ID mostly for responder State transitions
	hsTArray<proEventData*> fEvents;// list of events with data

	void SetType(notificationType type) { fType = type; }
	void SetState(hsScalar state) { fState = state; }

	// event records for the notify message
	void AddEvent( proEventData* ed);
	void AddCollisionEvent( hsBool enter, const plKey &other, const plKey &self, hsBool onlyOneCollision=true );
	void AddPickEvent( const plKey &other, const plKey& self, hsBool enabled, hsPoint3 hitPoint );
	void AddControlKeyEvent( Int32 key, hsBool down );
	void AddVariableEvent( const char* name, hsScalar number );
	void AddVariableEvent( const char *name, const plKey &key);
	void AddFacingEvent( const plKey &other, const plKey &self, hsScalar dot, hsBool enabled);
	void AddContainerEvent( const plKey &container, const plKey &contained, hsBool entering);
	void AddActivateEvent( hsBool activate );
	void AddCallbackEvent( Int32 event );
	void AddResponderStateEvent( Int32 state );
	void AddMultiStageEvent( Int32 stage, Int32 event, const plKey& avatar );
	void AddCoopEvent(UInt32 id, UInt16 serial);
	void AddSpawnedEvent (const plKey &spawner, const plKey &spawned);
	void AddClickDragEvent(const plKey& dragger, const plKey& dragee, hsScalar animPos);
	void AddOfferBookEvent(const plKey& offerer, int targetAge, int offeree);
	void AddBookEvent( UInt32 event, UInt32 linkID = 0 );
	void AddHitClimbingBlockerEvent(const plKey &blocker);
	proEventData* FindEventRecord( Int32 eventtype );
	Int32 GetEventCount() { return fEvents.Count(); }
	proEventData* GetEventRecord(Int32 i) { return fEvents[i]; }
	void ClearEvents();

	// Searches the event records for an event triggered by an avatar, and returns that key
	plKey GetAvatarKey();

	// IO 
	void Read(hsStream* stream, hsResMgr* mgr);
	void Write(hsStream* stream, hsResMgr* mgr);
	
	void ReadVersion(hsStream* s, hsResMgr* mgr);
	void WriteVersion(hsStream* s, hsResMgr* mgr);
};



#endif // _plNotifyMsg_h_