#include <memory>
#include <typeindex>
#include <iostream>
#include <string>
#include <vector>
#include <map>

typedef std::multimap<std::type_index, void*> Object;
typedef std::map<Object, std::shared_ptr<void>> ObjectCollection;

Object object;
ObjectCollection objectCollection;

template<typename T, typename... Args>
T* getResource(Args&& ... args)
{
	// Creating tuple from the arguments
	std::tuple<Args...> currentArgs(std::forward<Args>(args)...);
	// Getting object type info
	std::type_index type = { typeid(T) };

	// Getting all objects from the collection that are of the same type
	auto range = object.equal_range(type);

	for (auto it = range.first; it != range.second; ++it)
	{
		// it->second is a void* Since we are iterating through
		// the the collection of the same type I'm trying to cast
		// back. Object construct parameters should be the same 
		// (in this example: const std::string &fileName)
		auto storedArgs = *static_cast<std::tuple<Args...>*>(it->second);

		// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		// Problem is here. currentArgs and storedArgs are always equal :/
		// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
		// Return the object from the collection if current arguments and
		// arguments from the collection are the same
		if (currentArgs == storedArgs)
		{
			std::cout << "Found... returning..." << std::endl;
			// found... return...
			return static_cast<T*>(objectCollection[object].get());
		}
	}

	// Object with the same arguments were not found
	// Adding to collection and return
	std::cout << "Adding to collection..." << std::endl;
	object.emplace(type, &currentArgs);
	objectCollection[object] = std::make_shared<T>(std::forward<Args>(args)...);
	return static_cast<T*>(objectCollection[object].get());
}

class Resource
{
public:
	virtual ~Resource() = default;

	template<typename T, typename... Args>
	static T* get(Args&& ... args)
	{
		return getResource<T>(std::forward<Args>(args)...);
	}
};

class Image
{
public:
	Image(const std::string &fileName)
	{
		std::cout << "Loading image " << fileName.c_str() << std::endl;
	}

	~Image(){};
};

int main()
{
	auto image1 = Resource::get<Image>("aaa.jpg");
	auto image2 = Resource::get<Image>("bbb.jpg");
	auto image3 = Resource::get<Image>("aaa.jpg");
	//getchar();
}