#include <iostream>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <openpluginlib/pl/openpluginlib.hpp>
#include <openassetlib/al/al.hpp>
#include <openassetlib/al/al_plugin.hpp>
#include <openassetlib/plugins/filesystem_storage_plugin/filesystem_storage_plugin.hpp>

#if defined(_WIN32)
#include <atlbase.h>
#endif

namespace fs = boost::filesystem;
namespace opl = olib::openpluginlib;
namespace oal = olib::openassetlib;	

class local_guid_generator : public oal::guid_generator
{
public:
	const opl::string generate_guid()
	{
#if defined(_WIN32)
		GUID guid;
		if(FAILED(::CoCreateGuid(&guid)))
			throw std::runtime_error("Failed to create GUID");

		char szGuid[40];
		sprintf(szGuid, 
				"{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
				guid.Data1,
				guid.Data2,
				guid.Data3,
				guid.Data4[0],
				guid.Data4[1],
				guid.Data4[2],
				guid.Data4[3],
				guid.Data4[4],
				guid.Data4[5],
				guid.Data4[6],
				guid.Data4[7]);
		return opl::string(szGuid);
#else
		throw std:://runtime_error("Not Implemented yet!!");
#endif
	}
};

namespace
{
	struct filesystem_storage_plugin_query_traits
	{
		opl::wstring libname( ) const
		{ return L"openassetlib"; }
		
		opl::wstring to_match( ) const
		{ return L"filesystem"; }

		opl::wstring type( ) const
		{ return L""; }
		
		int merit( ) const
		{ return 0; }
	};
}

void print_asset_content(oal::al::set_ptr set)
{
	std::cout << "SET CONTENT:" << std::endl;
	if(		(!set)
		||	(set->empty()) )
		std::cout << "No hits!" << std::endl;
	else
	{
		std::cout << "Got " << set->size() << " hits:" << std::endl;
		oal::al::set::const_iterator resultit = set->begin();
		do
		{
			fprintf(stdout, "\tGUID: %s\n", (*resultit)->guid()->string().c_str());
			oal::al::asset::const_iterator attribit = (*resultit)->begin();
			do
			{
				fprintf(stdout, "\t%s: %s", attribit->first.c_str(), attribit->second.c_str());
				if(attribit->first.compare("last_modified") != 0)
					fprintf(stdout, "\n");

			} while(++attribit != (*resultit)->end());
			std::cout << std::endl;

		} while(++resultit != set->end());
	}
	std::cout << std::endl;
}


int main( int argc, char* argv[ ] )
{
	if( argc > 1 )
	{
		if( !opl::init( argv[ 1 ] ) )
		{
			std::cerr << "Failed to init openpluginlib with " << argv[1];
			#if defined (_WIN32)
			_asm int 3;
			#endif
			return 1;
		}

		opl::discovery< filesystem_storage_plugin_query_traits > discoveryResults;  // Note: construction of this object does the query
		std::cout << static_cast<unsigned int>(discoveryResults.size()) << " openassetlib plugins discovered" << std::endl;
		if(!discoveryResults.empty())
		{
			opl::discovery<filesystem_storage_plugin_query_traits>::const_iterator resultsIt = discoveryResults.begin( );
			std::wcout << "libname=" << resultsIt->libname() << ", name=" << resultsIt->name() << ", type=" << resultsIt->type() << ", category=" << resultsIt->category() << ", mime=" << resultsIt->mime() << ", merit=" << resultsIt->merit() << std::endl;
			boost::shared_ptr<oal::al::al_plugin> pPlugin = boost::shared_dynamic_cast<oal::al::al_plugin>(resultsIt->create_plugin(""/*"c:\\media\\"*/));
			if(!pPlugin)
			{
				std::cerr << "Failed to get pointer to plugin!" << std::endl;
				#if defined (_WIN32)
				_asm int 3;
				#endif
				return 1;
			}

			oal::guid_generator_ptr pGUIDGenerator = oal::guid_generator_ptr(new local_guid_generator);
			oal::init(pGUIDGenerator);

#if defined(_WIN32)
			::CoInitialize(0);
#endif
			opl::string query;
			std::cout << "Searching for .png files in c:\\media\\images (non-recursive)" << std::endl;
			opl::string filename_regex = ".*\\.png";
			opl::string search_folder = "c:\\Media\\Images";
			oal::al::set_ptr set1 = oal::al::search(pPlugin, filename_regex, search_folder, false);
			print_asset_content(set1);

			std::cout << "Searching for .png files in c:\\media\\images (recursive)" << std::endl;
			oal::al::set_ptr set2 = oal::al::search(pPlugin, ".*\\.png", "c:\\Media\\Images", true);
			print_asset_content(set2);


			/*
			oal::al::field_equal_to_predicate p(opl::string("filename"), opl::string("seagirl.png"));
			oal::al::set_ptr search_results = oal::al::search(set2, p);
			print_asset_content(search_results);
			*/
			oal::al::key_value_regex_search_predicate p(opl::string("filename"), opl::string("seagirl.png"));
			oal::al::set_ptr search_results = oal::al::search(set2, p);
			print_asset_content(search_results);


			std::cout << "Searching for girl.bmp in c:\\media\\images (recursive)" << std::endl;
			oal::al::set_ptr set3 = oal::al::search(pPlugin, "girl.bmp", "c:\\Media\\Images", true);
			print_asset_content(set3);

			std::cout << "Searching for girl.bmp in c:\\media\\images (non-recursive)" << std::endl;
			oal::al::set_ptr set4 = oal::al::search(pPlugin, "girl.bmp", "c:\\Media\\Images", false);
			print_asset_content(set4);

			std::cout << "Searching for all assets > 10000000 bytes long in c:\\media (non-recursive)" << std::endl;
			oal::al::set_ptr set5 = oal::al::search(pPlugin, ".*", "c:\\media", false, ">", 10000000);
			print_asset_content(set5);

			std::cout << "Searching for all assets modified after 2005/10/1 00:00:00 in c:\\media (non-recursive)" << std::endl;
			std::tm datetime;
			datetime.tm_year	= 2005 - 1900;
			datetime.tm_mon		= 10;
			datetime.tm_mday	= 1;
			datetime.tm_hour	= 0;
			datetime.tm_min		= 0;
			datetime.tm_sec		= 0;
			datetime.tm_isdst	= -1;
			oal::al::set_ptr set6 = oal::al::search(pPlugin, ".*", "c:\\media", false, ">", datetime);
			print_asset_content(set6);

			std::cout << "Intersection of last 2 sets:" << std::endl;
			oal::al::set_ptr set7 = oal::al::calculate_intersection(set5, set6);
			print_asset_content(set7);

			std::cout << "Difference between same 2 sets:" << std::endl;
			oal::al::set_ptr set8 = oal::al::calculate_difference(set5, set6);
			print_asset_content(set8);

			std::cout << "Union of same 2 sets:" << std::endl;
			oal::al::set_ptr set9 = oal::al::calculate_union(set5, set6);
			print_asset_content(set9);

#if defined(_WIN32)
			::CoUninitialize();
#endif

			oal::uninit();
			opl::uninit();
		}
	}
}