/*
Author:		Chris Kaynor (chris@kaynor.net)
Edited By:		

Description:		A general purpose factory. Calling the load mutiple times with the same name will return
			instances to the same object.
			
Usage:		Create an instance of the factory object. For template parameters, It is recommended
			that itemType be derived from factory::Item. Use the load function to create instances of
			itemType, making sure that all loads are matched by a call to unload. deleteItem should only
			be called if removeAutomatically is set to false, and then only if getUsage returns zero.
			
Requirements:	hash_map as defined as part of the extented standard template libary as specified by Mircosoft.
			string as defined in the standard template libary.
			NoCopy as defined in kLibrary's common section.
			_Storage as defined in kLibrary's factory section.
			Item as defined in kLibrary's item section.
			
Last Edited:	May 20th, 2008
*/
/*
Copyright 2008 Chris Kaynor

This file is part of kLibrary.

kLibrary is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or(at your option) any later version.

kLibrary 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License.
*/

#ifndef KLIBRARY_FACTORY_FACTORY_H
#define KLIBRARY_FACTORY_FACTORY_H

#include <string>

//The hash_map raises some warnings we do not care about, but do not want to clog the error outputs.
#pragma warning(push)
#pragma warning(disable:4181)
#include <hash_map>
#pragma warning(pop)

#include "factory_storage.h"
#include "factory_item.h"
#include "..\Common\NoCopy.h"

namespace kLibrary
{
	/*
	Factory includes various functionality to load and unload files of various types, while maintaining only one copy in memory.
	
	itemType should be a type which implements a load function which takes a file name as a std::string& which loads the file
	in, dealing with any errors which might occur.
	
	Calls to loadFile will call itemType's constructor if, and only if,the file has not already been loaded.
	
	If unloadAutomatically is true, the class will automatically delete and clean-up the loaded file when the last instance is
	unloaded. In	any case, when the Factory object is unloaded, it will unload all files loaded by deleting their pointers.
	
	As such, itemType should also include a deconstructor to unload the data it loads.
	
	NOTE: The class does not differincate between paremeters in determing if the object is already loaded. Using the same
	name with different parameters will return the same object, loaded with the first parameter passed in. If a need comes
	up in which different parameters must load different objects, either use differing factories or modify the name as well
	as the parameter.
	*/
	template<class itemType, class paramType, class nameType = std::string, bool removeAutomatically = true>
	class Factory : NoCopy
	{
	public:
		//Define a type for later use in the class of the storage type. Used to improve readability and reduce code duplication with the templates.
		typedef factory::_Storage<itemType, paramType, nameType> _storageClass;

		//Define the type of the hash_map used to hold the active instances loaded.
		typedef stdext::hash_map<nameType, _storageClass*> _itemStorage;

		//Define the type of the data storage pair, consisting of the item storage pointer and the name.
		typedef std::pair<nameType, _storageClass*> _itemStoragePair;

		//Create an instance of the factory.
		inline Factory(void);
		
		//Clean-up the factory, Automatically invalidates all items loaded by the class.
		inline ~Factory(void);
		
		/*
		Loads a new item. Name should specify a name understood by the load function of the itemType class.
		All calls to load should be matched by a call to unload. If a file is not unloaded after being loaded, it will
		retain its memory usage. All instances loaded by the Factory class will be cleaned up when the Factory is
		destructed.
		*/
		itemType* load(const nameType& name, paramType param);
		
		/*
		Unloads an item loaded by the factory. If the item is not currently loaded, it will just return with no error.
		If all instances of the class have been unloaded and removeAutomatically is true, unload will preceed to
		clean-up and unload the item from memory by deleting the pointer to the object.
		The variations should be choosen automatically, as files may be unloaded either by name or by a pointer to an
		instance. If an instance pointer is passed in, the getName function of the object will be called.
		*/
		inline void unload(const itemType* object);
		void unload(const nameType& name);
		
		/*
		Returns the number of active pointers to the object specified by name or by instance. If removeAutomatically
		is true, the object will be automatically unloaded when the usage reaches zero by the unload function. Otherwise
		this function must be used to check to see if the item may be unloaded.
		The variations should be choosen automatically, as files may be unloaded either by name or by a pointer to an
		instance. If an instance pointer is passed in, the getName function of the object will be called.
		*/
		inline int getUsage(const itemType* object);
		int getUsage(const nameType& name);

		/*
		deleteItem should only ever be called if removeAutomatically is false, and then only when there are no open
		instances of the object. The function does not check to see if the item	is still in use before deleting, and the
		getUsage function should be called to verify that the object is no longer needed. Failure to do so may result
		in invalid pointers.
		The variations should be choosen automatically, as files may be unloaded either by name or by a pointer to an
		instance. If an instance pointer is passed in, the getName function of the object will be called.
		*/
		inline void deleteItem(const itemType* object);
		void deleteItem(const nameType& name);
		
		/*
		WARNING: Renaming an item may result in problems if the new name is already in use. The factory will also 
		no longer reconigze the old name.
		Renames the specified item in the hash table. This function should be called anytime the name of an object is
		changed, such as if the itemType is reloaded as a new file.
		The variations should be choosen automatically, as files may be unloaded either by name or by a pointer to an
		instance. If an instance pointer is passed in, the getName function of the object will be called.
		*/
		inline void renameItem(const itemType* object, const nameType& newName);
		void renameItem(const nameType& name, const nameType& newName);

		/*
		WARNING: The following functions give nearly direct access into the hash tables used to store the files. It
		is not recommended that they be used unless all objects which were loaded with the factory intend to be
		reloaded.
		*/
		typename _itemStorage::iterator getBegin(void);
		typename _itemStorage::iterator getEnd(void);
	protected:

	private:	
		//Declare the specification to hold the items.
		_itemStorage _items;
	};
}

#include "factory_code.h"

#endif //KLIBRARY_FACTORY_FACTORY_H