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

#include "plKey.h"
#include "hsTemplates.h"
#include "plUoid.h"  
#include "hsBitVector.h"
#include "plRefFlags.h"

//------------------------------------
// plKey is a handle to a keyedObject
//------------------------------------
class plKeyImp : public plKeyData 
{
public:
	plKeyImp();
	plKeyImp(plUoid, UInt32 pos,UInt32 len);
	virtual ~plKeyImp();

	virtual const plUoid&	GetUoid() const { return fUoid; }
	virtual const char*		GetName() const;

	virtual hsKeyedObject*	GetObjectPtr();
	virtual hsKeyedObject*	ObjectIsLoaded() const;
	virtual hsKeyedObject*	VerifyLoaded();

	// called before writing to disk so that static keys can have faster lookups (int compare instead of string compare)
	void SetObjectID(UInt32 id) {fUoid.SetObjectID(id);}

	//----------------------
	// I/O
	// ResMgr performs read, so it can search for an existing instance....
	//----------------------
	void Read(hsStream* s);
	void Write(hsStream* s);
	void WriteObject(hsStream* s);
	// For when you need to skip over a key in a stream
	static void SkipRead(hsStream* s);

	UInt32 GetStartPos() const	{ return fStartPos; } // for ResMgr to read the Objects
	UInt32 GetDataLen() const	{ return fDataLen;  } // for ResMgr to read the Objects

	//----------------------
	// Allow a keyed object to behave as if it has an active ref when in fact the object
	// should only be active ref'ed by a non-keyed parent.  Essentially just bumps/decs
	// the active ref count to facilitate normal object creation/destruction
	//----------------------
	virtual hsKeyedObject*	RefObject(plRefFlags::Type flags = plRefFlags::kActiveRef);
	virtual void			UnRefObject(plRefFlags::Type flags = plRefFlags::kActiveRef);

	//----------------------
	// Release has two behaviors, depending on whether the ref is active or passive:
	// Active - Release decs the ActiveRefCnt. When it gets to zero, the object will be deleted.
	// Passive - Unregisters my interest in when the object is created or destroyed.
	//----------------------
	virtual void Release(plKey targetKey);

	void UnRegister();
	void SetUoid(const plUoid& uoid);
	void SetupNotify(plRefMsg* msg, plRefFlags::Type flags);

	// hsKeyedObject use only!
	hsKeyedObject* SetObjectPtr(hsKeyedObject* p);

	////////////////////////////////////////////////////////////////////////////
	// ResManager/Registry use only!
	//

	//----------------------
	// Clone access
	//----------------------
	void	AddClone(plKeyImp* c);
	void	RemoveClone(plKeyImp* c) const;
	plKey	GetClone(UInt32 playerID, UInt32 cloneID) const;
	void	CopyForClone(const plKeyImp* p, UInt32 playerID, UInt32 cloneID);    // Copy the contents of p for cloning process

	UInt32	GetNumClones();
	plKey	GetCloneByIdx(UInt32 idx);
	plKey	GetCloneOwner() { return fCloneOwner; }

	void NotifyCreated();
	void ISetupNotify(plRefMsg* msg, plRefFlags::Type flags); // Setup notifcations for reference, don't send anything.

	void		AddRef(plKeyImp* key) const;
	UInt16		GetNumRefs() const { return fRefs.GetCount(); }
	plKeyImp*	GetRef(int i) const { return fRefs[i]; }
	void		RemoveRef(plKeyImp *key) const;

	virtual UInt16      GetActiveRefs() const			{ return fNumActiveRefs; }
	virtual UInt16		GetNumNotifyCreated() const		{ return fNotifyCreated.GetCount(); }
	virtual plRefMsg*	GetNotifyCreated(int i) const	{ return fNotifyCreated[i]; }
	virtual const hsBitVector& GetActiveBits() const	{ return fActiveRefs; }

protected:
	void        AddNotifyCreated(plRefMsg* msg, plRefFlags::Type flags);
	void        ClearNotifyCreated();
	UInt16      GetNumNotifyCreated() { return fNotifyCreated.GetCount(); }
	plRefMsg*   GetNotifyCreated(int i) { return fNotifyCreated[i]; }
	void        RemoveNotifyCreated(int i);

	UInt16      IncActiveRefs() { return ++fNumActiveRefs; }
	UInt16      DecActiveRefs() { return fNumActiveRefs ? --fNumActiveRefs : 0; }

	hsBool	IsActiveRef(int i) const			{ return fActiveRefs.IsBitSet(i) != 0; }
	void	SetActiveRef(int i, hsBool on=true)	{ fActiveRefs.SetBit(i, on); }
	hsBool	IsNotified(int i) const				{ return fNotified.IsBitSet(i) != 0; }
	void	SetNotified(int i, hsBool on=true)	{ fNotified.SetBit(i, on); }

	void SatisfyPending(plRefMsg* msg) const;
	void SatisfyPending() const;

	void INotifySelf(hsKeyedObject* ko);
	void INotifyDestroyed();
	void IClearRefs();

	void IRelease(plKeyImp* keyImp);

	hsKeyedObject* fObjectPtr;

	// These fields are the ones actually saved to disk
	plUoid fUoid;
	UInt32 fStartPos;	// where I live in the Datafile  
	UInt32 fDataLen;	// Length in the Datafile

	// Following used by hsResMgr to notify on defered load or when a passive ref is destroyed.
	UInt16						fNumActiveRefs;	// num active refs on me
	hsBitVector					fActiveRefs;	// Which of notify created are active refs
	hsBitVector					fNotified;		// which of notifycreated i've already notified.
	hsTArray<plRefMsg*>			fNotifyCreated;	// people to notify when I'm created or destroyed
	mutable hsTArray<plKeyImp*>	fRefs;			// refs I've made (to be released when I'm unregistered).
	mutable Int16				fPendingRefs;	// Outstanding requests I have out.
	mutable hsTArray<plKeyImp*>	fClones;		// clones of me
	mutable plKey				fCloneOwner;	// pointer for clones back to the owning key
};

#endif // hsRegistry_inc