/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2020 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

#ifndef OSGEARTH_CAPABILITIES_H
#define OSGEARTH_CAPABILITIES_H 1

#include <osgEarth/Common>
#include <osgEarth/Version>
#include <osg/Uniform>
#include <osg/Texture>

namespace osgEarth
{
    /**
     * Stores information about the hardware and graphics system capbilities.
     * The osgEarth::Registry stores a singleton Capabilities object that you can
     * use to determine what your system supports.
     */
    class OSGEARTH_EXPORT Capabilities : public osg::Referenced
    {
    public:
        static const Capabilities& get();

        /** maximum # of texture image units exposed in a GPU fragment shader */
        int getMaxGPUTextureUnits() const { return _maxGPUTextureUnits; }

        /** maximum supported size (in pixels) of a texture */
        int getMaxTextureSize() const { return _maxTextureSize; }

        /** maximum texture size that doesn't cause a slowdown (vendor-specific) */
        int getMaxFastTextureSize() const { return _maxFastTextureSize; }

        /** whether the GPU supports shaders. */
        bool supportsGLSL() const { return _supportsGLSL; }

        /** whether the GPU supports a minimum GLSL version */
        bool supportsGLSL(float minimumVersion) const {
            return _supportsGLSL && _GLSLversion >= minimumVersion; }

        /** whether the GPU supports a minimum GLSL version (as an int; e.g. 1.2 => "120") */
        bool supportsGLSL(unsigned minVersionInt) const {
            return _supportsGLSL && ((unsigned)(_GLSLversion*100.0f)) >= minVersionInt; }

        /** the GLSL version */
        float getGLSLVersion() const { return _GLSLversion; }

        /** GLSL version as an integer x 100. I.e.: GLSL 1.4 => 140. */
        unsigned getGLSLVersionInt() const { return (unsigned)(_GLSLversion*100.0f + 0.5f); }

        /** Are we running OpenGLES? */
        bool isGLES() const { return _isGLES; }

        //! Reported GL_VENDOR
        const std::string& getVendor() const { return _vendor;}

        //! Reported GL_RENDERER
        const std::string& getRenderer() const { return _renderer;}

        //! the GL_VERSION string, which include the GL version, driver vendor, and driver version
        const std::string& getVersion() const { return _version; }

        /** whether the GPU supports DEPTH_PACKED_STENCIL buffer */
        bool supportsDepthPackedStencilBuffer() const { return _supportsDepthPackedStencilBuffer; }

        /** whether the GPU supports DrawInstanced rendering */
        bool supportsDrawInstanced() const { return _supportsDrawInstanced; }

        /** whether the GPU can handle non-power-of-two textures. */
        bool supportsNonPowerOfTwoTextures() const { return _supportsNonPowerOfTwoTextures; }

        /** number of logical CPUs available. */
        int getNumProcessors() const { return _numProcessors; }

        /** whether the GPU supports writing to the depth fragment */
        bool supportsFragDepthWrite() const { return _supportsFragDepthWrite; }

        /** whether the GPU supports a texture compression scheme */
        bool supportsTextureCompression(const osg::Texture::InternalFormatMode& mode) const;

        /** Maximum size of a texture buffer, when supportsTextureBuffer() is true. */
        int getMaxTextureBufferSize() const { return _maxTextureBufferSize; }

        /** whether OpenGL core profile is active */
        bool isCoreProfile() const { return _isCoreProfile; }

        /** whether OpenGL supports vertex array objects */
        bool supportsVertexArrayObjects() const { return _supportsVertexArrayObjects; }

        //! Whether the driver support 64-bit integers int GLSL
        bool supportsInt64() const { return _supportsInt64; }
        
        //! Whether the NVIDIA GL4 extensions for unified memory, including
        //! bindless buffers and draw commands, are available
        bool supportsNVGL() const { return _supportsNVGL; }

    protected:
        Capabilities();

        /** dtor */
        virtual ~Capabilities() { }

    private:
        int  _maxGPUTextureUnits;
        int  _maxTextureSize;
        int  _maxFastTextureSize;
        bool _supportsGLSL;
        float _GLSLversion;
        bool _supportsDepthPackedStencilBuffer;
        bool _supportsDrawInstanced;
        bool _supportsNonPowerOfTwoTextures;
        int  _numProcessors;
        bool _supportsFragDepthWrite;
        std::string _vendor;
        std::string _renderer;
        std::string _version;

        bool _supportsS3TC;
        bool _supportsPVRTC;
        bool _supportsARBTC;
        bool _supportsETC;
        bool _supportsRGTC;
        bool _isGLES;
        int  _maxTextureBufferSize;
        bool _isCoreProfile;
        bool _supportsVertexArrayObjects;
        bool _supportsInt64;
        bool _supportsNVGL;

    public:
        friend class Registry;
    };
}

#endif // OSGEARTH_CAPABILITIES_H
