/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006-2007 Rivo Laks <rivolaks@hot.ee>
Copyright (C) 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

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 for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/

#ifndef KWIN_GLUTILS_H
#define KWIN_GLUTILS_H

#include <kwinconfig.h> // KWIN_HAVE_OPENGL

#ifdef KWIN_HAVE_OPENGL
#include <kwinglutils_funcs.h>

#include <QtGui/QPixmap>

#include <QtGui/QImage>
#include <QtCore/QSize>
#include <QtCore/QSharedData>
#include <QtCore/QStack>

/** @addtogroup kwineffects */
/** @{ */

class QVector2D;
class QVector3D;
class QVector4D;
class QMatrix4x4;

template< class K, class V > class QHash;


namespace KWin
{


class GLTexture;
class GLVertexBuffer;
class GLVertexBufferPrivate;


// Initializes GLX function pointers
void KWIN_EXPORT initGLX();
// Initializes OpenGL stuff. This includes resolving function pointers as
//  well as checking for GL version and extensions
//  Note that GL context has to be created by the time this function is called
void KWIN_EXPORT initGL();
// Initializes EGL function pointers
void KWIN_EXPORT initEGL();
// Cleans up all resources hold by the GL Context
void KWIN_EXPORT cleanupGL();

// Number of supported texture units
extern KWIN_EXPORT int glTextureUnitsCount;


bool KWIN_EXPORT hasGLVersion(int major, int minor, int release = 0);
bool KWIN_EXPORT hasGLXVersion(int major, int minor, int release = 0);
bool KWIN_EXPORT hasEGLVersion(int major, int minor, int release = 0);
// use for both OpenGL and GLX extensions
bool KWIN_EXPORT hasGLExtension(const QString& extension);

// detect OpenGL error (add to various places in code to pinpoint the place)
bool KWIN_EXPORT checkGLError(const char* txt);

inline bool KWIN_EXPORT isPowerOfTwo(int x)
{
    return ((x & (x - 1)) == 0);
}
/**
 * @return power of two integer _greater or equal to_ x.
 *  E.g. nearestPowerOfTwo(513) = nearestPowerOfTwo(800) = 1024
 **/
int KWIN_EXPORT nearestPowerOfTwo(int x);

/**
 * Push a new matrix on the GL matrix stack.
 * In GLES this method is a noop. This method should be preferred over glPushMatrix
 * as it also handles GLES.
 * @since 4.7
 **/
KWIN_EXPORT void pushMatrix();
/**
 * Multiplies current matrix on GL stack with @p matrix and pushes the result on the matrix stack.
 * This method is the same as pushMatrix followed by multiplyMatrix.
 * In GLES this method is a noop. This method should be preferred over glPushMatrix
 * as it also handles GLES.
 * @param matrix The matrix the current matrix on the stack should be multiplied with.
 * @see pushMatrix
 * @see multiplyMatrix
 * @since 4.7
 **/
KWIN_EXPORT void pushMatrix(const QMatrix4x4 &matrix);
/**
 * Multiplies the current matrix on GL stack with @p matrix.
 * In GLES this method is a noop. This method should be preferred over glMultMatrix
 * as it also handles GLES.
 * @param matrix The matrix the current matrix on the stack should be multiplied with.
 * @since 4.7
 **/
KWIN_EXPORT void multiplyMatrix(const QMatrix4x4 &matrix);
/**
 * Replaces the current matrix on GL stack with @p matrix.
 * In GLES this method is a no-op. This method should be preferred over glLoadMatrix
 * as it also handles GLES.
 * @param matrix The new matrix to replace the existing one on the GL stack.
 * @since 4.7
 **/
KWIN_EXPORT void loadMatrix(const QMatrix4x4 &matrix);
/**
 * Pops the current matrix from the GL matrix stack.
 * In GLES this method is a noop. This method should be preferred over glPopMatrix
 * as it also handles GLES.
 * @since 4.7
 **/
KWIN_EXPORT void popMatrix();


class KWIN_EXPORT GLTexture
    : public QSharedData
{
public:
    GLTexture();
    explicit GLTexture(const QImage& image, GLenum target = GL_TEXTURE_2D);
    explicit GLTexture(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
    GLTexture(const QString& fileName);
    GLTexture(int width, int height);
    virtual ~GLTexture();

    bool isNull() const;
    QSize size() const;
    int width() const {
        return mSize.width();    /// @since 4.5
    }
    int height() const {
        return mSize.height();    /// @since 4.5
    }
    /**
     * @since 4.7
     **/
    bool isYInverted() const;

    virtual bool load(const QImage& image, GLenum target = GL_TEXTURE_2D);
    virtual bool load(const QPixmap& pixmap, GLenum target = GL_TEXTURE_2D);
    virtual bool load(const QString& fileName);
    virtual void discard();
    virtual void bind();
    virtual void unbind();
    void render(QRegion region, const QRect& rect);
    /**
     * Set up texture transformation matrix to automatically map unnormalized
     * texture coordinates (i.e. 0 to width, 0 to height, (0,0) is top-left)
     * to OpenGL coordinates. Automatically adjusts for different texture
     * types. This can be done only for one texture at a time, repeated
     * calls for the same texture are allowed though, requiring the same
     * amount of calls to disableUnnormalizedTexCoords().
     *
     * In OpenGL ES this method is a no-op. In core profile a shader should be used
     * @deprecated
     */
    void enableUnnormalizedTexCoords();
    /**
     * Disables transformation set up using enableUnnormalizedTexCoords().
     *
     * In OpenGL ES this method is a no-op. In core profile a shader should be used
     * @deprecated
     */
    void disableUnnormalizedTexCoords();
    /**
     * Set up texture transformation matrix to automatically map normalized
     * texture coordinates (i.e. 0 to 1, 0 to 1, (0,0) is bottom-left)
     * to OpenGL coordinates. Automatically adjusts for different texture
     * types. This can be done only for one texture at a time, repeated
     * calls for the same texture are allowed though, requiring the same
     * amount of calls to disableNormalizedTexCoords().
     *
     * In OpenGL ES this method is a no-op. In core profile a shader should be used
     * @deprecated
     */
    void enableNormalizedTexCoords();
    /**
     * Disables transformation set up using enableNormalizedTexCoords().
     *
     * In OpenGL ES this method is a no-op. In core profile a shader should be used
     * @deprecated
     */
    void disableNormalizedTexCoords();

    GLuint texture() const;
    GLenum target() const;
    GLenum filter() const;
    virtual bool isDirty() const;
    void setTexture(GLuint texture);
    void setTarget(GLenum target);
    void setFilter(GLenum filter);
    void setWrapMode(GLenum mode);
    virtual void setDirty();

    static void initStatic();
    static bool NPOTTextureSupported()  {
        return sNPOTTextureSupported;
    }
    static bool framebufferObjectSupported()  {
        return sFramebufferObjectSupported;
    }
    static bool saturationSupported()  {
        return sSaturationSupported;
    }

protected:
    void enableFilter();
    QImage convertToGLFormat(const QImage& img) const;

    GLuint mTexture;
    GLenum mTarget;
    GLenum mFilter;
    QSize mSize;
    QSizeF mScale; // to un-normalize GL_TEXTURE_2D
    bool y_inverted; // texture has y inverted
    bool can_use_mipmaps;
    bool has_valid_mipmaps;

private:
    void init();
    int mUnnormalizeActive; // 0 - no, otherwise refcount
    int mNormalizeActive; // 0 - no, otherwise refcount
    GLVertexBuffer* m_vbo;
    QSize m_cachedSize;

    static bool sNPOTTextureSupported;
    static bool sFramebufferObjectSupported;
    static bool sSaturationSupported;
    Q_DISABLE_COPY(GLTexture)
};

class KWIN_EXPORT GLShader
{
public:
    GLShader(const QString& vertexfile, const QString& fragmentfile);
    ~GLShader();

    bool isValid() const  {
        return mValid;
    }

    int uniformLocation(const char* name);

    bool setUniform(const char* name, float value);
    bool setUniform(const char* name, int value);
    bool setUniform(const char* name, const QVector2D& value);
    bool setUniform(const char* name, const QVector3D& value);
    bool setUniform(const char* name, const QVector4D& value);
    bool setUniform(const char* name, const QMatrix4x4& value);
    bool setUniform(const char* name, const QColor& color);

    bool setUniform(int location, float value);
    bool setUniform(int location, int value);
    bool setUniform(int location, const QVector2D &value);
    bool setUniform(int location, const QVector3D &value);
    bool setUniform(int location, const QVector4D &value);
    bool setUniform(int location, const QMatrix4x4 &value);
    bool setUniform(int location, const QColor &value);

    int attributeLocation(const char* name);
    bool setAttribute(const char* name, float value);
    /**
     * @return The value of the uniform as a matrix
     * @since 4.7
     **/
    QMatrix4x4 getUniformMatrix4x4(const char* name);

    enum MatrixUniform {
        TextureMatrix = 0,
        ProjectionMatrix,
        ModelViewMatrix,
        WindowTransformation,
        ScreenTransformation,
        MatrixCount
    };

    enum Vec2Uniform {
        Offset,
        Vec2UniformCount
    };

    enum Vec4Uniform {
        ModulationConstant,
        Vec4UniformCount
    };

    enum FloatUniform {
        Saturation,
        FloatUniformCount
    };

    enum IntUniform {
        AlphaToOne,
        IntUniformCount
    };

    bool setUniform(MatrixUniform uniform, const QMatrix4x4 &matrix);
    bool setUniform(Vec2Uniform uniform,   const QVector2D &value);
    bool setUniform(Vec4Uniform uniform,   const QVector4D &value);
    bool setUniform(FloatUniform uniform,  float value);
    bool setUniform(IntUniform uniform,    int value);

protected:
    GLShader();
    bool loadFromFiles(const QString& vertexfile, const QString& fragmentfile);
    bool load(const QByteArray &vertexSource, const QByteArray &fragmentSource);
    bool compile(GLuint program, GLenum shaderType, const QByteArray &sourceCode) const;
    void bind();
    void unbind();
    void resolveLocations();

private:
    unsigned int mProgram;
    bool mValid:1;
    bool mLocationsResolved:1;
    int mMatrixLocation[MatrixCount];
    int mVec2Location[Vec2UniformCount];
    int mVec4Location[Vec4UniformCount];
    int mFloatLocation[FloatUniformCount];
    int mIntLocation[IntUniformCount];

    friend class ShaderManager;
};

/**
 * @short Manager for Shaders.
 *
 * This class provides some built-in shaders to be used by both compositing scene and effects.
 * The ShaderManager provides methods to bind a built-in or a custom shader and keeps track of
 * the shaders which have been bound. When a shader is unbound the previously bound shader
 * will be rebound.
 *
 * @author Martin Gräßlin <kde@martin-graesslin.com>
 * @since 4.7
 **/
class KWIN_EXPORT ShaderManager
{
public:
    /**
     * Identifiers for built-in shaders available for effects and scene
     **/
    enum ShaderType {
        /**
         * An orthographic projection shader able to render textured geometries.
         * Expects a @c vec2 uniform @c offset describing the offset from top-left corner
         * and defaults to @c (0/0). Expects a @c vec2 uniform @c textureSize to calculate
         * normalized texture coordinates. Defaults to @c (1.0/1.0). And expects a @c vec3
         * uniform @c colorManiuplation, with @c x being opacity, @c y being brightness and
         * @c z being saturation. All three values default to @c 1.0.
         * The fragment shader takes a uniform @c u_forceAlpha to force the alpha component
         * to @c 1.0 if it is set to a value not @c 0. This uniform is useful when rendering
         * a RGB-only window and the alpha channel is required in a later step.
         * The sampler uniform is @c sample and defaults to @c 0.
         * The shader uses two vertex attributes @c vertex and @c texCoord.
         **/
        SimpleShader,
        /**
         * A generic shader able to render transformed, textured geometries.
         * This shader is mostly needed by the scene and not of much interest for effects.
         * Effects can influence this shader through @link ScreenPaintData and @link WindowPaintData.
         * The shader expects four @c mat4 uniforms @c projection, @c modelview,
         * @c screenTransformation and @c windowTransformation. The fragment shader expect the
         * same uniforms as the SimpleShader and the same vertex attributes are used.
         **/
        GenericShader,
        /**
         * An orthographic shader to render simple colored geometries without texturing.
         * Expects a @c vec2 uniform @c offset describing the offset from top-left corner
         * and defaults to @c (0/0). The fragment shader expects a single @c vec4 uniform
         * @c geometryColor, which defaults to fully opaque black.
         * The Shader uses one vertex attribute @c vertex.
         **/
        ColorShader
    };

    /**
     * @return The currently bound shader or @c null if no shader is bound.
     **/
    GLShader *getBoundShader() const;

    /**
     * @return @c true if a shader is bound, @c false otherwise
     **/
    bool isShaderBound() const;
    /**
     * @return @c true if the built-in shaders are valid, @c false otherwise
     **/
    bool isValid() const;

    /**
     * Binds the shader of specified @p type.
     * To unbind the shader use @link popShader. A previous bound shader will be rebound.
     * @param type The built-in shader to bind
     * @param reset Whether all uniforms should be reset to their default values
     * @return The bound shader or @c NULL if shaders are not valid
     * @see popShader
     **/
    GLShader *pushShader(ShaderType type, bool reset = false);
    /**
     * Binds the @p shader.
     * To unbind the shader use @link popShader. A previous bound shader will be rebound.
     * To bind a built-in shader use the more specific method.
     * @param shader The shader to be bound
     * @see popShader
     **/
    void pushShader(GLShader *shader);

    /**
     * Unbinds the currently bound shader and rebinds a previous stored shader.
     * If there is no previous shader, no shader will be rebound.
     * It is not safe to call this method if there is no bound shader.
     * @see pushShader
     * @see getBoundShader
     **/
    void popShader();

    /**
     * Creates a GLShader with a built-in vertex shader and a custom fragment shader.
     * @param vertex The generic vertex shader
     * @param fragmentFile The path to the source code of the fragment shader
     * @return The created shader
     **/
    GLShader *loadFragmentShader(ShaderType vertex, const QString &fragmentFile);
    /**
     * Creates a GLShader with a built-in fragment shader and a custom vertex shader.
     * @param fragment The generic fragment shader
     * @param vertexFile The path to the source code of the vertex shader
     * @return The created shader
     **/
    GLShader *loadVertexShader(ShaderType fragment, const QString &vertexFile);
    /**
     * Creates a GLShader with the specified sources.
     * The difference to GLShader is that it does not need to be loaded from files.
     * @param vertexSource The source code of the vertex shader
     * @param fragmentSource The source code of the fragment shader.
     * @return The created shader
     **/
    GLShader *loadShaderFromCode(const QByteArray &vertexSource, const QByteArray &fragmentSource);

    /**
     * @return a pointer to the ShaderManager instance
     **/
    static ShaderManager *instance();

    /**
     * @internal
     **/
    static void cleanup();

private:
    ShaderManager();
    ~ShaderManager();

    void initShaders();
    void resetShader(ShaderType type);

    QStack<GLShader*> m_boundShaders;
    GLShader *m_orthoShader;
    GLShader *m_genericShader;
    GLShader *m_colorShader;
    bool m_inited;
    bool m_valid;
    static ShaderManager *s_shaderManager;
};

/**
 * @short Render target object
 *
 * Render target object enables you to render onto a texture. This texture can
 *  later be used to e.g. do post-processing of the scene.
 *
 * @author Rivo Laks <rivolaks@hot.ee>
 **/
class KWIN_EXPORT GLRenderTarget
{
public:
    /**
     * Constructs a GLRenderTarget
     * @param color texture where the scene will be rendered onto
     **/
    GLRenderTarget(GLTexture* color);
    ~GLRenderTarget();

    /**
     * Enables this render target.
     * All OpenGL commands from now on affect this render target until the
     *  @ref disable method is called
     **/
    bool enable();
    /**
     * Disables this render target, activating whichever target was active
     *  when @ref enable was called.
     **/
    bool disable();

    bool valid() const  {
        return mValid;
    }

    static void initStatic();
    static bool supported()  {
        return sSupported;
    }
    static void pushRenderTarget(GLRenderTarget *target);
    static GLRenderTarget *popRenderTarget();
    static bool isRenderTargetBound();


protected:
    void initFBO();


private:
    static bool sSupported;
    static QStack<GLRenderTarget*> s_renderTargets;

    GLTexture* mTexture;
    bool mValid;

    GLuint mFramebuffer;
};

/**
 * @short Vertex Buffer Object
 *
 * This is a short helper class to use vertex buffer objects (VBO). A VBO can be used to buffer
 * vertex data and to store them on graphics memory. It is the only allowed way to pass vertex
 * data to the GPU in OpenGL ES 2 and OpenGL 3 with forward compatible mode.
 *
 * If VBOs are not supported on the used OpenGL profile this class falls back to legacy
 * rendering using client arrays. Therefore this class should always be used for rendering geometries.
 *
 * @author Martin Gräßlin <kde@martin-graesslin.com>
 * @since 4.6
 */
class KWIN_EXPORT GLVertexBuffer
{
public:
    /**
     * Enum to define how often the vertex data in the buffer object changes.
     */
    enum UsageHint {
        Dynamic, ///< frequent changes, but used several times for rendering
        Static, ///< No changes to data
        Stream ///< Data only used once for rendering, updated very frequently
    };

    GLVertexBuffer(UsageHint hint);
    ~GLVertexBuffer();

    /**
     * Sets the vertex data.
     * @param numberVertices The number of vertices in the arrays
     * @param dim The dimension of the vertices: 2 for x/y, 3 for x/y/z
     * @param vertices The vertices, size must equal @a numberVertices * @a dim
     * @param texcoords The texture coordinates for each vertex.
     * Size must equal 2 * @a numberVertices.
     */
    void setData(int numberVertices, int dim, const float* vertices, const float* texcoords);
    /**
     * Renders the vertex data in given @a primitiveMode.
     * Please refer to OpenGL documentation of glDrawArrays or glDrawElements for allowed
     * values for @a primitiveMode. Best is to use GL_TRIANGLES or similar to be future
     * compatible.
     */
    void render(GLenum primitiveMode);
    /**
     * Same as above restricting painting to @a region.
     */
    void render(const QRegion& region, GLenum primitiveMode);
    /**
     * Sets the color the geometry will be rendered with.
     * For legacy rendering glColor is used before rendering the geometry.
     * For core shader a uniform "geometryColor" is expected and is set.
     * @param color The color to render the geometry
     * @param enableColor Whether the geometry should be rendered with a color or not
     * @see setUseColor
     * @see isUseColor
     * @since 4.7
     **/
    void setColor(const QColor& color, bool enableColor = true);
    /**
     * @return @c true if geometry will be painted with a color, @c false otherwise
     * @see setUseColor
     * @see setColor
     * @since 4.7
     **/
    bool isUseColor() const;
    /**
     * Enables/Disables rendering the geometry with a color.
     * If no color is set an opaque, black color is used.
     * @param enable Enable/Disable rendering with color
     * @see isUseColor
     * @see setColor
     * @since 4.7
     **/
    void setUseColor(bool enable);

    /**
     * Resets the instance to default values.
     * Useful for shared buffers.
     * @since 4.7
     **/
    void reset();

    /**
     * @internal
     */
    static void initStatic();
    /**
     * Returns true if VBOs are supported, it is save to use this class even if VBOs are not
     * supported.
     * @returns true if vertex buffer objects are supported
     */
    static bool isSupported();

    /**
     * @return A shared VBO for streaming data
     * @since 4.7
     **/
    static GLVertexBuffer *streamingBuffer();

private:
    GLVertexBufferPrivate* const d;
};

} // namespace

#endif

/** @} */

#endif
