/*
 * Copyright 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
#define SF_RENDER_ENGINE_PROGRAMCACHE_H

#include <GLES2/gl2.h>

#include <utils/Singleton.h>
#include <utils/KeyedVector.h>
#include <utils/TypeHelpers.h>

#include "Description.h"

namespace android {

class Description;
class Program;
class String8;

/*
 * This class generates GLSL programs suitable to handle a given
 * Description. It's responsible for figuring out what to
 * generate from a Description.
 * It also maintains a cache of these Programs.
 */
class ProgramCache : public Singleton<ProgramCache> {
public:
    /*
     * Key is used to retrieve a Program in the cache.
     * A Key is generated from a Description.
     */
    class Key {
        friend class ProgramCache;
        typedef uint32_t key_t;
        key_t mKey;
    public:
        enum {
            BLEND_PREMULT           =       0x00000001,
            BLEND_NORMAL            =       0x00000000,
            BLEND_MASK              =       0x00000001,

            OPACITY_OPAQUE          =       0x00000002,
            OPACITY_TRANSLUCENT     =       0x00000000,
            OPACITY_MASK            =       0x00000002,

            PLANE_ALPHA_LT_ONE      =       0x00000004,
            PLANE_ALPHA_EQ_ONE      =       0x00000000,
            PLANE_ALPHA_MASK        =       0x00000004,

            TEXTURE_OFF             =       0x00000000,
            TEXTURE_EXT             =       0x00000008,
            TEXTURE_2D              =       0x00000010,
            TEXTURE_MASK            =       0x00000018,

            COLOR_MATRIX_OFF        =       0x00000000,
            COLOR_MATRIX_ON         =       0x00000020,
            COLOR_MATRIX_MASK       =       0x00000020,

            TEXTURE_MASKING_OFF     =       0x00000000,
            TEXTURE_MASKING_EXT     =       0x00800000,
            TEXTURE_MASKING_2D      =       0x01000000,
            TEXTURE_MASKING_MASK    =       0x01800000,
        };

        inline Key() : mKey(0) { }
        inline Key(const Key& rhs) : mKey(rhs.mKey) { }

        inline Key& set(key_t mask, key_t value) {
            mKey = (mKey & ~mask) | value;
            return *this;
        }

        inline bool isTexturing() const {
            return (mKey & TEXTURE_MASK) != TEXTURE_OFF;
        }
        inline int getTextureTarget() const {
            return (mKey & TEXTURE_MASK);
        }
        inline bool isPremultiplied() const {
            return (mKey & BLEND_MASK) == BLEND_PREMULT;
        }
        inline bool isOpaque() const {
            return (mKey & OPACITY_MASK) == OPACITY_OPAQUE;
        }
        inline bool hasPlaneAlpha() const {
            return (mKey & PLANE_ALPHA_MASK) == PLANE_ALPHA_LT_ONE;
        }
        inline bool hasColorMatrix() const {
            return (mKey & COLOR_MATRIX_MASK) == COLOR_MATRIX_ON;
        }
        inline bool isTextureMasking() const {
            return (mKey & TEXTURE_MASKING_MASK) != TEXTURE_MASKING_OFF;
        }
        inline int getTextureMaskingTarget() const {
            return (mKey & TEXTURE_MASKING_MASK);
        }

        // this is the definition of a friend function -- not a method of class Needs
        friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
            return  (lhs.mKey < rhs.mKey) ? 1 : 0;
        }
    };

    ProgramCache();
    ~ProgramCache();

    // useProgram lookup a suitable program in the cache or generates one
    // if none can be found.
    void useProgram(const Description& description);

private:
    // Generate shaders to populate the cache
    void primeCache();
    // compute a cache Key from a Description
    static Key computeKey(const Description& description);
    // generates a program from the Key
    static Program* generateProgram(const Key& needs);
    // generates the vertex shader from the Key
    static String8 generateVertexShader(const Key& needs);
    // generates the fragment shader from the Key
    static String8 generateFragmentShader(const Key& needs);

    // Key/Value map used for caching Programs. Currently the cache
    // is never shrunk.
    DefaultKeyedVector<Key, Program*> mCache;
};


ANDROID_BASIC_TYPES_TRAITS(ProgramCache::Key)

} /* namespace android */

#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
