/*
 * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
 * http://www.gnu.org/licenses/lgpl-3.0.html
 */

#ifndef CBPLUGIN_H
#define CBPLUGIN_H

#include <wx/arrstr.h>
#include <wx/event.h>
#include <wx/intl.h>
#include <wx/string.h>

#include "settings.h" // build settings
#include "globals.h"
#include "manager.h"
#include "pluginmanager.h"

#ifdef __WXMSW__
	#ifndef PLUGIN_EXPORT
		#ifdef EXPORT_LIB
			#define PLUGIN_EXPORT __declspec (dllexport)
		#else // !EXPORT_LIB
			#ifdef BUILDING_PLUGIN
				#define PLUGIN_EXPORT __declspec (dllexport)
			#else // !BUILDING_PLUGIN
				#define PLUGIN_EXPORT __declspec (dllimport)
			#endif // BUILDING_PLUGIN
		#endif // EXPORT_LIB
	#endif // PLUGIN_EXPORT
#else
	#define PLUGIN_EXPORT
#endif

// this is the plugins SDK version number
// it will change when the SDK interface breaks
#define PLUGIN_SDK_VERSION_MAJOR 1
#define PLUGIN_SDK_VERSION_MINOR 11
#define PLUGIN_SDK_VERSION_RELEASE 12

// class decls
class wxMenuBar;
class wxMenu;
class wxToolBar;
class wxPanel;
class wxWindow;
class cbEditor;
class cbProject;
class ProjectBuildTarget;
class CompileTargetBase;
class FileTreeData;
class cbConfigurationPanel;
struct PluginInfo;

// Define basic groups for plugins' configuration.
static const int cgCompiler         = 0x01; ///< Compiler related.
static const int cgDebugger         = 0x02; ///< Debugger related.
static const int cgEditor           = 0x04; ///< Editor related.
static const int cgCorePlugin       = 0x08; ///< One of the core plugins.
static const int cgContribPlugin    = 0x10; ///< One of the contrib plugins (or any third-party plugin for that matter).
static const int cgUnknown          = 0x20; ///< Unknown. This will be probably grouped with cgContribPlugin.

/** @brief Base class for plugins
  * This is the most basic class a plugin must descend
  * from.
  * cbPlugin descends from wxEvtHandler, so it provides its methods as well...
  * \n \n
  * It's not enough to create a new plugin. You must also provide a resource
  * zip file containing a file named "manifest.xml". Check the manifest.xml
  * file of existing plugins to see how to create one (it's ultra-simple).
  */
class PLUGIN_EXPORT cbPlugin : public wxEvtHandler
{
    public:
		/** In default cbPlugin's constructor the associated PluginInfo structure
		  * is filled with default values. If you inherit from cbPlugin, you
		  * should fill the m_PluginInfo members with the appropriate values.
		  */
        cbPlugin();

		/** cbPlugin destructor. */
        virtual ~cbPlugin();

		/** The plugin must return its type on request. */
        virtual PluginType GetType() const { return m_Type; }

		/** If a plugin provides some sort of configuration dialog,
		  * this is the place to invoke it.
		  */
        virtual int Configure(){ return 0; }

        /** Return the plugin's configuration priority.
          * This is a number (default is 50) that is used to sort plugins
          * in configuration dialogs. Lower numbers mean the plugin's
          * configuration is put higher in the list.
          */
        virtual int GetConfigurationPriority() const { return 50; }

        /** Return the configuration group for this plugin. Default is cgUnknown.
          * Notice that you can logically AND more than one configuration groups,
          * so you could set it, for example, as "cgCompiler | cgContribPlugin".
          */
        virtual int GetConfigurationGroup() const { return cgUnknown; }

		/** Return plugin's configuration panel.
		  * @param parent The parent window.
		  * @return A pointer to the plugin's cbConfigurationPanel. It is deleted by the caller.
		  */
        virtual cbConfigurationPanel* GetConfigurationPanel(wxWindow* parent){ return 0; }

		/** Return plugin's configuration panel for projects.
		  * The panel returned from this function will be added in the project's
		  * configuration dialog.
		  * @param parent The parent window.
		  * @param project The project that is being edited.
		  * @return A pointer to the plugin's cbConfigurationPanel. It is deleted by the caller.
		  */
        virtual cbConfigurationPanel* GetProjectConfigurationPanel(wxWindow* parent, cbProject* project){ return 0; }

		/** This method is called by Code::Blocks and is used by the plugin
		  * to add any menu items it needs on Code::Blocks's menu bar.\n
		  * It is a pure virtual method that needs to be implemented by all
		  * plugins. If the plugin does not need to add items on the menu,
		  * just do nothing ;)
		  *
		  * @note This function may be called more than one time. This can happen,
		  * for example, when a plugin is installed or uninstalled.
		  *
		  * @param menuBar the wxMenuBar to create items in
		  */
        virtual void BuildMenu(wxMenuBar* menuBar) = 0;

		/** This method is called by Code::Blocks core modules (EditorManager,
		  * ProjectManager etc) and is used by the plugin to add any menu
		  * items it needs in the module's popup menu. For example, when
		  * the user right-clicks on a project file in the project tree,
		  * ProjectManager prepares a popup menu to display with context
		  * sensitive options for that file. Before it displays this popup
		  * menu, it asks all attached plugins (by asking PluginManager to call
		  * this method), if they need to add any entries
		  * in that menu. This method is called.\n
		  * If the plugin does not need to add items in the menu,
		  * just do nothing ;)
		  * @param type the module that's preparing a popup menu
		  * @param menu pointer to the popup menu
		  * @param data pointer to FileTreeData object (to access/modify the file tree)
		  */
        virtual void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0) = 0;

		/** This method is called by Code::Blocks and is used by the plugin
		  * to add any toolbar items it needs on Code::Blocks's toolbar.\n
		  * It is a pure virtual method that needs to be implemented by all
		  * plugins. If the plugin does not need to add items on the toolbar,
		  * just do nothing ;)
		  * @param toolBar the wxToolBar to create items on
		  * @return The plugin should return true if it needed the toolbar, false if not
		  */
        virtual bool BuildToolBar(wxToolBar* toolBar) = 0;

        /** See whether this plugin is attached or not. A plugin should not perform
		  * any of its tasks, if not attached...
		  * @note This function is *not* virtual.
		  * @return Returns true if it attached, false if not.
		  */
		bool IsAttached() const { return m_IsAttached; }

        /** See whether this plugin can be detached (unloaded) or not.
          * This function is called usually when the user requests to
          * uninstall or disable a plugin. Before disabling/uninstalling it, Code::Blocks
          * asks the plugin if it can be detached or not. In other words, it checks
          * to see if it can be disabled/uninstalled safely...
          * @par
          * A plugin should return true if it can be detached at this moment, false if not.
          * @return The default implementation returns true.
		  */
		virtual bool CanDetach() const { return true; }
    protected:
		/** Any descendent plugin should override this virtual method and
		  * perform any necessary initialization. This method is called by
		  * Code::Blocks (PluginManager actually) when the plugin has been
		  * loaded and should attach in Code::Blocks. When Code::Blocks
		  * starts up, it finds and <em>loads</em> all plugins but <em>does
		  * not</em> activate (attaches) them. It then activates all plugins
		  * that the user has selected to be activated on start-up.\n
		  * This means that a plugin might be loaded but <b>not</b> activated...\n
		  * Think of this method as the actual constructor...
		  */
        virtual void OnAttach(){}

		/** Any descendent plugin should override this virtual method and
		  * perform any necessary de-initialization. This method is called by
		  * Code::Blocks (PluginManager actually) when the plugin has been
		  * loaded, attached and should de-attach from Code::Blocks.\n
		  * Think of this method as the actual destructor...
		  * @param appShutDown If true, the application is shutting down. In this
		  *         case *don't* use Manager::Get()->Get...() functions or the
		  *         behaviour is undefined...
		  */
        virtual void OnRelease(bool appShutDown){}

		/** This method logs a "Not implemented" message and is provided for
		  * convenience only.
		  */
        virtual void NotImplemented(const wxString& log) const;

		/** Holds the plugin's type. Set in the default constructor. */
        PluginType m_Type;

		/** Holds the "attached" state. */
        bool m_IsAttached;

    private:
        friend class PluginManager; // only the plugin manager has access here

		/** Attach is <b>not</b> a virtual function, so you can't override it.
		  * The default implementation hooks the plugin to Code::Block's
		  * event handling system, so that the plugin can receive (and process)
		  * events from Code::Blocks core library. Use OnAttach() for any
		  * initialization specific tasks.
		  * @see OnAttach()
		  */
        void Attach();

		/** Release is <b>not</b> a virtual function, so you can't override it.
		  * The default implementation un-hooks the plugin from Code::Blocks's
		  * event handling system. Use OnRelease() for any clean-up specific
		  * tasks.
		  * @param appShutDown If true, the application is shutting down. In this
		  *         case *don't* use Manager::Get()->Get...() functions or the
		  *         behaviour is undefined...
		  * @see OnRelease()
		  */
        void Release(bool appShutDown);
};

/** @brief Base class for compiler plugins
  *
  * This plugin type must offer some pre-defined build facilities, on top
  * of the generic plugin's.
  */
class PLUGIN_EXPORT cbCompilerPlugin: public cbPlugin
{
	public:
		cbCompilerPlugin();

		/** @brief Run the project/target.
		  *
		  * Running a project means executing its build output. Of course
		  * this depends on the selected build target and its type.
		  *
		  * @param target The specific build target to "run". If NULL, the plugin
		  * should ask the user which target to "run" (except maybe if there is
		  * only one build target in the project).
		  */
        virtual int Run(ProjectBuildTarget* target = 0L) = 0;

        /** Same as Run(ProjectBuildTarget*) but with a wxString argument. */
        virtual int Run(const wxString& target) = 0;

		/** @brief Clean the project/target.
		  *
		  * Cleaning a project means deleting any files created by building it.
		  * This includes any object files, the binary output file, etc.
		  *
		  * @param target The specific build target to "clean". If NULL, it
		  * cleans all the build targets of the current project.
		  */
        virtual int Clean(ProjectBuildTarget* target = 0L) = 0;

        /** Same as Clean(ProjectBuildTarget*) but with a wxString argument. */
        virtual int Clean(const wxString& target) = 0;

		/** @brief DistClean the project/target.
		  *
		  * DistClean will typically remove any config files
		  * and anything else that got created as part of
		  * building a software package.
		  *
		  * @param target The specific build target to "distclean". If NULL, it
		  * cleans all the build targets of the current project.
		  */
        virtual int DistClean(ProjectBuildTarget* target = 0L) = 0;

        /** Same as DistClean(ProjectBuildTarget*) but with a wxString argument. */
        virtual int DistClean(const wxString& target) = 0;

		/** @brief Build the project/target.
		  *
		  * @param target The specific build target to build. If NULL, it
		  * builds all the targets of the current project.
		  */
        virtual int Build(ProjectBuildTarget* target = 0L) = 0;

        /** Same as Build(ProjectBuildTarget*) but with a wxString argument. */
        virtual int Build(const wxString& target) = 0;

		/** @brief Rebuild the project/target.
		  *
		  * Rebuilding a project is equal to calling Clean() and then Build().
		  * This makes sure that all compilable files in the project will be
		  * compiled again.
		  *
		  * @param target The specific build target to rebuild. If NULL, it
		  * rebuilds all the build targets of the current project.
		  */
        virtual int Rebuild(ProjectBuildTarget* target = 0L) = 0;

        /** Same as Rebuild(ProjectBuildTarget*) but with a wxString argument. */
        virtual int Rebuild(const wxString& target) = 0;

		/** @brief Build all open projects.
		  * @param target If not empty, the target to build in each project. Else all targets.
		  */
        virtual int BuildWorkspace(const wxString& target = wxEmptyString) = 0;

		/** @brief Rebuild all open projects.
		  * @param target If not empty, the target to rebuild in each project. Else all targets.
		  */
        virtual int RebuildWorkspace(const wxString& target = wxEmptyString) = 0;

		/** @brief Clean all open projects.
		  * @param target If not empty, the target to clean in each project. Else all targets.
		  */
        virtual int CleanWorkspace(const wxString& target = wxEmptyString) = 0;

        /** @brief Compile a specific file.
          *
          * @param file The file to compile (must be a project file!)
          */
        virtual int CompileFile(const wxString& file) = 0;

        /** @brief Abort the current build process. */
        virtual int KillProcess() = 0;

        /** @brief Is the plugin currently compiling? */
		virtual bool IsRunning() const = 0;

        /** @brief Get the exit code of the last build process. */
		virtual int GetExitCode() const = 0;

        /** @brief Display configuration dialog.
          *
          * @param project The selected project (can be NULL).
          * @param target The selected target (can be NULL).
          */
		virtual int Configure(cbProject* project, ProjectBuildTarget* target = 0L) = 0;
	private:
};

/** @brief Base class for debugger plugins
  *
  * This plugin type must offer some pre-defined debug facilities, on top
  * of the generic plugin's.
  */
class PLUGIN_EXPORT cbDebuggerPlugin: public cbPlugin
{
	public:
		cbDebuggerPlugin();

		/** @brief Request to add a breakpoint.
		  * @param file The file to add the breakpoint based on a file/line pair.
		  * @param line The line number to put the breakpoint in @c file.
		  * @return True if succeeded, false if not.
		  */
		virtual bool AddBreakpoint(const wxString& file, int line) = 0;

		/** @brief Request to add a breakpoint based on a function signature.
		  * @param functionSignature The function signature to add the breakpoint.
		  * @return True if succeeded, false if not.
		  */
		virtual bool AddBreakpoint(const wxString& functionSignature) = 0;

		/** @brief Request to remove a breakpoint based on a file/line pair.
		  * @param file The file to remove the breakpoint.
		  * @param line The line number the breakpoint is in @c file.
		  * @return True if succeeded, false if not.
		  */
		virtual bool RemoveBreakpoint(const wxString& file, int line) = 0;

		/** @brief Request to remove a breakpoint based on a function signature.
		  * @param functionSignature The function signature to remove the breakpoint.
		  * @return True if succeeded, false if not.
		  */
		virtual bool RemoveBreakpoint(const wxString& functionSignature) = 0;

		/** @brief Request to remove all breakpoints from a file.
		  * @param file The file to remove all breakpoints in. If the argument is empty, all breakpoints are removed from all files.
		  * @return True if succeeded, false if not.
		  */
		virtual bool RemoveAllBreakpoints(const wxString& file = wxEmptyString) = 0;

		/** @brief Notify the debugger that lines were added or removed in an editor.
		  * This causes the debugger to keep the breakpoints list in-sync with the
		  * editors (i.e. what the user sees).
		  * @param editor The editor in question.
		  * @param startline The starting line this change took place.
		  * @param lines The number of lines added or removed. If it's a positive number,
		  *              lines were added. If it's a negative number, lines were removed.
		  */
		virtual void EditorLinesAddedOrRemoved(cbEditor* editor, int startline, int lines) = 0;

		/** @brief Start a new debugging process. */
		virtual int Debug() = 0;

		/** @brief Continue running the debugged program. */
		virtual void Continue() = 0;

		/** @brief Execute the next instruction and return control to the debugger. */
		virtual void Next() = 0;

		/** @brief Execute the next instruction, stepping into function calls if needed, and return control to the debugger. */
		virtual void Step() = 0;

		/** @brief Break the debugging process (stop the debuggee for debugging). */
		virtual void Break() = 0;

		/** @brief Stop the debugging process (exit debugging). */
		virtual void Stop() = 0;

        /** @brief Is the plugin currently debugging? */
		virtual bool IsRunning() const = 0;

        /** @brief Get the exit code of the last debug process. */
		virtual int GetExitCode() const = 0;
};

/** @brief Base class for tool plugins
  *
  * This plugin is automatically managed by Code::Blocks, so the inherited
  * functions to build menus/toolbars are hidden.
  *
  * Tool plugins are automatically added under the "Plugins" menu.
  */
class PLUGIN_EXPORT cbToolPlugin : public cbPlugin
{
    public:
        cbToolPlugin();

        /** @brief Execute the plugin.
          *
          * This is the only function needed by a cbToolPlugin.
          * This will be called when the user selects the plugin from the "Plugins"
          * menu.
          */
        virtual int Execute() = 0;
    private:
        // "Hide" some virtual members, that are not needed in cbToolPlugin
        void BuildMenu(wxMenuBar* menuBar){}
        void RemoveMenu(wxMenuBar* menuBar){}
        void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0){}
        bool BuildToolBar(wxToolBar* toolBar){ return false; }
        void RemoveToolBar(wxToolBar* toolBar){}
};

/** @brief Base class for mime plugins
  *
  * Mime plugins are called by Code::Blocks to operate on files that Code::Blocks
  * wouldn't know how to handle on itself.
  */
class PLUGIN_EXPORT cbMimePlugin : public cbPlugin
{
    public:
        cbMimePlugin();

        /** @brief Can a file be handled by this plugin?
          *
          * @param filename The file in question.
          * @return The plugin should return true if it can handle this file,
          * false if not.
          */
        virtual bool CanHandleFile(const wxString& filename) const = 0;

        /** @brief Open the file.
          *
          * @param filename The file to open.
          * @return The plugin should return zero on success, other value on error.
          */
        virtual int OpenFile(const wxString& filename) = 0;

        /** @brief Is this a default handler?
          *
          * This is a flag notifying the main app that this plugin can handle
          * every file passed to it. Usually you 'll want to return false in
          * this function, because you usually create specialized handler
          * plugins (for specific MIME types)...
          *
          * @return True if this plugin can handle every possible MIME type,
          * false if not.
          */
        virtual bool HandlesEverything() const = 0;
    private:
        // "Hide" some virtual members, that are not needed in cbMimePlugin
        void BuildMenu(wxMenuBar* menuBar){}
        void RemoveMenu(wxMenuBar* menuBar){}
        void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0){}
        bool BuildToolBar(wxToolBar* toolBar){ return false; }
        void RemoveToolBar(wxToolBar* toolBar){}
};

/** @brief Base class for code-completion plugins
  *
  * This interface is subject to change, so not much info here...
  */
class PLUGIN_EXPORT cbCodeCompletionPlugin : public cbPlugin
{
    public:
        cbCodeCompletionPlugin();
		virtual wxArrayString GetCallTips() = 0;
		virtual int CodeComplete() = 0;
		virtual void ShowCallTip() = 0;
};

/** @brief Base class for wizard plugins
  *
  * Wizard plugins are called by Code::Blocks when the user selects
  * "File->New...".
  *
  * A plugin of this type can support more than one wizard. Additionally,
  * each wizard can support new workspaces, new projects, new targets or new files.
  * The @c index used as a parameter to most of the functions, denotes 0-based index
  * of the project wizard to run.
  */
class PLUGIN_EXPORT cbWizardPlugin : public cbPlugin
{
    public:
        cbWizardPlugin();

        /** @return the number of template wizards this plugin contains */
        virtual int GetCount() const = 0;

        /** @param index the wizard index.
          * @return the output type of the specified wizard at @c index */
        virtual TemplateOutputType GetOutputType(int index) const = 0;

        /** @param index the wizard index.
          * @return the template's title */
        virtual wxString GetTitle(int index) const = 0;

        /** @param index the wizard index.
          * @return the template's description */
        virtual wxString GetDescription(int index) const = 0;

        /** @param index the wizard index.
          * @return the template's category (GUI, Console, etc; free-form text). Try to adhere to standard category names... */
        virtual wxString GetCategory(int index) const = 0;

        /** @param index the wizard index.
          * @return the template's bitmap */
        virtual const wxBitmap& GetBitmap(int index) const = 0;

        /** @param index the wizard index.
          * @return this wizard's script filename (if this wizard is scripted). */
        virtual wxString GetScriptFilename(int index) const = 0;

        /** When this is called, the wizard must get to work ;).
          * @param index the wizard index.
          * @param createdFilename if provided, on return it should contain the main filename
          *                         this wizard created. If the user created a project, that
          *                         would be the project's filename.
          *                         If the wizard created a build target, that would be an empty string.
          *                         If the wizard created a file, that would be the file's name.
          * @return a pointer to the generated cbProject or ProjectBuildTarget. NULL for everything else (failure too).
          * You should dynamic-cast this to the correct type based on GetOutputType() 's value. */
        virtual CompileTargetBase* Launch(int index, wxString* createdFilename = 0) = 0; // do your work ;)
    private:
        // "Hide" some virtual members, that are not needed in cbCreateWizardPlugin
        void BuildMenu(wxMenuBar* menuBar){}
        void RemoveMenu(wxMenuBar* menuBar){}
        void BuildModuleMenu(const ModuleType type, wxMenu* menu, const FileTreeData* data = 0){}
        bool BuildToolBar(wxToolBar* toolBar){ return false; }
        void RemoveToolBar(wxToolBar* toolBar){}
};

/** @brief Plugin registration object.
  *
  * Use this class to register your new plugin with Code::Blocks.
  * All you have to do is instantiate a PluginRegistrant object.
  * @par
  * Example code to use in one of your plugin's source files (supposedly called "MyPlugin"):
  * @code
  * namespace
  * {
  *     PluginRegistrant<MyPlugin> registration("MyPlugin");
  * }
  * @endcode
  */
template<class T> class PluginRegistrant
{
    public:
        /// @param name The plugin's name.
        PluginRegistrant(const wxString& name)
        {
            Manager::Get()->GetPluginManager()->RegisterPlugin(name, // plugin's name
                                                                &CreatePlugin, // creation
                                                                &FreePlugin, // destruction
                                                                &SDKVersion); // SDK version
        }

        static cbPlugin* CreatePlugin()
        {
            return new T;
        }

        static void FreePlugin(cbPlugin* plugin)
        {
            delete plugin;
        }

        static void SDKVersion(int* major, int* minor, int* release)
        {
            if (major) *major = PLUGIN_SDK_VERSION_MAJOR;
            if (minor) *minor = PLUGIN_SDK_VERSION_MINOR;
            if (release) *release = PLUGIN_SDK_VERSION_RELEASE;
        }
};

#endif // CBPLUGIN_H
