#ifndef Magnum_DebugOutput_h
#define Magnum_DebugOutput_h
/*
    This file is part of Magnum.

    Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016
              Vladimír Vondruš <mosra@centrum.cz>

    Permission is hereby granted, free of charge, to any person obtaining a
    copy of this software and associated documentation files (the "Software"),
    to deal in the Software without restriction, including without limitation
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included
    in all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    DEALINGS IN THE SOFTWARE.
*/

#ifndef MAGNUM_TARGET_WEBGL
/** @file
 * @brief Class @ref Magnum::DebugOutput, @ref Magnum::DebugMessage, @ref Magnum::DebugGroup
 */
#endif

#include <string>
#include <Corrade/Containers/ArrayView.h>

#include "Magnum/OpenGL.h"
#include "Magnum/Magnum.h"
#include "Magnum/visibility.h"

#ifdef MAGNUM_BUILD_DEPRECATED
#include <Corrade/Utility/Macros.h>
#endif

#ifndef MAGNUM_TARGET_WEBGL
namespace Magnum {

namespace Implementation { struct DebugState; }

/**
@brief Debug output

Manages OpenGL debug output. The debug messages are emitted either from driver
(such as GL error descriptions and various performance and optimization hints)
or from third party software and the application itself using @ref DebugMessage
and @ref DebugGroup, which can be also used to mark various portions of command
stream in various graphics debuggers, such as Apitrace or gDEBugger.

## Basic usage

Support for debug output is provided by OpenGL 4.3 or @extension{KHR,debug}
(desktop/ES extension, covered also by @es_extension{ANDROID,extension_pack_es31a}).
Subset of the functionality is provided also by @extension2{EXT,debug_marker}
(desktop/ES extensions) or @extension{GREMEDY,string_marker} (desktop only
extension).

With OpenGL 4.3 or @extension{KHR,debug} desktop/ES extension, the debug output
needs to be enabled first. It can be enabled globally using
@ref Platform::Sdl2Application::Configuration::Flag::Debug "Platform::*Application::Configuration::Flag::Debug"
when creating context or only for some portions of the code using
@ref Renderer::Feature::DebugOutput. If enabled globally, some OpenGL drivers
may provide additional debugging information. In addition to that you can
control the output at even finer granularity using @ref setEnabled().

You can gather the messages either through graphics debugger or in the
application itself by setting up message callback using @ref setCallback() or
@ref setDefaultCallback(). You might also want to enable
@ref Renderer::Feature::DebugOutputSynchronous. Example usage, completely with
@ref DebugGroup and @link DebugMessage @endlink:

@code
Renderer::enable(Renderer::Feature::DebugOutput);
Renderer::enable(Renderer::Feature::DebugOutputSynchronous);
DebugOutput::setDefaultCallback();

// Disable rather spammy "Buffer detailed info" debug messages on NVidia drivers
DebugOutput::setEnabled(DebugOutput::Source::Api, DebugOutput::Type::Other, {131185}, false);

{
    DebugGroup group{DebugGroup::Source::Application, 42, "Scene rendering"};

    DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker,
        1337, DebugOutput::Severity::Notification, "Rendering transparent mesh");

    Renderer::enable(Renderer::Feature::Blending);
    mesh.draw(shader);
    Renderer::disable(Renderer::Feature::Blending);

    // ...
}
@endcode

With default callback the group entering/leaving and the inserted message (and
possibly also other messages) will be printed on standard output:

> Debug output: application debug group enter (42): Scene rendering\n
> Debug output: application marker (1337): Rendering transparent mesh\n
> ...\n
> Debug output: application debug group leave (42): Scene rendering

If only @extension2{EXT,debug_marker} or @extension{GREMEDY,string_marker} are
supported, only user-inserted messages and debug groups are supported and they
can be seen only through graphics debugger.

If OpenGL 4.3 is not supported and neither @extension{KHR,debug} nor
@extension2{EXT,debug_marker} nor @extension{GREMEDY,string_marker} are
available, all the functions are essentially a no-op.

Besides inserting messages into GL command stream you can also annotate OpenGL
objects with labels. See @ref AbstractQuery::setLabel(),
@ref AbstractShaderProgram::setLabel(), @ref AbstractTexture::setLabel(),
@ref Buffer::setLabel(), @ref Framebuffer::setLabel(), @ref Mesh::setLabel(),
@ref Renderbuffer::setLabel(), @ref Shader::setLabel() and
@ref TransformFeedback::setLabel() for more information.

@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_EXPORT DebugOutput {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref setEnabled()
         */
        enum class Source: GLenum {
            /** OpenGL */
            #ifndef MAGNUM_TARGET_GLES
            Api = GL_DEBUG_SOURCE_API,
            #else
            Api = GL_DEBUG_SOURCE_API_KHR,
            #endif

            /** Window system (GLX, WGL) */
            #ifndef MAGNUM_TARGET_GLES
            WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM,
            #else
            WindowSystem = GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR,
            #endif

            /** Shader compiler */
            #ifndef MAGNUM_TARGET_GLES
            ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER,
            #else
            ShaderCompiler = GL_DEBUG_SOURCE_SHADER_COMPILER_KHR,
            #endif

            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES
            Application = GL_DEBUG_SOURCE_APPLICATION,
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR,
            #endif

            /** Any other source */
            #ifndef MAGNUM_TARGET_GLES
            Other = GL_DEBUG_SOURCE_OTHER
            #else
            Other = GL_DEBUG_SOURCE_OTHER_KHR
            #endif
        };

        /**
         * @brief Message type
         *
         * @see @ref setEnabled()
         */
        enum class Type: GLenum {
            /** OpenGL error */
            #ifndef MAGNUM_TARGET_GLES
            Error = GL_DEBUG_TYPE_ERROR,
            #else
            Error = GL_DEBUG_TYPE_ERROR_KHR,
            #endif

            /** Behavior that has been marked for deprecation */
            #ifndef MAGNUM_TARGET_GLES
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR,
            #else
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR,
            #endif

            /** Behavior that is undefined according to the specification */
            #ifndef MAGNUM_TARGET_GLES
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR,
            #else
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR,
            #endif

            /** Non-portable usage of extensions or shaders */
            #ifndef MAGNUM_TARGET_GLES
            Portability = GL_DEBUG_TYPE_PORTABILITY,
            #else
            Portability = GL_DEBUG_TYPE_PORTABILITY_KHR,
            #endif

            /** Implementation-dependent performance warning */
            #ifndef MAGNUM_TARGET_GLES
            Performance = GL_DEBUG_TYPE_PERFORMANCE,
            #else
            Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR,
            #endif

            /** Annotation of the command stream */
            #ifndef MAGNUM_TARGET_GLES
            Marker = GL_DEBUG_TYPE_MARKER,
            #else
            Marker = GL_DEBUG_TYPE_MARKER_KHR,
            #endif

            /** Entering a debug group */
            #ifndef MAGNUM_TARGET_GLES
            PushGroup = GL_DEBUG_TYPE_PUSH_GROUP,
            #else
            PushGroup = GL_DEBUG_TYPE_PUSH_GROUP_KHR,
            #endif

            /** Leaving a debug group */
            #ifndef MAGNUM_TARGET_GLES
            PopGroup = GL_DEBUG_TYPE_POP_GROUP,
            #else
            PopGroup = GL_DEBUG_TYPE_POP_GROUP_KHR,
            #endif

            /** Any other type */
            #ifndef MAGNUM_TARGET_GLES
            Other = GL_DEBUG_TYPE_OTHER,
            #else
            Other = GL_DEBUG_TYPE_OTHER_KHR,
            #endif
        };

        /**
         * @brief Message severity
         *
         * @see @ref setEnabled()
         */
        enum class Severity: GLenum {
            /**
             * Any OpenGL error, dangerous undefined behavior, shader
             * compilation errors.
             */
            #ifndef MAGNUM_TARGET_GLES
            High = GL_DEBUG_SEVERITY_HIGH,
            #else
            High = GL_DEBUG_SEVERITY_HIGH_KHR,
            #endif

            /**
             * Severe performance warnings, shader compilation warnings, use of
             * deprecated behavior.
             */
            #ifndef MAGNUM_TARGET_GLES
            Medium = GL_DEBUG_SEVERITY_MEDIUM,
            #else
            Medium = GL_DEBUG_SEVERITY_MEDIUM_KHR,
            #endif

            /** Minor performance warnings, trivial undefined behavior. */
            #ifndef MAGNUM_TARGET_GLES
            Low = GL_DEBUG_SEVERITY_LOW,
            #else
            Low = GL_DEBUG_SEVERITY_LOW_KHR,
            #endif

            /** Any message other than error or performance warning. */
            #ifndef MAGNUM_TARGET_GLES
            Notification = GL_DEBUG_SEVERITY_NOTIFICATION
            #else
            Notification = GL_DEBUG_SEVERITY_NOTIFICATION_KHR
            #endif
        };

        /**
         * @brief Debug callback
         *
         * @see @ref setCallback()
         */
        typedef void(*Callback)(Source, Type, UnsignedInt, Severity, const std::string&, const void*);

        /**
         * @brief Max count of debug messages in log
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 is not supported and @extension{KHR,debug}
         * desktop or ES extension (covered also by @es_extension{ANDROID,extension_pack_es31a})
         * is not available, returns `0`.
         * @see @fn_gl{Get} with @def_gl{MAX_DEBUG_LOGGED_MESSAGES}
         */
        static Int maxLoggedMessages();

        /**
         * @brief Max debug message length
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 is not supported and @extension{KHR,debug}
         * desktop or ES extension (covered also by @es_extension{ANDROID,extension_pack_es31a})
         * is not available, returns `0`.
         * @see @fn_gl{Get} with @def_gl{MAX_DEBUG_MESSAGE_LENGTH}
         */
        static Int maxMessageLength();

        /**
         * @brief Enable or disable particular output type
         *
         * @attention If any @ref DebugGroup is active when making this call,
         *      the setting will be remembered only for the time in which the
         *      group is active and leaving it will revert the setting to state
         *      set in parent debug group. See @ref DebugGroup documentation
         *      for more information.
         *
         * If OpenGL 4.3 is not supported and @extension{KHR,debug} desktop or
         * ES extension (covered also by @es_extension{ANDROID,extension_pack_es31a})
         * is not available, this function does nothing.
         * @see @ref Renderer::Feature::DebugOutput, @fn_gl{DebugMessageControl}
         */
        static void setEnabled(Source source, Type type, std::initializer_list<UnsignedInt> ids, bool enabled) {
            setEnabledInternal(GLenum(source), GLenum(type), GL_DONT_CARE, ids, enabled);
        }

        /** @overload */
        static void setEnabled(Source source, Type type, Severity severity, bool enabled) {
            setEnabledInternal(GLenum(source), GLenum(type), GLenum(severity), {}, enabled);
        }

        /** @overload */
        static void setEnabled(Source source, Type type, bool enabled) {
            setEnabledInternal(GLenum(source), GLenum(type), GL_DONT_CARE, {}, enabled);
        }

        /** @overload */
        static void setEnabled(Source source, Severity severity, bool enabled) {
            setEnabledInternal(GLenum(source), GL_DONT_CARE, GLenum(severity), {}, enabled);
        }

        /** @overload */
        static void setEnabled(Source source, bool enabled) {
            setEnabledInternal(GLenum(source), GL_DONT_CARE, GL_DONT_CARE, {}, enabled);
        }

        /** @overload */
        static void setEnabled(Type type, Severity severity, bool enabled) {
            setEnabledInternal(GL_DONT_CARE, GLenum(type), GLenum(severity), {}, enabled);
        }

        /** @overload */
        static void setEnabled(Type type, bool enabled) {
            setEnabledInternal(GL_DONT_CARE, GLenum(type), GL_DONT_CARE, {}, enabled);
        }

        /** @overload */
        static void setEnabled(Severity severity, bool enabled) {
            setEnabledInternal(GL_DONT_CARE, GL_DONT_CARE, GLenum(severity), {}, enabled);
        }

        /** @overload */
        static void setEnabled(bool enabled) {
            setEnabledInternal(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, {}, enabled);
        }

        /**
         * @brief Set debug message callback
         *
         * The messages are sent to the callback only if
         * @ref Renderer::Feature::DebugOutput is enabled. If OpenGL 4.3 is not
         * supported and @extension{KHR,debug} desktop or ES extension (covered
         * also by @es_extension{ANDROID,extension_pack_es31a}) is not
         * available, this function does nothing.
         * @see @ref setDefaultCallback(),
         *      @ref Renderer::Feature::DebugOutputSynchronous,
         *      @fn_gl{DebugMessageCallback}
         */
        static void setCallback(Callback callback, const void* userParam = nullptr);

        /**
         * @brief Set default debug message callback
         *
         * See @ref setCallback() for more information. The message is printed
         * to @ref Corrade::Utility::Debug "Debug" output in the following
         * format:
         * @code
         * DebugMessage::insert(DebugMessage::Source::Application,
         *      DebugMessage::Type::Marker, 1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!");
         * @endcode
         *
         * > Debug output: application marker (1337): Hello from OpenGL command stream!
         */
        static void setDefaultCallback();

        /** @brief There's no point in having an instance of this class */
        DebugOutput() = delete;

    private:
        static void setEnabledInternal(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);
        static MAGNUM_LOCAL void controlImplementationNoOp(GLenum, GLenum, GLenum, std::initializer_list<UnsignedInt>, bool);
        static MAGNUM_LOCAL void controlImplementationKhr(GLenum source, GLenum type, GLenum severity, std::initializer_list<UnsignedInt> ids, bool enabled);

        static MAGNUM_LOCAL void callbackImplementationNoOp(Callback, const void*);
        static MAGNUM_LOCAL void callbackImplementationKhr(Callback callback, const void* userParam);
};

/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Source} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Source value);

/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Type} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Type value);

/** @debugoperatorclassenum{Magnum::DebugOutput,Magnum::DebugOutput::Severity} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugOutput::Severity value);

/**
@brief Debug message

Allows inserting messages GL command stream with labels, useful for example
with conjunction with various graphics debuggers, such as Apitrace or
gDEBugger.

## Basic usage

See @ref DebugOutput for introduction.

If OpenGL 4.3 is supported or @extension{KHR,debug} desktop or ES extension
(covered also by @es_extension{ANDROID,extension_pack_es31a}) is available and
default debug output callback is enabled for given kind of messages, the
inserted message will be printed on standard output in the following form:

@code
DebugMessage::insert(DebugMessage::Source::Application, DebugMessage::Type::Marker,
    1337, DebugOutput::Severity::Notification, "Hello from OpenGL command stream!");
@endcode

> Debug output: application marker (1337): Hello from OpenGL command stream!

If only @extension2{EXT,debug_marker} or @extension{GREMEDY,string_marker} are
available, the message can be seen only through graphics debugger.

If OpenGL 4.3 is not supported and neither @extension{KHR,debug} nor
@extension2{EXT,debug_marker} nor @extension{GREMEDY,string_marker} are
available, the function is essentially a no-op.

## Performance notes

If you ensure that you always use the `const char` overload of @ref insert()
and the debug output is either not supported or turned off, the calls will not
result in any allocations and thus won't have any negative performance effects.

@see @ref DebugGroup

@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_EXPORT DebugMessage {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref insert()
         */
        enum class Source: GLenum {
            #ifdef MAGNUM_BUILD_DEPRECATED
            /** @copydoc DebugOutput::Source::Api
             * @deprecated Use @ref DebugOutput::Source::Api instead.
             */
            Api = GLenum(DebugOutput::Source::Api),

            /** @copydoc DebugOutput::Source::WindowSystem
             * @deprecated Use @ref DebugOutput::Source::WindowSystem instead.
             */
            WindowSystem = GLenum(DebugOutput::Source::WindowSystem),

            /** @copydoc DebugOutput::Source::ShaderCompiler
             * @deprecated Use @ref DebugOutput::Source::ShaderCompiler instead.
             */
            ShaderCompiler = GLenum(DebugOutput::Source::ShaderCompiler),
            #endif

            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES
            Application = GL_DEBUG_SOURCE_APPLICATION,
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR,
            #endif

            #ifdef MAGNUM_BUILD_DEPRECATED
            /** @copydoc DebugOutput::Source::Other
             * @deprecated Use @ref DebugOutput::Source::Other instead.
             */
            Other = GLenum(DebugOutput::Source::Other)
            #endif
        };

        /**
         * @brief Message type
         *
         * @see @ref insert()
         */
        enum class Type: GLenum {
            /** OpenGL error */
            #ifndef MAGNUM_TARGET_GLES
            Error = GL_DEBUG_TYPE_ERROR,
            #else
            Error = GL_DEBUG_TYPE_ERROR_KHR,
            #endif

            /** Behavior that has been marked for deprecation */
            #ifndef MAGNUM_TARGET_GLES
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR,
            #else
            DeprecatedBehavior = GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR,
            #endif

            /** Behavior that is undefined according to the specification */
            #ifndef MAGNUM_TARGET_GLES
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR,
            #else
            UndefinedBehavior = GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR,
            #endif

            /** Non-portable usage of extensions or shaders */
            #ifndef MAGNUM_TARGET_GLES
            Portability = GL_DEBUG_TYPE_PORTABILITY,
            #else
            Portability = GL_DEBUG_TYPE_PORTABILITY_KHR,
            #endif

            /** Implementation-dependent performance warning */
            #ifndef MAGNUM_TARGET_GLES
            Performance = GL_DEBUG_TYPE_PERFORMANCE,
            #else
            Performance = GL_DEBUG_TYPE_PERFORMANCE_KHR,
            #endif

            /** Annotation of the command stream */
            #ifndef MAGNUM_TARGET_GLES
            Marker = GL_DEBUG_TYPE_MARKER,
            #else
            Marker = GL_DEBUG_TYPE_MARKER_KHR,
            #endif

            /** Any other type */
            #ifndef MAGNUM_TARGET_GLES
            Other = GL_DEBUG_TYPE_OTHER
            #else
            Other = GL_DEBUG_TYPE_OTHER_KHR
            #endif
        };

        #ifdef MAGNUM_BUILD_DEPRECATED
        /** @copybrief DebugOutput::Severity
         * @deprecated Use @ref DebugOutput::Severity instead.
         */
        CORRADE_DEPRECATED("use DebugOutput::Severity instead") typedef DebugOutput::Severity Severity;

        /** @copybrief DebugOutput::Callback
         * @deprecated Use @ref DebugOutput::Callback instead.
         */
        /* Can't mark this as deprecated because compiler then complains when I use it as a parameter in setCallback() */
        typedef void(*Callback)(DebugMessage::Source, DebugMessage::Type, UnsignedInt, DebugOutput::Severity, const std::string&, const void*);

        /** @copybrief DebugOutput::maxLoggedMessages()
         * @deprecated Use @ref DebugOutput::maxLoggedMessages() instead.
         */
        CORRADE_DEPRECATED("use DebugOutput::maxLoggedMessages() instead") static Int maxLoggedMessages() {
            return DebugOutput::maxLoggedMessages();
        }

        /** @copybrief DebugOutput::maxMessageLength()
         * @deprecated Use @ref DebugOutput::maxMessageLength() instead.
         */
        CORRADE_DEPRECATED("use DebugOutput::maxMessageLength() instead") static Int maxMessageLength() {
            return DebugOutput::maxMessageLength();
        }

        /** @copybrief DebugOutput::setEnabled()
         * @deprecated Use @ref DebugOutput::setEnabled() instead.
         */
        #ifdef DOXYGEN_GENERATING_OUTPUT
        template<class ...T> static void setEnabled(T... args);
        #else
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, std::initializer_list<UnsignedInt> ids, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), ids, enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, DebugOutput::Severity severity, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), severity, enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, Type type, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Source(source), DebugOutput::Type(type), enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, DebugOutput::Severity severity, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Source(source), severity, enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Source source, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Source(source), enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Type type, DebugOutput::Severity severity, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Type(type), severity, enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(Type type, bool enabled) {
            DebugOutput::setEnabled(DebugOutput::Type(type), enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(DebugOutput::Severity severity, bool enabled) {
            DebugOutput::setEnabled(severity, enabled);
        }
        CORRADE_DEPRECATED("use DebugOutput::setEnabled() instead") static void setEnabled(bool enabled) {
            DebugOutput::setEnabled(enabled);
        }
        #endif

        /** @copybrief DebugOutput::setCallback()
         * @deprecated Use @ref DebugOutput::setCallback() instead.
         */
        CORRADE_DEPRECATED("use DebugOutput::setCallback() instead") static void setCallback(Callback callback, const void* userParam = nullptr) {
            DebugOutput::setCallback(reinterpret_cast<DebugOutput::Callback>(callback), userParam);
        }

        /** @copybrief DebugOutput::setDefaultCallback()
         * @deprecated Use @ref DebugOutput::setDefaultCallback() instead.
         */
        CORRADE_DEPRECATED("use DebugOutput::setDefaultCallback() instead") static void setDefaultCallback() {
            DebugOutput::setDefaultCallback();
        }
        #endif

        /**
         * @brief Insert message
         * @param source    Message source
         * @param type      Message type
         * @param id        Message-specific ID
         * @param severity  Message severity
         * @param string    The actual message
         *
         * If OpenGL 4.3 is not supported and neither @extension{KHR,debug}
         * (covered also by @es_extension{ANDROID,extension_pack_es31a}) nor
         * @extension2{EXT,debug_marker} (desktop or ES extensions) nor
         * @extension{GREMEDY,string_marker} (desktop only extension) are
         * available, this function does nothing.
         *
         * If @extension{KHR,debug} is not available and only @extension2{EXT,debug_marker}
         * or @extension{GREMEDY,string_marker} are available, only @p string
         * is used and all other parameters are ignored.
         * @see @ref DebugOutput::maxMessageLength(), @fn_gl{DebugMessageInsert},
         *      @fn_gl_extension2{InsertEventMarker,EXT,debug_marker} or
         *      @fn_gl_extension{StringMarker,GREMEDY,string_marker}
         */
        static void insert(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, const std::string& string) {
            insertInternal(source, type, id, severity, {string.data(), string.size()});
        }

        /** @overload */
        template<std::size_t size> static void insert(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, const char(&string)[size]) {
            insertInternal(source, type, id, severity, {string, size - 1});
        }

        /** @brief There's no point in having an instance of this class */
        DebugMessage() = delete;

    private:
        static void insertInternal(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView<const char> string);
        static MAGNUM_LOCAL void insertImplementationNoOp(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char>);
        static MAGNUM_LOCAL void insertImplementationKhr(Source source, Type type, UnsignedInt id, DebugOutput::Severity severity, Containers::ArrayView<const char> string);
        static MAGNUM_LOCAL void insertImplementationExt(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char> string);
        #ifndef MAGNUM_TARGET_GLES
        static MAGNUM_LOCAL void insertImplementationGremedy(Source, Type, UnsignedInt, DebugOutput::Severity, Containers::ArrayView<const char> string);
        #endif
};

/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Source} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Source value);

/** @debugoperatorclassenum{Magnum::DebugMessage,Magnum::DebugMessage::Type} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugMessage::Type value);

/**
@brief Debug group

Allows marking portions of GL command stream with labels, useful for example
with conjunction with various graphics debuggers, such as Apitrace or gDEBugger.

## Basic usage

See @ref DebugOutput for introduction.

Easiest way is to push debug group by creating instance and pop it
automatically at the end of scope:
@code
{
    // Push debug group
    DebugGroup group{DebugGroup::Source::Application, 42, "Scene rendering"};

    Renderer::enable(Renderer::Feature::Blending);
    mesh.draw(shader);
    Renderer::disable(Renderer::Feature::Blending);

    // The debug group is popped automatically at the end of the scope
}
@endcode

If, for some reason, you need to pop in different scope, you can call @ref push()
and @ref pop() manually:
@code
DebugGroup group;

group.push(DebugGroup::Source::Application, 42, "Scene rendering");

Renderer::enable(Renderer::Feature::Blending);
mesh.draw(shader);
Renderer::disable(Renderer::Feature::Blending);

group.pop();
@endcode

If OpenGL 4.3 is supported or @extension{KHR,debug} desktop or ES extension
(covered also by @es_extension{ANDROID,extension_pack_es31a}) is available and
the default debug output callback is enabled for these kinds of messages, the
group entering and leaving will be printed on standard output in the following
form:

> Debug output: application debug group enter (42): Scene rendering\n
> Debug output: application debug group leave (42): Scene rendering

If only @extension2{EXT,debug_marker} is available, the group can be seen only
through graphics debugger.

If OpenGL 4.3 is not supported and neither @extension{KHR,debug} nor
@extension2{EXT,debug_marker} are available, the functions are essentially a
no-op.

@attention To avoid accidental debug group stack overflow/underflow, you cannot
    call @ref push() again when the group is already pushed onto the stack,
    similarly for @ref pop(). So if you want to have nested debug groups, you
    need to create one instance for each level.

## Interaction with debug output volume control

Besides putting hierarchical messages in debug output, the group also affects
settings done by @ref DebugOutput::setEnabled(). Entering debug group inherits
the settings from previously active debug group, call to
@ref DebugOutput::setEnabled() will be remembered only for
the time in which given group is active and leaving it will revert the setting
to state set in parent debug group. No state is preserved, thus calling
@ref push() after previous @ref pop() will not restore settings done when the
group was active previously.

## Performance notes

If you ensure that you always use the `const char` overload of @ref push()
and the debug output is either not supported or turned off, the calls will not
result in any allocations and thus won't have any negative performance effects.

@see @ref DebugMessage

@requires_gles Debug output is not available in WebGL.
*/
class MAGNUM_EXPORT DebugGroup {
    friend Implementation::DebugState;

    public:
        /**
         * @brief Message source
         *
         * @see @ref DebugGroup(), @ref push()
         */
        enum class Source: GLenum {
            /** External debugger or third-party middleware */
            #ifndef MAGNUM_TARGET_GLES
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY,
            #else
            ThirdParty = GL_DEBUG_SOURCE_THIRD_PARTY_KHR,
            #endif

            /** The application */
            #ifndef MAGNUM_TARGET_GLES
            Application = GL_DEBUG_SOURCE_APPLICATION
            #else
            Application = GL_DEBUG_SOURCE_APPLICATION_KHR
            #endif
        };

        /**
         * @brief Max debug group stack depth
         *
         * The result is cached, repeated queries don't result in repeated
         * OpenGL calls. If OpenGL 4.3 is not supported and @extension{KHR,debug}
         * desktop or ES extension (covered also by @es_extension{ANDROID,extension_pack_es31a})
         * is not available, returns `0`.
         * @see @fn_gl{Get} with @def_gl{MAX_DEBUG_GROUP_STACK_DEPTH}
         */
        static Int maxStackDepth();

        /**
         * @brief Default constructor
         *
         * Doesn't do anything. Call @ref push() to enter debug group.
         */
        explicit DebugGroup(): _active{false} {}

        /**
         * @brief Constructor
         *
         * Calls @ref push().
         * @see @link ~DebugGroup() @endlink, @ref pop()
         */
        explicit DebugGroup(Source source, UnsignedInt id, const std::string& message): DebugGroup{} {
            push(source, id, message);
        }

        /** @overload */
        template<std::size_t size> explicit DebugGroup(Source source, UnsignedInt id, const char(&message)[size]): DebugGroup{} {
            push(source, id, message);
        }

        /**
         * @brief Destructor
         *
         * If the group is active, calls @ref pop().
         */
        ~DebugGroup() { if(_active) pop(); }

        /**
         * @brief Push debug group onto the stack
         *
         * Expects that the group isn't already pushed on the stack. The group
         * entering message is put into debug output with
         * @ref DebugOutput::Type::PushGroup and
         * @ref DebugOutput::Severity::Notification.
         *
         * If OpenGL 4.3 is not supported and neither @extension{KHR,debug}
         * (covered also by @es_extension{ANDROID,extension_pack_es31a}) nor
         * @extension2{EXT,debug_marker} is available, this function does
         * nothing. If @extension{KHR,debug} is not available and only
         * @extension2{EXT,debug_marker} is available, only @p message is used
         * and all other parameters are ignored.
         * @see @ref pop(), @ref maxStackDepth(), @ref DebugOutput::maxMessageLength(),
         *      @ref Renderer::Error::StackOverflow, @fn_gl{PushDebugGroup} or
         *      @fn_gl_extension2{PushGroupMarker,EXT,debug_marker}
         */
        void push(Source source, UnsignedInt id, const std::string& message) {
            pushInternal(source, id, {message.data(), message.size()});
        }

        /** @overload */
        template<std::size_t size> void push(Source source, UnsignedInt id, const char(&message)[size]) {
            pushInternal(source, id, {message, size - 1});
        }

        /**
         * @brief Pop debug group from the stack
         *
         * Expects that the group is currently pushed on the stack. Leaving the
         * group will also revert all @ref DebugOutput::setEnabled() settings
         * done when the group was active. See class documentation for more
         * information. The group leaving message is put into debug output with
         * @ref DebugOutput::Type::PopGroup and
         * @ref DebugOutput::Severity::Notification.
         *
         * If OpenGL 4.3 is not supported and neither @extension{KHR,debug}
         * (covered also by @es_extension{ANDROID,extension_pack_es31a}) nor
         * @extension2{EXT,debug_marker} is available, this function does
         * nothing.
         * @see @ref push(), @ref Renderer::Error::StackUnderflow,
         *      @fn_gl{PopDebugGroup} or
         *      @fn_gl_extension2{PopGroupMarker,EXT,debug_marker}
         */
        void pop();

    private:
        void pushInternal(Source source, UnsignedInt id, Containers::ArrayView<const char> message);

        static MAGNUM_LOCAL void pushImplementationNoOp(Source source, UnsignedInt id, Containers::ArrayView<const char> message);
        static MAGNUM_LOCAL void pushImplementationKhr(Source source, UnsignedInt id, Containers::ArrayView<const char> message);
        static MAGNUM_LOCAL void pushImplementationExt(Source source, UnsignedInt id, Containers::ArrayView<const char> message);

        static MAGNUM_LOCAL void popImplementationNoOp();
        static MAGNUM_LOCAL void popImplementationKhr();
        static MAGNUM_LOCAL void popImplementationExt();

        bool _active;
};

/** @debugoperatorclassenum{Magnum::DebugGroup,Magnum::DebugGroup::Source} */
MAGNUM_EXPORT Debug& operator<<(Debug& debug, DebugGroup::Source value);

}
#else
#error this header is not available in WebGL build
#endif

#endif
