// =================================================================================================
// Copyright Adobe
// Copyright 2011 Adobe
// All Rights Reserved
//
// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
// of the Adobe license agreement accompanying it. 
// =================================================================================================

#ifndef _HANDLERREGISTRY_h_
#define _HANDLERREGISTRY_h_

#include "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.

#include "public/include/XMP_Const.h"
#include "public/include/XMP_IO.hpp"

#include "XMPFiles/source/XMPFiles_Impl.hpp"
#include "source/XMPFiles_IO.hpp"

#include "XMPFiles/source/FormatSupport/IFF/IChunkBehavior.h"
#include "XMPFiles/source/FormatSupport/IFF/ChunkPath.h"
#include "source/Endian.h"

namespace Common
{

/**
	File handler data
 */
struct XMPFileHandlerInfo 
{
	XMP_FileFormat		format;
	XMP_OptionBits		flags;
	void*				checkProc;
	XMPFileHandlerCTor	handlerCTor;

	XMPFileHandlerInfo() : format(0), flags(0), checkProc(0), handlerCTor(0) 
	{};
	
	XMPFileHandlerInfo( XMP_FileFormat _format, 
						XMP_OptionBits _flags,
						CheckFileFormatProc _checkProc, 
						XMPFileHandlerCTor _handlerCTor )
		: format(_format), flags(_flags), checkProc((void*)_checkProc), handlerCTor(_handlerCTor) 
	{};
	
	XMPFileHandlerInfo( XMP_FileFormat _format, 
						XMP_OptionBits _flags,
						CheckFolderFormatProc _checkProc, 
						XMPFileHandlerCTor _handlerCTor )
		: format(_format), flags(_flags), checkProc((void*)_checkProc), handlerCTor(_handlerCTor) 
	{};
};

/**
	The singleton class HandlerRegistry is responsible to manage all file handler.
	It registers file handlers during initialization time and provides functionality
	to select a file handler based on a given file format.
*/

class HandlerRegistry
{
public:
	static HandlerRegistry&		getInstance();
	static void					terminate();

#if EnableDynamicMediaHandlers
	static XMP_FileFormat		checkTopFolderName( const std::string & rootPath );
	static XMP_FileFormat		checkParentFolderNames( const std::string& rootPath,
														const std::string& gpName,
														const std::string& parentName, 
														const std::string& leafName );
#endif

public:
	/**
	 * Register all file handler
	 */
	void				initialize();

	/**
	 * Register a single folder based file handler.
	 *
	 * @param format			File format identifier
	 * @param flags				Flags
	 * @param checkProc			Check format function pointer
	 * @param handlerCTor		Factory function pointer
	 * @param replaceExisting	Replace an already existing handler
	 */
	bool				registerFolderHandler( XMP_FileFormat			format,
											   XMP_OptionBits			flags,
											   CheckFolderFormatProc	checkProc,
											   XMPFileHandlerCTor		handlerCTor,
											   bool						replaceExisting = false );

	/**
	 * Register a single normal file handler.
	 *
	 * @param format			File format identifier
	 * @param flags				Flags
	 * @param checkProc			Check format function pointer
	 * @param handlerCTor		Factory function pointer
	 * @param replaceExisting	Replace an already existing handler
	 */
	bool				registerNormalHandler( XMP_FileFormat			format,
											   XMP_OptionBits			flags,
											   CheckFileFormatProc		checkProc,
											   XMPFileHandlerCTor		handlerCTor,
											   bool						replaceExisting = false );

	/**
	 * Register a single owning file handler.
	 *
	 * @param format			File format identifier
	 * @param flags				Flags
	 * @param checkProc			Check format function pointer
	 * @param handlerCTor		Factory function pointer
	 * @param replaceExisting	Replace an already existing handler
	 */
	bool				registerOwningHandler( XMP_FileFormat			format,
											   XMP_OptionBits			flags,
											   CheckFileFormatProc		checkProc,
											   XMPFileHandlerCTor		handlerCTor,
											   bool						replaceExisting = false );

	// Remove a handler. Does nothing if no such handler exists.
	void removeHandler ( XMP_FileFormat format );
	
	/**
	 * Get file format identifier for filename extension.
	 *
	 * @param fileExt			Filename extension
	 * @param addIfNotFound		If true if handler doesn't exists then add now
	 */
	XMP_FileFormat		getFileFormat( const std::string & fileExt, bool addIfNotFound = false );

	/**
	 * Get handler flags for file format.
	 *
	 * @param format	File format identifier
	 * @param flags		Return handler flag
	 * @return			True on success
	 */
	bool				getFormatInfo( XMP_FileFormat format, XMP_OptionBits* flags = NULL );

	/**
	 * Get handler information for passed format. 
	 * The returned file handler is the default handler. I.e. that handler
	 * that is used when called from outside using the XMPFiles API.
	 *
	 * @param format	File format identifier
	 * @return			Return handler info if available, otherwise NULL
	 */
	XMPFileHandlerInfo*	getHandlerInfo( XMP_FileFormat format );

	/**
	 * Get file handler information of the standard file handler for the
	 * file format identifier.
	 * If there is a replacement for this format then the standard handler
	 * is the replaced handler. Otherwise the standard handler and the 
	 * default handler are the same.
	 *
	 * @param format	File format identifier
	 * @return			Return handler info if available, otherwise NULL
	 */
	XMPFileHandlerInfo*	getStandardHandlerInfo( XMP_FileFormat format );

	/**
	 * Return true if there is a replacement for the file format
	 */
	bool isReplaced( XMP_FileFormat format );

	/**
	 * Select file handler based on passed information and setup XMPFiles instance with related data.
	 *
	 * @param session		XMPFiles instance
	 * @param clientPath	Path to file
	 * @param format		File format identifier
	 * @param openFlags		Flags
	 * @return				File handler structure
	 */
	XMPFileHandlerInfo*	selectSmartHandler( XMPFiles* session, XMP_StringPtr clientPath, XMP_FileFormat format, XMP_OptionBits openFlags );

private:
	/**
	 * Return default file handler for file format identifier or filename extension
	 *
	 * @param format	File format identifier
	 * @param fileExt	Filename extension
	 * @return			File handler structure for passed format/extension
	 */
	XMPFileHandlerInfo* pickDefaultHandler ( XMP_FileFormat format, const std::string & fileExt );

#if EnableDynamicMediaHandlers
	/**
	 * Try to find folder based file handler.
	 *
	 * @param format		File format identifier
	 * @param rootPath		Path to root folder
	 * @param gpName		Grand parent folder name
	 * @param parentName	Parent folder name
	 * @param leafName		File name
	 * @param parentObj		XMPFiles instance
	 * @return				File handler structure
	 */
	XMPFileHandlerInfo* tryFolderHandlers( XMP_FileFormat format,
										   const std::string& rootPath,
										   const std::string& gpName,
										   const std::string& parentName,
										   const std::string& leafName,
										   XMPFiles* parentObj );
#endif

private:
	/**
	 * ctor/dtor
	 */
	 HandlerRegistry();
	~HandlerRegistry();

private:
	typedef std::map <XMP_FileFormat, XMPFileHandlerInfo>	XMPFileHandlerTable;
	typedef XMPFileHandlerTable::iterator					XMPFileHandlerTablePos;
	typedef std::pair <XMP_FileFormat, XMPFileHandlerInfo>	XMPFileHandlerTablePair;

	XMPFileHandlerTable*	mFolderHandlers;	// The directory-oriented handlers.
	XMPFileHandlerTable*	mNormalHandlers;	// The normal file-oriented handlers.
	XMPFileHandlerTable*	mOwningHandlers;	// The file-oriented handlers that "own" the file.

	XMPFileHandlerTable*	mReplacedHandlers;	// All file handler that where replaced by a later one

	static HandlerRegistry*	sInstance;			// singleton instance
};

} // Common

#endif	// _HANDLERREGISTRY_h_
