#ifndef SH_PLUGIN_BASE_H
#define SH_PLUGIN_BASE_H

#include <QProcess>
#include <QObject>
#include <piplugin.h>
#include "SH_types.h"

#ifdef CC_GCC
#  pragma GCC diagnostic ignored "-Wunused-function"
#endif

#define SHS_PLUGIN \
	PIP_PLUGIN \
	QList<SHPluginFactoryBase * > collection; \
	extern "C" { \
		PIP_PLUGIN_EXPORT QList<SHPluginFactoryBase * > getPluginList() { \
			return collection; \
		} \
	}

#define SHS_ADD_PLUGIN(name) \
	STATIC_INITIALIZER_BEGIN \
		collection << new SHPluginFactory<name>(); \
	STATIC_INITIALIZER_END

#define SHS_BUILTIN_PLUGIN(name) \
	STATIC_INITIALIZER_BEGIN \
		SHPluginsLoader::addBuiltinPlugin(new name()); \
	STATIC_INITIALIZER_END


QT_BEGIN_NAMESPACE


class SHSScriptEngine;
class SHPacketWidgetBase;
class SHBlockWidgetBase;
class DeviceWidget;

class SHPluginBase: public QObject {
	Q_OBJECT
	Q_ENUMS(PluginType PluginFlag PluginActionFlag PluginActionIcon)
	Q_FLAGS(PluginFlag BlockFlags)
	Q_FLAGS(PluginActionFlag BlockFlags)
	friend class SHSScriptEngine;
	friend class SHPluginsLoader;
public:
	explicit SHPluginBase() {_rt = nullptr;}
	virtual ~SHPluginBase() {}

	/// Type of plugin
	enum PluginType {
		None    /** Undefined plugin */ ,
		Block   /** Block plugin. Used to modify blocks, executes some actions from block propery dialog */ ,
		Bus     /** Bus plugin. Used to modify buses and create well-controlled address space */ ,
		Packet  /** Packet plugin. Used to form finish binary packet for device from packet description
					and dynamic arguments. Add properties to script block. Add packet variable aliases
					and control variable name by arguments*/ ,
		Script  /** Script plugin. Used to extend script functionality */ ,
		Gateway /** Gateway plugin. Used to control packets if block has Gateway flag */ ,
		Extra   /** Some extrs functionality */ ,
	};
	
	enum PluginFlag {
	};

	/// This enum describes some actions, accompanying action
	enum PluginActionFlag {
		GenerateDatabase /** Generate scheme block properties and packets before runAction() */ = 0x1,
		CloseDialog      /** Close dialog after runAction() */ = 0x2,
		ApplyProperties  /** Save property changes before runAction() */ = 0x4,
		UpdateProperties /** Update properties after runAction() */ = 0x8
	};

	/// Icon for action button
	enum PluginActionIcon {
		NoIcon        /**  */ ,
		EditIcon      /**  */ ,
		ConfigureIcon /**  */ ,
		RunIcon       /**  */ ,
		BurnIcon      /**  */ ,
		CustomIcon    /**  */
	};

	Q_DECLARE_FLAGS(PluginActionFlags, PluginActionFlag)
	Q_DECLARE_FLAGS(PluginFlags, PluginFlag)

	struct PluginAction {
		PluginAction(const QString & n = QString(), const QString & c = QString(),
					 int f = 0, PluginActionIcon it = NoIcon,
					 const QIcon & i = QIcon(), const QString & t = QString()):
			name(n), caption(c), flags(f), iconType(it), icon(i) , tooltip(t) {}
		QString name;
		QString caption;
		PluginActionFlags flags;
		PluginActionIcon iconType;
		QIcon icon;
		QString tooltip;
	};

	/// Plugin name, this is only mandatory function of plugin
	virtual QString name() const = 0;

	/// Plugin type
	virtual PluginType type() const {return None;}

	/// Plugin description
	virtual QString description() const {return "No description";}

	/// Plugin version
	virtual QString version() const {return "0.0";}

	/// Plugin flags
	virtual PluginFlags flags() const {return PluginFlags();}

	/// Executed before main script execute
	virtual void init() {}

	/// Executed after main script execute
	virtual void destroy() {}

	/// Implement to add some dynamic properties in project
	virtual PropertyStorage addPropertiesToProject() const {return PropertyStorage();}

	//virtual PropertyStorage itemProperties   () const {return PropertyStorage();}
	virtual PropertyStorage projectProperties() const {return PropertyStorage();}
	virtual PropertyStorage globalProperties () const {return PropertyStorage();}
	//static SHRuntime * runtime();

	/// Implement to add buttons in the plugin property widget
	virtual QList<PluginAction> addProjectActions() {return QList<PluginAction>();}

	/// Implement to enable or disable given action
	virtual bool checkProjectAction(const PluginAction & action) {return true;}

	/// Execute action from property widget
	virtual void runProjectAction(const PluginAction & action) {}

	uint id() const;

	/// Call global script function "function(args)"
	QVariant call(const QString & function, QVariantList args = QVariantList());

	SHPluginBase * anotherPlugin(PluginType type_, const QString & name_);

	SHRuntime * runtime() const {return _rt;}

	PropertyStorage project_properties, server_block;
	
protected:
	void emitMessage(SHS::MessageClass mc, const QString & text) {emit __message(mc, text);}
	void setPluginProperty(const QString & pname, const QVariant & value);
	QVariant pluginProperty(const QString & pname);

private:
	static uint getID(const QString & name, int type);

	QMap<QString, SHS::Object*> _objects;
	QMap<uint, SHPluginBase*> _plugins;
	SHRuntime * _rt = nullptr;
	
signals:
	void __message(int mc, QString t);

};


class SHPluginFactoryBase {
public:
	virtual SHPluginBase * createPlugin() = 0;
};


template<typename T>
class SHPluginFactory: public SHPluginFactoryBase {
public:
	SHPluginBase * createPlugin() final {return new T();}
};


QT_END_NAMESPACE

#endif
