/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2012 Torus Knot Software Ltd

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#ifndef __Ogre_PagedWorld_H__
#define __Ogre_PagedWorld_H__

#include "OgrePagingPrerequisites.h"
#include "OgreString.h"
#include "OgreDataStream.h"
#include "OgreCommon.h"

namespace Ogre
{
	class PageManager;

	/** \addtogroup Optional Components
	*  @{
	*/
	/** \addtogroup Paging
	*  Some details on paging component
	*  @{
	*/


	/** This class represents a collection of pages which make up a world. 
	@remarks
		It's important to bear in mind that the PagedWorld only delineates the
		world and knows how to find out about the contents of it. It does not, 
		by design, contain all elements of the world, in memory, at once. 
	*/
	class _OgrePagingExport PagedWorld : public PageAlloc
	{
	protected:
		String mName;
		PageManager* mManager;
		PageProvider* mPageProvider;

	public:
		static const uint32 CHUNK_ID;
		static const uint16 CHUNK_VERSION;
		static const uint32 CHUNK_SECTIONDECLARATION_ID;
		/** Constructor.
		@param name The name of the world, which must be enough to identify the 
			place where data for it can be loaded from (doesn't have to be a filename
			necessarily).
		@param manager The PageManager that is in charge of providing this world with
			services such as related object factories.
		*/
		PagedWorld(const String& name, PageManager* manager);
		virtual ~PagedWorld();

		const String& getName() const { return mName; }
		/// Get the manager of this world
		PageManager* getManager() const { return mManager; }

		/// Load world data from a file
		void load(const String& filename);
		/// Load world data from a stream
		void load(const DataStreamPtr& stream);
		/// Load world data from a serialiser (returns true if successful)
		bool load(StreamSerialiser& stream);
		/** Save world data to a file
		@param filename The name of the file to create; this can either be an 
			absolute filename or 
		*/
		void save(const String& filename);
		/// Save world data to a stream
		void save(const DataStreamPtr& stream);
		/// Save world data to a serialiser
		void save(StreamSerialiser& stream);

		/** Create a new section of the world based on a specialised type.
		@remarks
			World sections are areas of the world that use a particular
			PageStrategy, with a certain set of parameters specific to that
			strategy, and potentially some other rules. 
			So you would have more than one section in a world only 
			if you needed different simultaneous paging strategies, or you 
			wanted the same strategy but parameterised differently.
		@param sceneMgr The SceneManager to use for this section. 
		@param typeName The type of section to use (must be registered	
			with PageManager), or blank to use the default type (simple grid)
		@param sectionName An optional name to give the section (if none is
			provided, one will be generated)
		*/
		PagedWorldSection* createSection(SceneManager* sceneMgr,
			const String& typeName,
			const String& sectionName = StringUtil::BLANK);


		/** Create a new manually defined section of the world.
		@remarks
			World sections are areas of the world that use a particular
			PageStrategy, with a certain set of parameters specific to that
			strategy. So you would have more than one section in a world only 
			if you needed different simultaneous paging strategies, or you 
			wanted the same strategy but parameterised differently.
		@param strategyName The name of the strategy to use (must be registered	
			with PageManager)
		@param sceneMgr The SceneManager to use for this section
		@param sectionName An optional name to give the section (if none is
			provided, one will be generated)
		*/
		PagedWorldSection* createSection(const String& strategyName, SceneManager* sceneMgr,
			const String& sectionName = StringUtil::BLANK);


		/** Create a manually defined new section of the world.
		@remarks
			World sections are areas of the world that use a particular
			PageStrategy, with a certain set of parameters specific to that
			strategy. So you would have more than one section in a world only 
			if you needed different simultaneous paging strategies, or you 
			wanted the same strategy but parameterised differently.
		@param strategy The strategy to use 
		@param sceneMgr The SceneManager to use for this section
		@param sectionName An optional name to give the section (if none is
			provided, one will be generated)
		*/
		PagedWorldSection* createSection(PageStrategy* strategy, SceneManager* sceneMgr, 
			const String& sectionName = StringUtil::BLANK);
		/** Destroy a section of world. */
		void destroySection(const String& name);
		/** Destroy a section of world. */
		void destroySection(PagedWorldSection* sec);
		/** Destroy all world sections */
		void destroyAllSections();

		/** Get the number of sections this world has. */
		size_t getSectionCount() const { return mSections.size(); }

		/** Retrieve a section of the world. */
		PagedWorldSection* getSection(const String& name);

		typedef map<String, PagedWorldSection*>::type SectionMap;
		/// Retrieve a const reference to all the sections in this world
		const SectionMap& getSections() const { return mSections; }

		/** Set the PageProvider which can provide streams for Pages in this world. 
		@remarks
			This is the top-level way that you can direct how Page data is loaded. 
			When data for a Page is requested for a PagedWorldSection, the following
			sequence of classes will be checked to see if they have a provider willing
			to supply the stream: PagedWorldSection, PagedWorld, PageManager.
			If none of these do, then the default behaviour is to look for a file
			called worldname_sectionname_pageID.page. 
		@note
			The caller remains responsible for the destruction of the provider.
		*/
		void setPageProvider(PageProvider* provider) { mPageProvider = provider; }
		
		/** Get the PageProvider which can provide streams for Pages in this world. */
		PageProvider* getPageProvider() const { return mPageProvider; }

		/** Give a world  the opportunity to prepare page content procedurally. 
		@remarks
		You should not call this method directly. This call may well happen in 
		a separate thread so it should not access GPU resources, use _loadProceduralPage
		for that
		@return true if the page was populated, false otherwise
		*/
		virtual bool _prepareProceduralPage(Page* page, PagedWorldSection* section);
		/** Give a world  the opportunity to prepare page content procedurally. 
		@remarks
		You should not call this method directly. This call will happen in 
		the main render thread so it can access GPU resources. Use _prepareProceduralPage
		for background preparation.
		@return true if the page was populated, false otherwise
		*/
		virtual bool _loadProceduralPage(Page* page, PagedWorldSection* section);
		/** Give a world  the opportunity to unload page content procedurally. 
		@remarks
		You should not call this method directly. This call will happen in 
		the main render thread so it can access GPU resources. Use _unprepareProceduralPage
		for background preparation.
		@return true if the page was populated, false otherwise
		*/
		virtual bool _unloadProceduralPage(Page* page, PagedWorldSection* section);
		/** Give a world  the opportunity to unprepare page content procedurally. 
		@remarks
		You should not call this method directly. This call may well happen in 
		a separate thread so it should not access GPU resources, use _unloadProceduralPage
		for that
		@return true if the page was unpopulated, false otherwise
		*/
		virtual bool _unprepareProceduralPage(Page* page, PagedWorldSection* section);
		/** Get a serialiser set up to read Page data for the given PageID. 
		@param pageID The ID of the page being requested
		@param section The parent section to which this page will belong
		@remarks
		The StreamSerialiser returned is the responsibility of the caller to
		delete. 
		*/
		StreamSerialiser* _readPageStream(PageID pageID, PagedWorldSection* section);

		/** Get a serialiser set up to read Page data for the given PageID. 
		@param pageID The ID of the page being requested
		@param section The parent section to which this page will belong
		@remarks
		The StreamSerialiser returned is the responsibility of the caller to
		delete. 
		*/
		StreamSerialiser* _writePageStream(PageID pageID, PagedWorldSection* section);

		/// Called when the frame starts
		virtual void frameStart(Real timeSinceLastFrame);
		/// Called when the frame ends
		virtual void frameEnd(Real timeElapsed);
		/// Notify a world of the current camera
		virtual void notifyCamera(Camera* cam);

		/** Function for writing to a stream.
		*/
		_OgrePagingExport friend std::ostream& operator <<( std::ostream& o, const PagedWorld& p );


	protected:
		SectionMap mSections;
		NameGenerator mSectionNameGenerator;
	};
	
	/** @} */
	/** @} */

}

#endif
