/**********************************************************************
 * LeechCraft - modular cross-platform feature rich internet client.
 * Copyright (C) 2006-2014  Georg Rudoy
 *
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt)
 **********************************************************************/

#ifndef INTERFACES_ISCRIPTLOADER_H
#define INTERFACES_ISCRIPTLOADER_H
#include <memory>
#include <QVariant>

/** @brief Interface for objects representing scripts.
 *
 * This interface is to be implemented by the objects that represent
 * loaded scripts.
 */
class Q_DECL_EXPORT IScript
{
public:
	virtual ~IScript () {}

	/** @brief Returns this script object as a QObject.
	 *
	 * @return This script object as a QObject.
	 */
	virtual QObject* GetQObject () = 0;

	/** @brief Runs the given function and returns its value.
	 *
	 * This method invokes the method identified by name with the given
	 * args, if any, and returns its value.
	 *
	 * If there is no such method or call has failed, the returned
	 * variant is null.
	 *
	 * @warning This function should be called after all required objects
	 * are added with AddQObject() otherwise they may not be visible to
	 * the script.
	 *
	 * @param[in] name The name of the method to invoke.
	 * @param[in] args The list of arguments for the method.
	 * @return The return value of the method, if any, or null variant
	 * if the call has failed.
	 */
	virtual QVariant InvokeMethod (const QString& name,
			const QVariantList& args = {}) = 0;

	/** @brief Adds the given object to the script context.
	 *
	 * This method adds the \em object to the script context under the
	 * given \em name. The metafunctions of the \em object (that is,
	 * signals, slots, properties and Q_INVOKABLE functions) will become
	 * available to the script.
	 *
	 * @warning This function should be called before Execute()
	 * or InvokeMethod (), otherwise the added objects may not be visible
	 * to the script.
	 *
	 * @param[in] object The object to add to the script context.
	 * @param[in] name The name under which the object should be added.
	 */
	virtual void AddQObject (QObject *object, const QString& name) = 0;

	/** @brief Executes the script.
	 *
	 * There is no need to call this function explicitly if
	 * InvokeMethod() will be called, since the latter implies executing
	 * the script.
	 *
	 * @warning This function should be called after all required objects
	 * are added with AddQObject() otherwise they may not be visible to
	 * the script.
	 */
	virtual void Execute () = 0;
};

typedef std::shared_ptr<IScript> IScript_ptr;

/** @brief Interface for script loaders.
 *
 * Script loaders are those objects that are directly responsible for
 * loading scripts and exposing them as IScript. Scripts are loaded via
 * the LoadScript() method.
 *
 * The list of all available scripts with the current set of prefixes
 * could also be obtained via the
 *
 * The scripts are identified by their name. Script loader must properly
 * append the corresponding extension while checking for the existence
 * of the corresponding script.
 *
 * The scripts are loaded from the path which is built as concatenation
 * of prefix path (adjusted via AddGlobalPrefix() and AddLocalPrefix()
 * methods), relative path (which is passed to the
 * IScriptLoader::CreateScriptLoaderInstance() method as parameter) and
 * interpreter name. The following interpreter names are defined (and
 * the corresponding extensions):
 * - qtscript for *.qs, *.js, *.es.
 * - python for *.py
 * - ruby for *.rb
 *
 * Several local prefixes could be added by the consecutive calls to
 * AddGlobalPrefix() and AddLocalPrefix() respectively. The script
 * loader must check the prefixes in the reversed order of their
 * addition, so if you want the local scripts to have higher precedence
 * than global ones, you should call AddLocalPrefix() after calling
 * AddGlobalPrefix().
 *
 * @sa IScriptLoader, IScript
 */
class Q_DECL_EXPORT IScriptLoaderInstance
{
public:
	virtual ~IScriptLoaderInstance () {}

	/** @brief Returns this loader instance as a QObject.
	 *
	 * @return This script loader instance as a QObject.
	 */
	virtual QObject* GetQObject () = 0;

	/** @brief Adds a global load prefix.
	 *
	 * This is <code>/usr/[local/]share/leechcraft/scripts</code> on
	 * Unix-like OSes and <code>%APP_PATH%/share/scripts</code> on
	 * Windows.
	 */
	virtual void AddGlobalPrefix () = 0;

	/** @brief Adds a local load prefix.
	 *
	 * This is ~/.leechcraft/data/scripts/ + prefix.
	 */
	virtual void AddLocalPrefix (QString prefix = QString ()) = 0;

	/** @brief Lists all available scripts.
	 *
	 * This function lists the names of all available scripts, ready to
	 * be passed to LoadScript(), from the locations available with the
	 * current set of prefixes.
	 *
	 * @return The list of available scripts.
	 */
	virtual QStringList EnumerateScripts () const = 0;

	/** @brief Returns the metadata of the given script.
	 */
	virtual QVariantMap GetScriptInfo (const QString& script) = 0;

	/** @brief Loads the given script.
	 *
	 * This method loads the script identified by the script, and
	 * returns an object used to communicate with the script, or a null
	 * pointer if the given script could not be loaded.
	 *
	 * Please refer to this class' documentation for more information
	 * regarding script paths.
	 *
	 * @note Implementations may choose to return a valid pointer even
	 * if the script is loaded correctly. In this case, all operations
	 * on it will fail, though.
	 *
	 * @param[in] script The script base name.
	 * @return The script wrapper object.
	 */
	virtual IScript_ptr LoadScript (const QString& script) = 0;
};

typedef std::shared_ptr<IScriptLoaderInstance> IScriptLoaderInstance_ptr;

/** @brief Interface for plugins providing scripting.
 *
 * This interface is to be implemented by plugins that support loading
 * scripts if they wish to provide scripting support for other plugins.
 *
 * Plugins that wish to load and execute scripts create instances of
 * script loaders and use them for loading script objects.
 *
 * See the documentation of IScriptLoaderInstance for details about
 * script paths and such.
 *
 * @sa IScriptLoaderInstance
 */
class Q_DECL_EXPORT IScriptLoader
{
public:
	virtual ~IScriptLoader () {}

	/** @brief Creates an instance of the script loader.
	 *
	 * The loader instance is the object that is used to load the
	 * scripts.
	 *
	 * The script loader loads them from the (prefix + relPath) path,
	 * where path is one of paths later added to the instance's list of
	 * search paths via the IScriptLoaderInstance::AddGlobalPrefix() and
	 * IScriptLoaderInstance::AddLocalPrefix() functions.
	 *
	 * Ownership is transferred to the caller.
	 *
	 * @param[in] relPath The relative path of the scripts to be loaded.
	 * @return The script loader for the given relative path.
	 */
	virtual IScriptLoaderInstance_ptr CreateScriptLoaderInstance (const QString& relPath) = 0;
};

Q_DECLARE_INTERFACE (IScript, "org.Deviant.LeechCraft.IScript/1.0")
Q_DECLARE_INTERFACE (IScriptLoaderInstance, "org.Deviant.LeechCraft.IScriptLoaderInstance/1.0")
Q_DECLARE_INTERFACE (IScriptLoader, "org.Deviant.LeechCraft.IScriptLoader/1.0")

#endif
