#ifndef FILE_LOADER_H
#define FILE_LOADER_H

#include <string>
#pragma warning(push)
#pragma warning(disable:4181)
#include <hash_map>
#pragma warning(pop)

namespace cbk
{
	/*FileLoader 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 load function 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 FileLoader 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.
	*/
	template<class itemType, class paramType, class nameType = std::string, bool unloadAutomatically = true>
	class FileLoader
	{
	private:
		template<class storageType, class paramType, class nameType = std::string>
		class _Storage
		{
		public:
			_Storage& operator++()
			{
				usage++;
				return *this;
			}

			_Storage& operator--()
			{
				usage--;
				return *this;
			}

			_Storage& operator++(int)
			{
				usage++;
				return *this;
			}

			_Storage& operator--(int)
			{
				usage--;
				return *this;			
			}

			storageType& operator*(void)
			{
				return item;
			}

			storageType* operator->(void)
			{
				return (&item);
			}

			_Storage(const nameType& name, paramType& param) : item(name, param)
			{
				usage = 0;
			}

			~_Storage()
			{
			}

			operator int()
			{
				return usage;
			}

			operator storageType&()
			{
				return item;
			}

		private:
			_Storage(_Storage& other);
			storageType item;
			int usage;
		};
		typedef _Storage<itemType, paramType, nameType> storageClass;

	public:
		FileLoader(void)
		{
		}

		~FileLoader(void)
		{
			for (stdext::hash_map<std::string, storageClass*>::iterator x = _items.begin(); x != _items.end(); x++)
			{
				delete (x->second);
			}
		}

		itemType& load(const nameType& name, paramType& param)
		{
			stdext::hash_map<nameType, storageClass*>::iterator found = _items.find(name);
			if (found != _items.end())
			{
				(*found->second)++;
				return **(found->second);
			} //else, but the other condition returns

			storageClass *newItem = new storageClass(name, param);
			(*newItem)++;
			itemStoragePair tItem(name, newItem);
			_items.insert(tItem);

			return (**newItem);
		}

		inline void unload(const itemType& object)
		{
			unload(object.getName());
		}

		void unload(const nameType& name)
		{
			stdext::hash_map<std::string, storageClass*>::iterator found = _items.find(name);
			if (found == _items.end())
			{
				return;
			} //else, but the other condition returns

			storageClass& tItem = *(found->second);
			tItem--;

			if (unloadAutomatically)
				if (tItem == 0)
				{
					delete (found->second);
					found->second = 0;
					_items.erase(found);
				}
		}

	protected:

	private:
		typedef stdext::hash_map<nameType, storageClass*> itemStorage;
		typedef std::pair<nameType, storageClass*> itemStoragePair;
		itemStorage _items;
	};
}

#endif //FILE_LOADER_H