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

#include "hsResMgr.h"
#include <set>
#include <map>
#include <vector>

class plRegistryPageNode;
class plRegistryKeyIterator;
class plRegistryPageIterator;
class plRegistryDataStream;
class plResAgeHolder;
class plResManagerHelper;
class plDispatch;

// plProgressProc is a proc called every time an object loads, to keep a progress bar for
// loading ages up-to-date.
typedef	void(*plProgressProc)(plKey key);

class plResManager : public hsResMgr
{
public:
	plResManager();
	virtual ~plResManager();

	// If the ResManager has already been initialized, you should call Reset after setting this
	void SetDataPath(const char* path) { fDataPath = path; }

	// Mainly for external tools.
	void				AddSinglePage(const char* path);
	plRegistryPageNode* FindSinglePage(const char* path) const;
	void				RemoveSinglePage(const char* path);

	//---------------------------
	//  Load and Unload
	//---------------------------
	virtual void		Load  (const plKey& objKey);		// places on list to be loaded
	virtual hsBool		Unload(const plKey& objKey);		// Unregisters (deletes) an object, Return true if successful
	virtual plKey		CloneKey(const plKey& objKey);

	//---------------------------
	//  Finding Functions
	//---------------------------
	plKey				FindOriginalKey(const plUoid&);
	virtual plKey		FindKey(const plUoid&); // Same as above, but will check the uoid for clones
	const plLocation&	FindLocation(const char* age, const char* page) const;
	// Use nil for any strings you don't need
	const void			GetLocationStrings(const plLocation& loc, char* ageBuffer, char* pageBuffer) const;

	//---------------------------
	//  Establish reference linkage	
	//---------------------------
	virtual hsBool AddViaNotify(const plKey& key, plRefMsg* msg, plRefFlags::Type flags);
	virtual hsBool AddViaNotify(plRefMsg* msg, plRefFlags::Type flags); // msg->fRef->GetKey() == sentKey

	virtual hsBool SendRef(const plKey& key, plRefMsg* refMsg, plRefFlags::Type flags);
	virtual hsBool SendRef(hsKeyedObject* ko, plRefMsg* refMsg, plRefFlags::Type flags);

	//---------------------------
	//  Reding and Writing keys
	//---------------------------
	// Read a Key in, and Notify me when the Object is loaded
	virtual plKey ReadKeyNotifyMe(hsStream* stream, plRefMsg* retMsg, plRefFlags::Type flags); 
	// Just read the Key data in and find a match in the registry and return it.
	virtual plKey ReadKey(hsStream* stream); 

	// For convenience you can write a key using the KeyedObject or the Key...same result
	virtual void WriteKey(hsStream* s, hsKeyedObject* obj); 
	virtual void WriteKey(hsStream* s, const plKey& key); 

	//---------------------------
	//  Reding and Writing Objects directly
	//---------------------------
	virtual plCreatable*	ReadCreatable(hsStream* s);
	virtual void			WriteCreatable(hsStream* s, plCreatable* cre);

	virtual plCreatable*	ReadCreatableVersion(hsStream* s);
	virtual void			WriteCreatableVersion(hsStream* s, plCreatable* cre);

	//---------------------------
	// Registry Modification Functions
	//---------------------------
	virtual plKey NewKey(const char* name, hsKeyedObject* object, const plLocation& loc, const plLoadMask& m = plLoadMask::kAlways);
	virtual plKey NewKey(plUoid& newUoid, hsKeyedObject* object);

	virtual plDispatchBase* Dispatch();

	virtual void SetProgressBarProc(plProgressProc proc);

	//---------------------------
	//  Load optimizations
	//---------------------------
	void LoadAgeKeys(const char* age);
	void DropAgeKeys(const char* age);
	void PageInRoom(const plLocation& page, UInt16 objClassToRef, plRefMsg* refMsg);
	void PageInAge(const char* age);

	// Usually, a page file is kept open during load because the first keyed object
	// read causes all the other objects to be read before it returns.  In some
	// cases though (mostly just the texture file), this doesn't work.  In that
	// case, we just want to force it to stay open until we're done reading the age.
	void KeepPageOpen(const plLocation& page, hsBool keepOpen);

	// We're on the way down, act accordingly.
	virtual void BeginShutdown();

	// Determines whether the time to read each object is dumped to a log
	void LogReadTimes(hsBool logReadTimes);

	// All keys version
	hsBool IterateKeys(plRegistryKeyIterator* iterator);	
	// Single page version
	hsBool IterateKeys(plRegistryKeyIterator* iterator, const plLocation& pageToRestrictTo);
	// Iterate through loaded pages
	hsBool IteratePages(plRegistryPageIterator* iterator, const char* ageToRestrictTo = nil);
	// Iterate through ALL pages, loaded or not
	hsBool IterateAllPages(plRegistryPageIterator* iterator);

	// Helpers for key iterators
	void LoadPageKeys(plRegistryPageNode* pageNode);
	void UnloadPageObjects(plRegistryPageNode* pageNode, UInt16 classIndexHint);
	void DumpUnusedKeys(plRegistryPageNode* page) const;
	plRegistryPageNode* FindPage(const plLocation& location) const;
	plRegistryPageNode* FindPage(const char* age, const char* page) const;

	// Runs through all the pages and verifies that the data versions are good
	hsBool VerifyPages();

protected:
	friend class hsKeyedObject;
	friend class plKeyImp;
	friend class plResManagerHelper;

	virtual plKey	ReRegister(const char* nm, const plUoid& uoid);
	virtual hsBool	ReadObject(plKeyImp* key); // plKeys call this when needed
	virtual hsBool	IReadObject(plKeyImp* pKey, hsStream *stream);  

	plCreatable*	IReadCreatable(hsStream* s) const;
	plKey			ICloneKey(const plUoid& objUoid, UInt32 playerID, UInt32 cloneID);

	virtual void	IKeyReffed(plKeyImp* key);
	virtual void	IKeyUnreffed(plKeyImp* key);

	virtual hsBool	IReset();	
	virtual hsBool	IInit();
	virtual void	IShutdown();

	void	IPageOutSceneNodes(hsBool forceAll);
	void	IDropAllAgeKeys();

	hsKeyedObject* IGetSharedObject(plKeyImp* pKey);

	void IUnloadPageKeys(plRegistryPageNode* pageNode, hsBool dontClear = false);

	hsBool IDeleteBadPages(hsTArray<plRegistryPageNode*>& invalidPages, hsBool conflictingSeqNums);
	hsBool IWarnNewerPages(hsTArray<plRegistryPageNode*>& newerPages);

	void ILockPages();
	void IUnlockPages();

	void AddPage(plRegistryPageNode* page);

	// Adds a key to the registry. Assumes uoid already set
	void AddKey(plKeyImp* key);

	plRegistryPageNode* CreatePage(const plLocation& location, const char* age, const char* page);

	hsBool			fInited;
	UInt16			fPageOutHint;

	// True if we're reading in an object. We only read one object at a time
	hsBool			fReadingObject;
	std::vector<plKey> fQueuedReads;

	std::string fDataPath;

	plDispatch*		fDispatch;

	UInt32 fCurCloneID;		// Current clone ID.  If it isn't zero, we're cloning
	UInt32 fCurClonePlayerID;
	UInt32 fCloningCounter;	// Next clone ID to use.

	typedef std::map<std::string,plResAgeHolder*>	HeldAgeKeyMap;
	HeldAgeKeyMap	fHeldAgeKeys;
	plProgressProc	fProgressProc;

	plResManagerHelper	*fMyHelper;

	hsBool fLogReadTimes;

	UInt8 fPageListLock;	// Number of locks on the page lists.  If it's greater than zero, they can't be modified
	hsBool fPagesNeedCleanup;	// True if something modified the page lists while they were locked.

	typedef std::set<plRegistryPageNode*> PageSet;
	PageSet fAllPages;		// All the pages, loaded or not
	PageSet fLoadedPages;	// Just the loaded pages

	mutable plRegistryPageNode* fLastFoundPage;
};

#endif // plResManager_h_inc