/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2013  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.
 **********************************************************************/

#ifndef UTIL_UTIL_H
#define UTIL_UTIL_H
#include "utilconfig.h"
#include <QString>
#include <QDir>
#include <QModelIndex>
#include <QtXml/QDomElement>
#include <QtDebug>
#include <interfaces/structures.h>

class QTranslator;
class QLocale;

namespace LeechCraft
{
	namespace Util
	{
		/** @brief An utility function that creates a QString from
		 * UTF8-encoded std::string.
		 *
		 * @param[in] str The UTF-8 encoded std::string.
		 * @return The QString containing the same string.
		 */
		inline QString FromStdString (const std::string& str)
		{
			return QString::fromUtf8 (str.c_str ());
		}

		template<typename T>
		struct ValueFinder
		{
			typedef typename T::data_type data_type;
			data_type Object_;

			ValueFinder (data_type data)
			: Object_ (data)
			{
			}

			bool operator() (typename T::value_type i)
			{
				return i.second == Object_;
			}
		};

		/** @brief Returns the given image in a Base64-encoded form.
		 *
		 * The return result is suitable for inserting into
		 * <code>&lt;img></code>'s <code>src</code> attribute as is.
		 *
		 * @param[in] image The image to represent as Base64-encoded
		 * form.
		 * @return The source string.
		 */
		UTIL_API QString GetAsBase64Src (const QImage& image);

		/** @brief Return the user-readable representation of the entity.
		 *
		 * @param[in] entity The Entity from which to make the
		 * user-readable representation.
		 * @return The user-readable string describing the entity.
		 */
		UTIL_API QString GetUserText (const Entity& entity);

		/** @brief Makes a formatted size from number.
		 *
		 * Converts, for example, 1048576 to 1.0 MB.
		 *
		 * @param[in] sourcesize Size in bytes.
		 * @return Formatted string.
		 *
		 * @sa SetStrings
		 * @sa MakeTimeFromLong
		 */
		UTIL_API QString MakePrettySize (qint64);

		/** @brief Makes a formatted time from number.
		 *
		 * Converts, for example 256 to 00:04:16.
		 *
		 * @param[in] time Time interval in seconds.
		 * @return DateTime object.
		 *
		 * @sa MakePrettySize
		 */
		UTIL_API QString MakeTimeFromLong (ulong);

		/** @brief Loads and installs a translator.
		 *
		 * Attempts to load and install a translator for the current
		 * locale. The name is formed like this:
		 * 'prefix_' + base + '_' + locale
		 * If base is an empty string, the second _ isn't appended.
		 *
		 * First resources are searched (:/), then APPDIR/translations
		 * on Windows and /usr/[local/]share/appname/translations on
		 * Unix.
		 *
		 * @param[in] base Base name of the translation file.
		 * @param[in] prefix The optional prefix of the translation
		 * (useful if it's not LC's one).
		 * @param[in] appname Base name of the application.
		 * @return The translator object if loading is successful, NULL
		 * otherwise.
		 */
		UTIL_API QTranslator* InstallTranslator (const QString& base,
				const QString& prefix = "leechcraft",
				const QString& appname = "leechcraft");

		/** @brief Returns the current locale name, like en_US.
		 *
		 * First, this function checks the locale value stored in
		 * "Language" key of settings object with organizationName() and
		 * applicationName(). If it's equal to "system", this function
		 * queries the LANG environment variable, and if it is empty or
		 * in invalid format (not like en_US), it takes the value of
		 * QLocale::system().name().
		 *
		 * Then, if the resulting name the name of the language only,
		 * GetLocaleName() tries to find any countries for that
		 * language. If any countries are found, the code of the first
		 * found country is appended, else "_00" is appended.
		 *
		 * @return Current locale name.
		 *
		 * @sa GetLanguage()
		 */
		UTIL_API QString GetLocaleName ();

		UTIL_API QString GetInternetLocaleName (const QLocale&);

		/** @brief Returns the current language name.
		 *
		 * This function works as GetLocaleName() except it doesn't
		 * return (and doesn't query for) country name.
		 *
		 * @return Current language name.
		 *
		 * @sa GetLocaleName()
		 */
		UTIL_API QString GetLanguage ();

		/** @brief Creates a path if it doesn't exist.
		 *
		 * Creates a relative path ~/.leechcraft/path and throws an
		 * exception if this could not be done or if such path already
		 * exists and it is not readable.
		 *
		 * @param[in] path The path to create.
		 * @return The newly created dir.
		 * @exception std::runtime_error Throws if the path could not be
		 * created.
		 */
		UTIL_API QDir CreateIfNotExists (const QString& path);

		/** @brief Returns the path relative to user directory.
		 *
		 * Returns a QDir object that represents the ~/.leechcraft/path
		 * or throws an exceptions if this path doesn't exist.
		 *
		 * @param[in] path The path to return relative to ~/.leechcraft
		 * @return The QDir object for that path.
		 * @exception std::runtime_error Throws if the path doesn't
		 * exist.
		 */
		UTIL_API QDir GetUserDir (const QString& path);

		/** @brief Returns a temporary filename.
		 *
		 * This function returns a name of a temporary file that could
		 * be created, not createing the file itself.
		 *
		 * @param[in] pattern Pattern of the filename.
		 * @return The filename.
		 */
		UTIL_API QString GetTemporaryName (const QString& pattern = QString ("lc_temp.XXXXXX"));

		/** @brief An utility function to make a Entity.
		 *
		 * Creates a Entity that wraps the given entity from
		 * given location with parameterrs identified by tp and given
		 * mime type (which is null by default).
		 *
		 * This function is provided for convenience and is equivalent
		 * to manually filling the Entity.
		 *
		 * @param[in] entity The Entity_ field of the Entity.
		 * @param[in] location The Location_ field of the Entity.
		 * @param[in] tp The Params_ field of the Entity.
		 * @param[in] mime The Mime_ field of the Entity.
		 * @return The resulting Entity.
		 *
		 * @sa Entity, MakeNotification()
		 */
		UTIL_API Entity MakeEntity (const QVariant& entity,
				const QString& location,
				LeechCraft::TaskParameters tp,
				const QString& mime = QString ());

		/** @brief An utility function to make a Entity with
		 * notification.
		 *
		 * Creates a Entity that holds information about
		 * user-visible notification. These notifications have
		 * "x-leechcraft/notification" MIME.
		 *
		 * You can further customize the returned Entity to suit
		 * your exact needs. See the documentation for Entity
		 * about such entities.
		 *
		 * @param[in] header The header of the notification.
		 * @param[in] text The text of the notification.
		 * @param[in] priority The priority level of the notification.
		 * @return The Entity containing the corresponding
		 * notification.
		 *
		 * @sa Entity, MakeEntity()
		 */
		UTIL_API Entity MakeNotification (const QString& header,
				const QString& text,
				Priority priority);

		/** @brief Makes an event for canceling another Advanced
		 * Notifications event.
		 *
		 * Creates an Entity that cancels a previously generated
		 * Advanced Notifications event. The returned entity can be
		 * then emitted to notify plugins that the given event has been
		 * canceled.
		 *
		 * @param[in] event The event to cancel.
		 * @return The Entity canceling the given event.
		 */
		UTIL_API Entity MakeANCancel (const Entity& event);

		/** @brief Makes an event for canceling another Advanced
		 * Notifications event.
		 *
		 * Creates an Entity that cancels a previously generated
		 * Advanced Notifications event. The returned entity can be
		 * then emitted to notify plugins that the given event has been
		 * canceled.
		 *
		 * This function doesn't take a previously created entity as the
		 * other overload does. Instead, it plainly creates the required
		 * entity from the given senderId and eventId. They should match
		 * those of the event in question.
		 *
		 * @param[in] senderId The ID of the sender of the event that is
		 * to be canceled.
		 * @param[in] eventId The ID of the event that is to be canceled.
		 * @return The Entity canceling the given event.
		 */
		UTIL_API Entity MakeANCancel (const QString& senderId, const QString& eventId);

		UTIL_API QModelIndexList GetSummarySelectedRows (QObject *sender);

		/** @brief Returns the action that is set to act as a separator.
		 *
		 * That is the action with setSeparator(true);
		 *
		 * @param[in] parent The parent of the action.
		 * @return The separator action.
		 */
		UTIL_API QAction* CreateSeparator (QObject *parent);

		UTIL_API QVariantList GetPersistentData (const QList<QVariant>& keys,
				QObject *object);

		UTIL_API QPixmap DrawOverlayText (QPixmap px, const QString& text, QFont font, const QPen& pen, const QBrush& brush);

		/** @brief Returns an element for a given tags list.
		 *
		 * This function tries to implement projection from tags to a
		 * hierarchical structure in form of XML. It traverses the tags
		 * list and creates child nodes from the document, appending
		 * the hierarchical structure's tree root to the node. It
		 * returns the parent element to which the item should be
		 * appended.
		 *
		 * For empty tags list it just returns node converted to the
		 * QDomElement.
		 *
		 * tagSetter is a function or functor that should be able to
		 * take two parameters, a QDomElement and a QString, and set
		 * tags for it.
		 *
		 * tagGetter is a function or functor that should be able to
		 * take one parameter, a QDomElement, and return a QString for
		 * it with tags previously set with tagSetter.
		 *
		 * @param[in] tags List of tags.
		 * @param[in] node The parent-most node to which all other nodes
		 * are appended.
		 * @param[in] document The document containing all these nodes.
		 * @param[in] elementName The name of the XML element that
		 * carries info about the tags.
		 * @param[in] tagSetter Setter function for the tags for the
		 * given element.
		 * @param[in] tagGetter Getter function for the tags for the
		 * given element.
		 * @return Parent element of the item with tags.
		 */
		template<typename TagGetter, typename TagSetter>
		QDomElement GetElementForTags (const QStringList& tags,
				QDomNode& node,
				QDomDocument& document,
				const QString& elementName,
				TagGetter tagGetter,
				TagSetter tagSetter)
		{
			if (!tags.size ())
			{
				qWarning () << Q_FUNC_INFO
					<< "no tags"
					<< elementName;
				return node.toElement ();
			}

			QDomNodeList elements = node.childNodes ();
			for (int i = 0; i < elements.size (); ++i)
			{
				QDomElement elem = elements.at (i).toElement ();
				if (tagGetter (elem) == tags.at (0))
				{
					if (tags.size () > 1)
					{
						QStringList childTags = tags;
						childTags.removeAt (0);
						return GetElementForTags (childTags, elem,
								document, elementName,
								tagGetter, tagSetter);
					}
					else
						return elem;
				}
			}

			QDomElement result = document.createElement (elementName);
			tagSetter (result, tags.at (0));
			node.appendChild (result);
			if (tags.size () > 1)
			{
				QStringList childTags = tags;
				childTags.removeAt (0);
				return GetElementForTags (childTags, result,
						document, elementName,
						tagGetter, tagSetter);
			}
			else
				return result;
		}

		template<typename K, typename V>
		QMap<K, V> MakeMap (std::initializer_list<QPair<K, V>> l)
		{
			QMap<K, V> result;
			for (const auto& pair : l)
				result [pair.first] = pair.second;
			return result;
		}
	};
};

#endif

