//------------------------------------------------------------------------------
// emFpPlugin.h
//
// Copyright (C) 2006-2008,2010,2014,2018,2024-2025 Oliver Hamann.
//
// Homepage: http://eaglemode.sourceforge.net/
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License version 3 as published by the
// Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License version 3 for
// more details.
//
// You should have received a copy of the GNU General Public License version 3
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//------------------------------------------------------------------------------

#ifndef emFpPlugin_h
#define emFpPlugin_h

#ifndef emOwnPtrArray_h
#include <emCore/emOwnPtrArray.h>
#endif

#ifndef emPanel_h
#include <emCore/emPanel.h>
#endif

class emFpPlugin;


//==============================================================================
//=========================== Plugin function types ============================
//==============================================================================

extern "C" {
        typedef emPanel * (*emFpPluginFunc) (
                emPanel::ParentArg parent, const emString & name,
                const emString & path, emFpPlugin * plugin,
                emString * errorBuf
        );
                // Type of the plugin function of an emFpPlugin. Such a function
                // creates a panel for showing a file.
                // Arguments:
                //   parent   - Parent of the panel.
                //   name     - Name of the panel.
                //   path     - Path name of the file to be shown.
                //   plugin   - The plugin record (mainly for reading the
                //              plugin-defined properties).
                //   errorBuf - For returning an error message on failure.
                // Returns: The created panel, or NULL on failure.

        typedef bool (*emFpPluginModelFunc) (
                emContext & context, const char * className,
                const emString & name, bool common, emFpPlugin * plugin,
                emRef<emModel> * pResult, emString * errorBuf
        );
                // Type of the plugin model function of an emFpPlugin.
                // Such a function acquires a model of a desired class.
                // Arguments:
                //   context   - The context of the model.
                //   className - The class name or base class name of the model.
                //   name      - The name of the model (usually the file path).
                //   common    - true for refinding or creating a common model,
                //               false for creating a private model.
                //   plugin    - The plugin record (mainly for reading the
                //               plugin-defined properties).
                //   pResult   - Pointer for returning the acquired model.
                //   errorBuf  - For returning an error message on failure.
                // Returns: true if the acquired model has been assigned to
                //          *pResult, false on on failure.
}


//==============================================================================
//================================= emFpPlugin =================================
//==============================================================================

class emFpPlugin : public emStructRec {

public:

        // Record class for a file panel plugin. Such a plugin is able to create
        // a panel for showing (and maybe editing) a file. An instance of this
        // record class holds the configuration of such a plugin. It is usually
        // loaded from a configuration file (see emFpPluginList).

        emFpPlugin();
                // Construct empty.

        virtual ~emFpPlugin();
                // Destructor.


        // - - - - - Member Records - - - - -

        emTArrayRec<emStringRec> FileTypes;
                // Array of file types the plugin is able to handle. Each entry
                // must be a file name suffix including the leading dot, or the
                // special string "file" for accepting all regular files, or the
                // special string "directory" for accepting directories.

        emStringRec FileFormatName;
                // A display name for the file type(s).

        emDoubleRec Priority;
                // Priority of the plugin. If there are two plugins able to
                // handle a file, the one with the higher priority is taken
                // first.

        emStringRec Library;
                // Name of the dynamic library containing the plugin function
                // (just the pure name, read comments on emTryOpenLib).

        emStringRec Function;
                // Name of the plugin function. It must match the interface
                // defined by emFpPluginFunc.

        emStringRec ModelFunction;
                // Name of the plugin model function. It must match the
                // interface defined by emFpPluginModelFunc. This is optional
                // and can be left empty if the plugin does not provide such
                // function.

        emTArrayRec<emStringRec> ModelClasses;
                // Which classes are supported by the model function. This is an
                // array of class names which can be given to the className
                // parameter when calling the plugin model functions.

        emBoolRec ModelAbleToSave;
                // Whether the model acquired by the model function also
                // supports saving to the interfaced file.

        class PropertyRec : public emStructRec {
        public:
                PropertyRec();
                virtual ~PropertyRec();
                emStringRec Name;
                emStringRec Value;
        };
        emTArrayRec<PropertyRec> Properties;
                // Any number of plugin-defined properties in form of name/value
                // pairs.

        // - - - - - End of Member Records - - - - -


        PropertyRec * GetProperty(const char * name);
                // Search for a plugin-defined property. Returns NULL if not
                // found.

        emPanel * TryCreateFilePanel(
                emPanel::ParentArg parent, const emString & name,
                const emString & path
        );
                // Create a file panel via this plugin.
                // Arguments:
                //   parent - Parent of the panel.
                //   name   - Name of the panel.
                //   path   - Path name of the file to be shown.
                // Returns: The created panel.
                // Throws: An error message on failure.

        template <class MDL> emRef<MDL> TryAcquireModel(
                emContext & context, const char * className,
                const emString & name, bool common=true
        );
                // Acquire a model via this plugin. MDL must be emModel or a
                // derivation of that.
                // Arguments:
                //   context   - The context of the model.
                //   className - The class name or base class name of the model.
                //               This must match the template parameter MDL and
                //               usually has to be one of the names in
                //               ModelClasses.
                //   name      - The name of the model (usually the file path).
                //   common    - true for refinding or creating a common model,
                //               false for creating a private model.
                // Returns: The model (never NULL).
                // Throws: An error message on failure.

        virtual const char * GetFormatName() const;
                // The file format name of this record file format.

private:

        emRef<emModel> TryAcquireModelImpl(
                emContext & context, const char * className,
                const emString & name, bool common
        );

        emFpPluginFunc CachedFunc;
        emFpPluginModelFunc CachedModelFunc;
        emString CachedLibName;
        emString CachedFuncName;
        emString CachedModelFuncName;
};


//==============================================================================
//=============================== emFpPluginList ===============================
//==============================================================================

class emFpPluginList : public emModel {

public:

        // Class for a model containing a list of all the configured file panel
        // plugins. The plugin configurations are loaded from a certain
        // directory.

        static emRef<emFpPluginList> Acquire(emRootContext & rootContext);
                // Acquire the emFpPluginList.

        emPanel * CreateFilePanel(
                emPanel::ParentArg parent, const emString & name,
                const emString & path, int alternative=0
        );
                // Create a panel for a file. This calls the appropriate plugin.
                // On failure, a panel showing the error message is created.
                // Arguments:
                //   parent      - Parent of the panel.
                //   name        - Name of the panel.
                //   path        - Path name of the file to be shown.
                //   alternative - If there are multiple plugins able to show
                //                 the file, the one with the highest priority
                //                 is chosen if this argument is 0. If this
                //                 argument is 1, the one with the
                //                 second-highest priority is chosen, and so on.
                // Returns: The created panel.

        emPanel * CreateFilePanel(
                emPanel::ParentArg parent, const emString & name,
                const emString & absolutePath, int statErr, long statMode,
                int alternative=0
        );
                // This method exists for optimization. It's like above, but the
                // caller knows more about the file.
                // Arguments:
                //   parent       - Parent of the panel.
                //   name         - Name of the panel.
                //   absolutePath - Absolute path name of the file to be shown.
                //   statErr      - Zero if calling stat on the file was
                //                  successful, otherwise the resulting value of
                //                  errno.
                //   statMode     - st.st_mode from calling stat on the file.
                //   alternative  - Like with the above method.
                // Returns: The created panel.

        template <class MDL> emRef<MDL> TryAcquireModel(
                emContext & context, const char * className,
                const emString & name, bool nameIsFilePath=true,
                bool common=true, int alternative=0,
                long statMode=S_IFREG
        );
                // Acquire a model via a suitable plugin. MDL must be emModel or
                // a derivation of that. This searches for a plugin which can
                // acquire a model with the given model class name and which
                // optionally can handle the given file path. On success, the
                // plugin model function is called to acquire the model.
                // Arguments:
                //   context        - The context of the model.
                //   className      - The class name or base class name of the
                //                    model. This must match the template
                //                    parameter MDL. A plugin that has this name
                //                    in ModelClasses is searched for.
                //   name           - The name of the model.
                //   nameIsFilePath - If true, then the name is a file path
                //                    (which must be absolute!), and then only
                //                    plugins whose FileTypes matches that file
                //                    are considered. Otherwise only className
                //                    is used to choose a plugin.
                //   common         - true for refinding or creating a common
                //                    model, false for creating a private model.
                //   alternative    - If there are multiple matching plugins,
                //                    then the one with the highest priority is
                //                    chosen if this argument is 0. If this
                //                    argument is 1, the one with the
                //                    second-highest priority is chosen, and so
                //                    on.
                //   statMode       - This has to be S_IFREG to acquire the
                //                    model for a regular file, or S_IFDIR for a
                //                    directory. The parameter is ignored if
                //                    nameIsFilePath is false.
                // Returns: The model (never NULL).
                // Throws: An error message on failure, also if no suitable
                //         plugin found.

        emFpPlugin * SearchPlugin(
                const char * modelClassName=NULL, const char * filePath=NULL,
                bool requireAbleToSave=false, int alternative=0,
                long statMode=S_IFREG
        );
                // Search a plugin that fits the given criteria.
                // Arguments:
                //   modelClassName    - If not NULL, then only consider plugins
                //                       whose ModelClasses contains this name.
                //   filePath          - If not NULL, then only consider plugins
                //                       whose FileTypes matches that file.
                //   requireAbleToSave - Whether to consider only plugins where
                //                       ModelAbleToSave is true.
                //   alternative       - If there are multiple matching plugins,
                //                       then the one with the highest priority
                //                       is chosen if this argument is 0. If
                //                       this argument is 1, the one with the
                //                       second-highest priority is chosen, and
                //                       so on.
                //   statMode          - This has to be S_IFREG if filePath
                //                       denotes a regular file, or S_IFDIR for
                //                       a directory. The parameter is ignored
                //                       if filePath is NULL.
                // Returns: A pointer to the plugin or NULL if not found.

        emArray<emFpPlugin*> SearchPlugins(
                const char * modelClassName=NULL, const char * filePath=NULL,
                bool requireAbleToSave=false, long statMode=S_IFREG
        );
                // Same as SearchPlugin, but return all plugins which match
                // the criteria, sorted from highest priority to lowest. The
                // returned array never contains any NULL.

protected:

        emFpPluginList(emContext & context, const emString & name);
        virtual ~emFpPluginList();

private:

        emRef<emModel> TryAcquireModelImpl(
                emContext & context, const char * className,
                const emString & name, bool nameIsFilePath,
                bool common, int alternative, long statMode
        );

        static bool IsMatchingPlugin(
                const emFpPlugin & plugin, const char * modelClassName,
                const char * fileName, int fileNameLen,
                bool requireAbleToSave, long statMode
        );

        static int CmpReversePluginPriorities(
                const emFpPlugin * obj1, const emFpPlugin * obj2,
                void * context
        );

        emOwnPtrArray<emFpPlugin> Plugins;
                // Sorted by descending priority, secondly by file name.
};


//==============================================================================
//============================== Implementations ===============================
//==============================================================================

template <class MDL> emRef<MDL> emFpPlugin::TryAcquireModel(
        emContext & context, const char * className,
        const emString & name, bool common
)
{
        emRef<emModel> base=TryAcquireModelImpl(context,className,name,common);
        emRef<MDL> model=dynamic_cast<MDL*>(base.Get());
        if (!model) throw emException(
                "emFpPlugin::TryAcquireModel: dynamic cast failed for %s",
                className
        );
        return model;
}

template <class MDL> emRef<MDL> emFpPluginList::TryAcquireModel(
        emContext & context, const char * className,
        const emString & name, bool nameIsFilePath,
        bool common, int alternative, long statMode
)
{
        emRef<emModel> base=TryAcquireModelImpl(
                context,className,name,nameIsFilePath,common,alternative,statMode
        );
        emRef<MDL> model=dynamic_cast<MDL*>(base.Get());
        if (!model) throw emException(
                "emFpPluginList::TryAcquireModel: dynamic cast failed for %s",
                className
        );
        return model;
}


#endif