// SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
// SPDX-License-Identifier: LicenseRef-Apache-2.0
// SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
// SPDX-License-Identifier: GPL-3.0-or-later

#ifndef ExprFunc_h
#define ExprFunc_h

#include "Vec.h"
#include <vector>

#include "ExprEnv.h"
#include "ExprFuncStandard.h"
#include "ExprFuncX.h"
#include "ExprType.h"


namespace KSeExpr
{
//! Function Definition, used in parse tree and func table.
/** This class in a static setting manages all builtin functions defined by
    SeExpr internally.  These can be queried
    by name for documentation.

    Users can create their own custom functions by creating one of these with the appropriate
    argument template. Any function that doesn't work within the given templates
    can be written using a ExprFuncX template instead

    Note: If you use the convenience prototypes instead of ExprFuncX, the
    user defined function will be assumed to be thread safe. If you have a
    thread unsafe function be sure to use ExprFuncX and call the base constructor
    with false.
*/
class ExprFunc
{
    static void initInternal(); // call to define built-in funcs
public:
    //! call to define built-in funcs
    static void init();
    //! cleanup all functions
    static void cleanup();

    /* A pointer to the define func is passed to the init method of
       expression plugins.  This should be called instead of calling
       the static method directly so that the plugin will work if the
       expression library is statically linked. */
    static void define(const char *name, const ExprFunc &f, const char *docString);
    static void define(const char *name, const ExprFunc &f);
    using Define = void (*)(const char *, const ExprFunc &);
    using Define3 = void (*)(const char *, const ExprFunc &, const char *);

    //! Lookup a builtin function by name
    static const ExprFunc *lookup(const std::string &name);

    //! Get a list of registered builtin and DSO generated functions
    static void getFunctionNames(std::vector<std::string> &names);

    //! Get doc string for a specific function
    static std::string getDocString(const char *functionName);

    //! Get the total size estimate of all plugins
    static size_t sizeInBytes();

    //! Dump statistics
    static Statistics statistics();

    // bool isScalar() const { return _scalar; };

    ExprFunc() = default;

    //! User defined function with custom argument parsing
    ExprFunc(ExprFuncX &f, int min = 1, int max = 1)
        : _func(&f)
        , _minargs(min)
        , _maxargs(max) {};

    ExprFunc(ExprFuncStandard::Func0 *f)
        : _standardFunc(ExprFuncStandard::FUNC0, (void *)f)
    {
    }
    ExprFunc(ExprFuncStandard::Func1 *f)
        : _standardFunc(ExprFuncStandard::FUNC1, (void *)f)
        , _minargs(1)
        , _maxargs(1)
    {
    }
    ExprFunc(ExprFuncStandard::Func2 *f)
        : _standardFunc(ExprFuncStandard::FUNC2, (void *)f)
        , _minargs(2)
        , _maxargs(2)
    {
    }
    ExprFunc(ExprFuncStandard::Func3 *f)
        : _standardFunc(ExprFuncStandard::FUNC3, (void *)f)
        , _minargs(3)
        , _maxargs(3)
    {
    }
    ExprFunc(ExprFuncStandard::Func4 *f)
        : _standardFunc(ExprFuncStandard::FUNC4, (void *)f)
        , _minargs(4)
        , _maxargs(4)
    {
    }
    ExprFunc(ExprFuncStandard::Func5 *f)
        : _standardFunc(ExprFuncStandard::FUNC5, (void *)f)
        , _minargs(5) // NOLINT readability-magic-numbers
        , _maxargs(5) // NOLINT readability-magic-numbers
    {
    }
    ExprFunc(ExprFuncStandard::Func6 *f)
        : _standardFunc(ExprFuncStandard::FUNC6, (void *)f)
        , _minargs(6) // NOLINT readability-magic-numbers
        , _maxargs(6) // NOLINT readability-magic-numbers
    {
    }
    ExprFunc(ExprFuncStandard::Funcn *f, int minArgs, int maxArgs)
        : _standardFunc(ExprFuncStandard::FUNCN, (void *)f)
        , _minargs(minArgs)
        , _maxargs(maxArgs)
    {
    }
    ExprFunc(ExprFuncStandard::Func1v *f)
        : _standardFunc(ExprFuncStandard::FUNC1V, (void *)f)
        , _minargs(1)
        , _maxargs(1)
    {
    }
    ExprFunc(ExprFuncStandard::Func2v *f)
        : _standardFunc(ExprFuncStandard::FUNC2V, (void *)f)
        , _minargs(2)
        , _maxargs(2)
    {
    }
    ExprFunc(ExprFuncStandard::Funcnv *f, int minArgs, int maxArgs)
        : _standardFunc(ExprFuncStandard::FUNCNV, (void *)f)
        , _minargs(minArgs)
        , _maxargs(maxArgs)
    {
    }
    ExprFunc(ExprFuncStandard::Func1vv *f)
        : _standardFunc(ExprFuncStandard::FUNC1VV, (void *)f)
        , _minargs(1)
        , _maxargs(1)
    {
    }
    ExprFunc(ExprFuncStandard::Func2vv *f)
        : _standardFunc(ExprFuncStandard::FUNC2VV, (void *)f)
        , _minargs(2)
        , _maxargs(2)
    {
    }
    ExprFunc(ExprFuncStandard::Funcnvv *f)
        : _standardFunc(ExprFuncStandard::FUNC1VV, (void *)f)
        , _minargs(1)
        , _maxargs(1)
    {
    }
    ExprFunc(ExprFuncStandard::Funcnvv *f, int minArgs, int maxArgs)
        : _standardFunc(ExprFuncStandard::FUNCNVV, (void *)f)
        , _minargs(minArgs)
        , _maxargs(maxArgs)
    {
    }

    //! return the minimum number of acceptable arguments
    int minArgs() const
    {
        return _minargs;
    }
    //! return the maximum number of acceptable arguments
    int maxArgs() const
    {
        return _maxargs;
    }
    //! return pointer to the funcx
    const ExprFuncX *funcx() const
    {
        return _func ? _func : &_standardFunc;
    }

private:
    ExprFuncStandard _standardFunc;
    ExprFuncX *_func {nullptr};
    int _minargs {0};
    int _maxargs {0};
};
} // namespace KSeExpr

#endif
