#include <iostream>
#include <boost/filesystem/operations.hpp>
#include <openpluginlib/pl/openpluginlib.hpp>
#include <openassetlib/al/al.hpp>
#include <openassetlib/al/al_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 sqlite3_metadata_plugin_query_traits
	{
		opl::wstring libname( ) const
		{ return L"openassetlib"; }
		
		opl::wstring to_match( ) const
		{ return L"sqlite3"; }

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

int main( int argc, char* argv[ ] )
{
	const char* DATABASE_PATHNAME = "./oal_sqlite3_plugin.db"; // This'll go whereever the unittest project file is located

	// remove whatever database file that might already exist
	//remove(DATABASE_PATHNAME);

	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< sqlite3_metadata_plugin_query_traits > discoveryResults;  // Note: construction of this object does the query
		std::cout << static_cast<unsigned int>(discoveryResults.size()) << " openassetlib plugins discovered\n";
		if(!discoveryResults.empty())
		{
			opl::discovery<sqlite3_metadata_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() << "\n";
			boost::shared_ptr<oal::al::al_plugin> pPlugin = boost::shared_dynamic_cast<oal::al::al_plugin>(resultsIt->create_plugin(DATABASE_PATHNAME));
			if(!pPlugin)
			{
				std::cerr << "Failed to get pointer to plugin!\n";
				#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);

			opl::string		filename("BatmanBegins_1.avi");
			fs::path		location("C:\\Media", fs::native);
			boost::intmax_t	filesize(11501568);

			std::tm datetime;
			datetime.tm_year	= 2005 - 1900;
			datetime.tm_mon		= 7;
			datetime.tm_mday	= 26;
			datetime.tm_hour	= 15;
			datetime.tm_min		= 11;
			datetime.tm_sec		= 23;
			datetime.tm_isdst	= -1;

			const char* KEY_STRING_FILENAME			= "filename";
			const char* KEY_STRING_LOCATION			= "location";
			const char* KEY_STRING_FILESIZE			= "filesize";
			const char* KEY_STRING_LAST_MODIFIED	= "last_modified";

			oal::al::asset_ptr asset = oal::al::asset_factory::instance().create_asset();
			asset->set(KEY_STRING_FILENAME, filename);
			asset->set(KEY_STRING_LOCATION, location.native_file_string());
			asset->set(KEY_STRING_FILESIZE, boost::lexical_cast<opl::string>(filesize));
			//asset->set(KEY_STRING_LAST_MODIFIED, opl::string(::asctime(&datetime)));

//			asset->guid(oal::al::guid_factory::instance().create_guid("{03bcdf8b-380c-4ec7-8904-af10adda75a8}"));

			oal::al::set_ptr store_set = oal::al::set::create();
			store_set->push_back(asset);

			oal::al::set_ptr failed_to_store_set = oal::al::store(pPlugin, store_set);
			if(failed_to_store_set != oal::al::set_ptr())
			{
				std::cout << "The following assets failed to be stored..." << std::endl;
				for(oal::al::set::const_iterator cit = failed_to_store_set->begin(); cit != failed_to_store_set->end(); cit++)
				{
					std::cout << (*cit)->guid()->string().c_str() << std::endl;
				}
			}

			oal::al::asset_ptr asset_to_restore = oal::al::asset_factory::instance().create_asset(oal::al::guid_factory::instance().create_guid("{03bcdf8b-380c-4ec7-8904-af10adda75a8}"));
			oal::al::set_ptr set_to_restore = oal::al::set::create(asset_to_restore);
			oal::al::set_ptr restored_set = oal::al::restore(pPlugin, set_to_restore);
			restored_set->name("jahreel");

			if(restored_set != oal::al::set_ptr())
			{
				std::cout << "The following assets were successfully restored..." << std::endl;
				for(oal::al::set::const_iterator cit = restored_set->begin(); cit != restored_set->end(); cit++)
				{
					std::cout << (*cit)->guid()->string().c_str() << std::endl;
					for(oal::al::asset::const_iterator cit2 = (*cit)->begin(); cit2 != (*cit)->end(); cit2++)
					{
						std::cout << cit2->first.c_str() << ",\t" << cit2->second.c_str() << std::endl;
					}
				}
			}

			if(!save_set(pPlugin, "./jahreel1.xml", restored_set))
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}

			oal::al::set_ptr loaded_set = load_set(pPlugin, "./jahreel1.xml");
			if(loaded_set == oal::al::set_ptr())
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}

			std::cout << "loaded set name: \"" << loaded_set->name().c_str() << "\"" << std::endl;

			unsigned char buffer[256];
			for(int idx = 0; idx < 256; idx++)
			{
				buffer[idx] = static_cast<char>(idx);
			}

			oal::al::asset_ptr thumbnail_asset = oal::al::asset_factory::instance().create_asset();
			thumbnail_asset->set(KEY_STRING_LOCATION, opl::string(".\\jahlogo.png"));
			//thumbnail_asset->set(KEY_STRING_FILENAME, opl::string("jahlogo.png"));
			//thumbnail_asset->set(KEY_STRING_LOCATION, opl::string("C:\\Media\\images"));
			//thumbnail_asset->set(KEY_STRING_FILENAME, opl::string("i'm so bored.bmp"));
			//thumbnail_asset->set(KEY_STRING_LOCATION, opl::string("C:\\Media"));
			oil::il::image_type_ptr thumbnail_image = oal::al::convert_to_image(thumbnail_asset);
			memset((void*)thumbnail_image->data(0, false), NULL, thumbnail_image->size());
			memcpy((void*)thumbnail_image->data(0, false), (const void*)buffer, 256);
			if(!oal::al::store_image(thumbnail_asset, thumbnail_image, "thumbnail"))
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}
			store_set = oal::al::set::create(thumbnail_asset);
			failed_to_store_set = oal::al::store(pPlugin, store_set);
			if(failed_to_store_set != oal::al::set_ptr())
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}
			set_to_restore = oal::al::set::create(oal::al::asset_factory::instance().create_asset(thumbnail_asset->guid()));
			restored_set = oal::al::restore(pPlugin, set_to_restore);
			if(restored_set == oal::al::set_ptr())
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}
			//oil::il::image_type_ptr restored_thumbnail_image = oal::al::restore_image(asset, "thumbnail");
			oil::il::image_type_ptr restored_thumbnail_image = oal::al::restore_image(restored_set->at(0), "thumbnail");
			if(restored_thumbnail_image == oil::il::image_type_ptr())
			{
				#if defined (_WIN32)
				_asm int 3;
				#endif
			}
			else
			{
				fprintf(stdout, "thumbnail_image size = %d\n", thumbnail_image->size());
				fprintf(stdout, "restored_thumbnail_image size = %d\n", restored_thumbnail_image->size());
				if(thumbnail_image->size() != restored_thumbnail_image->size())
				{
					#if defined (_WIN32)
					_asm int 3;
					#endif
				}
				else if(memcmp(thumbnail_image->data(), restored_thumbnail_image->data(), thumbnail_image->size()) != 0)
				{
					fprintf(stdout, "Memcmp failed!\n");
					#if defined (_WIN32)
					_asm int 3;
					#endif
				}
				else
				{
					fprintf(stdout, "Memcmp succeeded!\n");
				}
			}

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