/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Boost Software License - Version 1.0 - August 17th, 2003
 *
 * Permission is hereby granted, free of charge, to any person or organization
 * obtaining a copy of the software and accompanying documentation covered by
 * this license (the "Software") to use, reproduce, display, distribute,
 * execute, and transmit the Software, and to prepare derivative works of the
 * Software, and to permit third-parties to whom the Software is furnished to
 * do so, all subject to the following:
 *
 * The copyright notices in the Software and this entire statement, including
 * the above license grant, this restriction and the following disclaimer,
 * must be included in all copies of the Software, in whole or in part, and
 * all derivative works of the Software, unless such copies or derivative
 * works are solely in the form of machine-executable object code generated by
 * a source language processor.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
 * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
 * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 **********************************************************************/

#include "core.h"
#include <interfaces/iplugin2.h>
#include <interfaces/secman/istorageplugin.h>

Q_DECLARE_METATYPE (QVariantList*);

namespace LeechCraft
{
namespace Plugins
{
namespace SecMan
{
	Core::Core ()
	{
	}

	Core& Core::Instance ()
	{
		static Core c;
		return c;
	}

	bool Core::CouldHandle (const Entity& e) const
	{
		if (e.Mime_ == "x-leechcraft/data-persistent-save" ||
				e.Mime_ == "x-leechcraft/data-persistent-load")
			return true;

		return false;
	}

	void Core::Handle (Entity e)
	{
		if (e.Mime_ == "x-leechcraft/data-persistent-load")
		{
			QList<QVariant> rawKeys = e.Entity_.toList ();

			QVariant valuesList = e.Additional_ ["Values"];
			if (!valuesList.canConvert<QVariantList*> ())
			{
				qWarning () << Q_FUNC_INFO
						<< "could not convert Entity.Additional_ [\"Values\"] "
							"to QVariant*, sender plugin sucks";
				return;
			}

			QVariantList *valuesListPtr = valuesList.value<QVariantList*> ();
			if (!valuesListPtr)
			{
				qWarning () << Q_FUNC_INFO
						<< "null Entity.Additional_ [\"Values\"] pointer";
				return;
			}

			bool secure = e.Additional_.value ("SecureStorage", false).toBool ();

			QList<QByteArray> keys;
			Q_FOREACH (const QVariant& rKey, rawKeys)
			{
				QByteArray key = rKey.toByteArray ();
				if (key.isEmpty ())
					continue;
				keys << key;
			}

			QList<QVariantList> values = Load (keys, secure);
			if (values.size () != keys.size ())
				return;

			valuesListPtr->clear ();
			Q_FOREACH (const QVariantList& value, values)
				*valuesListPtr << QVariant (value);
		}
		else if (e.Mime_ == "x-leechcraft/data-persistent-save")
		{
			QList<QVariant> rawKeys = e.Entity_.toList ();
			QList<QVariant> rawValues = e.Additional_ ["Values"].toList ();

			QList<QByteArray> keys;
			Q_FOREACH (const QVariant& rKey, rawKeys)
			{
				QByteArray key = rKey.toByteArray ();
				if (key.isEmpty ())
					continue;
				keys << key;
			}

			QList<QVariantList> values;
			Q_FOREACH (const QVariant& rValue, rawValues)
				values << rValue.toList ();

			if (values.size () != keys.size ())
			{
				qWarning () << Q_FUNC_INFO
						<< "keys size doesn't match values size, raw data:"
						<< rawKeys
						<< values;
				return;
			}

			if (keys.size () == 0)
				return;

			bool secure = e.Additional_.value ("SecureStorage", false).toBool ();
			bool overwrite = e.Additional_.value ("Overwrite", false).toBool ();
			Store (keys, values, secure, overwrite);
		}
	}

	QSet<QByteArray> Core::GetExpectedPluginClasses () const
	{
		return QSet<QByteArray> () << "org.LeechCraft.SecMan.StoragePlugins/1.0";
	}

	void Core::AddPlugin (QObject *plugin)
	{
		IPlugin2 *ip2 = qobject_cast<IPlugin2*> (plugin);
		if (!ip2)
		{
			qWarning () << Q_FUNC_INFO
					<< "passed object is not a IPlugin2"
					<< plugin;
			return;
		}

		QSet<QByteArray> classes = ip2->GetPluginClasses ();
		if (classes.contains ("org.LeechCraft.SecMan.StoragePlugins/1.0"))
			AddStoragePlugin (plugin);
	}

	QObjectList Core::GetStoragePlugins () const
	{
		return QObjectList () << GetStoragePlugin ();
	}

	void Core::AddStoragePlugin (QObject *plugin)
	{
		if (!qobject_cast<IStoragePlugin*> (plugin))
		{
			qWarning () << Q_FUNC_INFO
					<< "passed object is not a IStoragePlugin"
					<< plugin;
			return;
		}
		StoragePlugins_ << plugin;
	}

	void Core::Store (const QList<QByteArray>& keys,
			const QList<QVariantList>& values, bool secure, bool overwrite)
	{
		Q_ASSERT (keys.size () == values.size ());
		Q_ASSERT (keys.size ());

		QObject *storage = GetStoragePlugin ();

		if (!storage)
		{
			qWarning () << Q_FUNC_INFO
					<< "null storage";
			return;
		}

		IStoragePlugin::StorageType type = secure ?
				IStoragePlugin::STSecure :
				IStoragePlugin::STInsecure;

		QList<QPair<QByteArray, QVariantList>> data;
		for (int i = 0; i < keys.size (); ++i)
			data << qMakePair (keys.at (i), values.at (i));
		qobject_cast<IStoragePlugin*> (storage)->Save (data, type, overwrite);
	}

	QList<QVariantList> Core::Load (const QList<QByteArray>& keys, bool secure)
	{
		if (!keys.size ())
			return QList<QVariantList> ();

		QObject *storage = GetStoragePlugin ();

		if (!storage)
		{
			qWarning () << Q_FUNC_INFO
					<< "null storage";
			return QList<QVariantList> ();
		}

		IStoragePlugin::StorageType type = secure ?
				IStoragePlugin::STSecure :
				IStoragePlugin::STInsecure;

		return qobject_cast<IStoragePlugin*> (storage)->Load (keys, type);
	}

	QObject* Core::GetStoragePlugin () const
	{
		return StoragePlugins_.size () ? StoragePlugins_.at (0) : 0;
	}
}
}
}
