
#pragma once

#include <array>
#include <vector>
#include <functional>

#include <glbinding/glbinding_api.h>

#include <glbinding/ContextHandle.h>
#include <glbinding/Function.h>

#include <glbinding/gl/types.h>


namespace glbinding
{


/**
 * @brief
 *   The main interface to handle additional features to OpenGL functions besides regular function calls.
 *
 * Additional features include binding initialization (even for multi-threaded environments), additional function registration,
 * context switches (for multi-context environments) and basic reflection in form of accessors to the full list of functions.
 */
class GLBINDING_API Binding
{
public:
    using array_t = std::array<AbstractFunction *, {{functions.count}}>; ///< The type of the build-in functions collection.
    using ContextSwitchCallback = std::function<void(ContextHandle)>; ///< The signature of the context switch callback.

    /**
     * @brief
     *   Deleted Constructor as all functions are static.
     */
    Binding() = delete;

    /**
     * @brief
     *   Initializes the binding for the current active OpenGL context.
     *
     * @param[in] resolveFunctions (optional)
     *   Whether to resolve function pointers lazy (resolveFunctions = false) or immediately.
     *
     * After this call, the initialized context is already set active for the current thread.
     */
    static void initialize(bool resolveFunctions = true);
    
    /**
     * @brief
     *   Initializes the binding for the current active OpenGL context.
     *
     * @param[in] context
     *   The context handle of the context to initialize.
     * @param[in] useContext
     *   Whether to set the context active (useContext = true) after the initialization.
     * @param[in] resolveFunctions (optional)
     *   Whether to resolve function pointers lazy (resolveFunctions = false) or immediately.
     */
    static void initialize(ContextHandle context, bool useContext = true, bool resolveFunctions = true);
    
    /**
     * @brief
     *   Registers an additional function for the additional features.
     *
     * @param[in] function
     *   The function to register.
     *
     * The additional features are callbacks, and use in multi-context environments.
     */
    static void registerAdditionalFunction(AbstractFunction * function);

    /**
     * @brief
     *   Resolves the funtion pointers of all registered OpenGL functions immediately for the current context.
     */
    static void resolveFunctions();
    
    /**
     * @brief
     *   Update the current context state in glbinding.
     *   This function queries the driver for the current OpenGL context.
     */
    static void useCurrentContext();
    
    /**
     * @brief
     *   Update the current context state in glbinding.
     *
     * @param[in] context
     *   The context handle of the context to set current.
     */
    static void useContext(ContextHandle context);

    /**
     * @brief
     *   Removes the current context from the state of glbinding.
     *   This function queries the driver for the current OpenGL context.
     */
    static void releaseCurrentContext();
    
    /**
     * @brief
     *   Removes the current context from the state of glbinding.
     *
     * @param[in] context
     *   The context handle of the context to remove.
     */
    static void releaseContext(ContextHandle context);
    
    /**
     * @brief
     *   Registers an additional callback that gets called each time the context is switched using the useContext method.
     *
     * There may be multiple context switch callbacks registered at once.
     */
    static void addContextSwitchCallback(ContextSwitchCallback callback);

    /**
     * @brief
     *   The accessor for the number of all registered functions (build-in and additional).
     *
     * @return
     *   The number of all registered functions.
     */
    static size_t size();

    /**
     * @brief
     *   The accessor for all build-in functions.
     *
     * @return
     *   The list of all build-in functions.
     */
    static const array_t & functions();
    
    /**
     * @brief
     *   The accessor for all additional functions.
     *
     * @return
     *   The list of all additional functions.
     */
    static const std::vector<AbstractFunction *> & additionalFunctions();

public:
{{#functions.items}}
    {{#item}}static Function<{{>partials/general_typeNs}}{{^params.empty}}, {{>partials/general_paramSignatureNs}}{{/params.empty}}> {{identifierNoGl}};{{/item}}
{{/functions.items}}

protected:
	static const array_t s_functions; ///< The list of all build-in functions.
	static std::vector<AbstractFunction *> s_additionalFunctions; ///< The list of all additional functions;
	static std::vector<ContextSwitchCallback> s_callbacks; ///< The list of all currently registered context switch callbacks.
};


} // namespace glbinding
