//-------------------------------------------------------------------------
//
// File:		LoadC.h
// Desc:		Input stream class.
// Author:		memon <memon@inside.org>
//
//-------------------------------------------------------------------------
//	Copyright (c) 2000-2002 Moppi Productions. All Rights Reserved.
//  This file is part of Moppi Demopaja SDK. For conditions of 
//  distribution and use, see the accompanying license.txt file.
//  http://moppi.inside.org/demopaja/
//-------------------------------------------------------------------------

#ifndef __DEMOPAJA_LOADC_H__
#define __DEMOPAJA_LOADC_H__

#define FILEIO_DONT_INCLUDE_STREAMS
#include "PajaTypes.h"
#include "FileIO.h"
#include "FactoryC.h"
#include <stdio.h>
#include <vector>
#include <string>

namespace FileIO {

	//! Demopaja input stream
	/*!	LoadC is used to load the information in Demopaja from a chunk based file format.
		The data in the output stream is serialized in chunk based format.
		The read phase consist of opening a chunk, determining the chunk and the version of the chunk,
		and then reading the data aout of the chunk.

		The close_chunk() method skips from the opened chunk to the next chunk even if the whole chunk
		wasn't read.

		This class is implemented by the system.

		Example of load() method on effect class:
		\code
		uint32
		TestEffectC::load( LoadC* pLoad )
		{
			uint32	ui32Error = IO_OK;

			// Open new chunk for read.
			while( (ui32Error = pLoad->open_chunk()) == IO_OK ) {

				// Determine which chunk to read.
				switch( pLoad->get_chunk_id() ) {
				case CHUNK_TESTEFFECT_BASE:
					if( pLoad->get_chunk_version() == TESTEFFECT_VERSION )
						ui32Error = EffectI::load( pLoad );
					break;
				case CHUNK_TESTEFFECT_TRANSGIZMO:
					if( pLoad->get_chunk_version() == TESTEFFECT_VERSION )
						ui32Error = m_pTransGizmo->load( pLoad );
					break;
				case CHUNK_TESTEFFECT_ATTRIBGIZMO:
					if( pLoad->get_chunk_version() == TESTEFFECT_VERSION )
						ui32Error = m_pAttribGizmo->load( pLoad );
					break;
				default:
					// Report illegal chunks.
					assert( 0 );
				}

				// Goto next chunk
				pLoad->close_chunk();

				// Check for errors
				if( ui32Error != IO_OK && ui32Error != IO_END )
					return ui32Error;
			}

			return ui32Error;
		}
		\endcode

		\see SaveC
	*/
	class LoadC
	{
	public:
		//! Default constructor.
		LoadC( PluginClass::FactoryC* pFactory );
		//! Default destructor.
		virtual ~LoadC();

		//! Opens input stream.
		/*!	See FileIOErrorsE for more information on the error codes.
			If the signature of the opened file doe not match the given signature
			the error IO_ERROR_FORMAT is returned.

			\param szName NULL terminated name of the input file.
			\param pSignature pointer to array of bytes describing the signature of the file.
			\param length of the signature in bytes.

			\return IO_OK if everything went ok, error otherwise.
		*/
		virtual	PajaTypes::uint32	open( const char* szName, const PajaTypes::int8* pSignature, PajaTypes::int32 i32SignatureSize );
		//! Closes the input stream.
		virtual	PajaTypes::uint32	close();

		//! Opens a chunk for read.
		/*!	The open_chunk method returns IO_OK if opening succeed.
			Since the chunks can be hierarchial (that is, chunks stored
			inside chunks) IO_END is returned if the load() method should
			not continue to read the data.
		*/
		virtual	PajaTypes::uint32	open_chunk();
		//! Returns the ID if the currently open chunk, this value is used to determine different chunks.
		virtual	PajaTypes::uint32	get_chunk_id();
		//! Returns the Version of the currently open chunk.
		virtual	PajaTypes::uint32	get_chunk_version();
		//! Returns the size of the currently open chunk.
		virtual	PajaTypes::uint32	get_chunk_size();
		//! Returns the indentation level of the currently open chunk.
		virtual	PajaTypes::uint32	get_chunk_indent();

		//! Goes to the next chunk in this indentation level.
		virtual	PajaTypes::uint32	peek_next_chunk();

		//! Closes a chunk and goes to the next chunk in the indentation level.
		/*!	close_chunk uses peek_next_chunk to go to the next chunk.
		*/
		virtual	PajaTypes::uint32	close_chunk();

		//! Reads uiSize bytes to the specified buffer from the stream.
		virtual	PajaTypes::uint32	read( void* pBuffer, PajaTypes::uint32 ui32Size );
		//! Reads a string from the stream.
		virtual	PajaTypes::uint32	read_str( char* szStr );

		//! Returns current error.
		virtual	PajaTypes::uint32	get_error();

		//! Adds file handle patch.
		/*!	\param pPointer pointer of the file handle pointer.
			\param ui32HandleId The ID returned by Import::FileHandleC::get_id().

			Via this method a file handle pointer can be set to patched when the data
			is available. The file handle is patched before the initialize method of
			effect class is called. Therefore file handle parameters cannot be
			copied sooner than in the initialize method of the effect class.

			Since the file referencing is done via file parameter, the system takes care
			of all the file handle patching in effect classes. Importer classes have to
			use this file handle patching mechanism.

			Example of writing file handle:
			\code
			Import::FileHandleC*	m_pTextureHandle;
			uint32	ui32ID;
			// Write texture handle.
			if( m_pTextureHandle )
				ui32ID = m_pTextureHandle->get_id();
			else
				ui32ID = 0xffffffff;	// NULL ID.
			ui32Error = pSave->write( &ui32ID, sizeof( ui32ID ) );
			\endcode

			Example of reading file handle saved in previous example:
			\code
			Import::FileHandleC*	m_pTextureHandle;
			uint32	ui32ID = 0xffffffff;
			// Read texture handle.
			ui32Error = pLoad->read( &ui32ID, sizeof( ui32ID) );
			if( ui32ID != 0xffffffff )
				pLoad->add_file_handle_patch( (void**)&m_pTextureHandle, ui32ID );
			\endcode
		*/
		virtual void				add_file_handle_patch( void** pPointer, PajaTypes::uint32 ui32HandleId );
		//! Returns number of file handle patched (used internally).
		virtual PajaTypes::uint32	get_file_handle_patch_count();
		//! Returns pointer to patch (used internally).
		virtual void**				get_file_handle_patch_pointer( PajaTypes::uint32 ui32Index );
		//! Returns ID of the pointer to patch (used internally).
		virtual PajaTypes::uint32	get_file_handle_patch_id( PajaTypes::uint32 ui32Index );

		//! Returns factory class attached to the LoadC.
		virtual PluginClass::FactoryC*	get_factory() const;

		//! Returns number of error messages (used internally).
		virtual PajaTypes::uint32	get_error_message_count();
		//! Returns error message at given index (used internally).
		virtual const char*			get_error_message( PajaTypes::uint32 ui32Index );
		//! Adds new error message.
		/*!	On load the plugin class can report errors via this method.
			Adding a error message does not stop reading the file.
			The errors are reported after the file load is completed.
		*/
		virtual void				add_error_message( const char* szMessage );

		//! Returns the size of the file.
		virtual PajaTypes::uint32	get_file_size() const;
		//! Returns the current read position.
		virtual PajaTypes::uint32	get_read_pos() const;

	private:

		struct ChunkHdrS {
			PajaTypes::uint32	m_ui32ID;
			PajaTypes::uint32	m_ui32Version;
			PajaTypes::uint32	m_ui32Size;
			PajaTypes::uint32	m_ui32Next;
			bool				m_bLastChunk;
		};

		struct FileHandlePatchS {
			PajaTypes::uint32	m_ui32HandleId;
			void**				m_pPointer;
		};

		struct ErrorMessageS {
			std::string			m_sMessage;
		};
		
		FILE*							m_pStream;
		std::vector<ChunkHdrS>			m_rChunkList;
		std::vector<FileHandlePatchS>	m_rFileHandlePatches;
		std::vector<ErrorMessageS>		m_rErrorLog;
		PajaTypes::uint32				m_ui32Indent;
		PajaTypes::uint32				m_ui32Error;
		PajaTypes::uint32				m_ui32FileSize;

		PluginClass::FactoryC*			m_pFactory;
	};

};

#endif // __LOADC_H__