/*
 * Copyright (C) 2012 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.
 */

#include "Flatland.h"
#include "GLHelper.h"

namespace android {

static float colors[][4] = {
    { .85f, .14f, .44f, 1.0f },
    { .91f, .72f, .10f, 1.0f },
    { .04f, .66f, .42f, 1.0f },
    { .84f, .39f, .68f, 1.0f },
    { .38f, .53f, .78f, 1.0f },
};

static size_t g_colorIndex;

const float* genColor() {
    float* color = colors[g_colorIndex];
    g_colorIndex = (g_colorIndex + 1) % NELEMS(colors);
    return color;
}

void resetColorGenerator() {
    g_colorIndex = 0;
}

class GradientRenderer {

public:

    bool setUp(GLHelper* helper) {
        bool result;

        result = helper->getShaderProgram("Gradient", &mGradPgm);
        if (!result) {
            return false;
        }

        result = helper->getDitherTexture(&mDitherTexName);
        if (!result) {
            return false;
        }

        mPosAttribLoc = glGetAttribLocation(mGradPgm, "position");
        mUVAttribLoc = glGetAttribLocation(mGradPgm, "uv");
        mUVToInterpUniformLoc = glGetUniformLocation(mGradPgm, "uvToInterp");
        mObjToNdcUniformLoc = glGetUniformLocation(mGradPgm, "objToNdc");
        mDitherKernelSamplerLoc = glGetUniformLocation(mGradPgm, "ditherKernel");
        mInvDitherKernelSizeUniformLoc = glGetUniformLocation(mGradPgm,
                "invDitherKernelSize");
        mInvDitherKernelSizeSqUniformLoc = glGetUniformLocation(mGradPgm,
                "invDitherKernelSizeSq");
        mColor0UniformLoc = glGetUniformLocation(mGradPgm, "color0");
        mColor1UniformLoc = glGetUniformLocation(mGradPgm, "color1");

        return true;
    }

    void tearDown() {
    }

    bool drawGradient() {
        float identity[16] = {
            1.0f,   0.0f,   0.0f,   0.0f,
            0.0f,   1.0f,   0.0f,   0.0f,
            0.0f,   0.0f,   1.0f,   0.0f,
            0.0f,   0.0f,   0.0f,   1.0f,
        };
        const float pos[] = {
            -1.0f,  -1.0f,
            1.0f,   -1.0f,
            -1.0f,  1.0f,
            1.0f,   1.0f,
        };
        const float uv[] = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
        };
        const float* color0 = genColor();
        const float* color1 = genColor();

        glUseProgram(mGradPgm);

        glVertexAttribPointer(mPosAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, pos);
        glVertexAttribPointer(mUVAttribLoc, 2, GL_FLOAT, GL_FALSE, 0, uv);
        glEnableVertexAttribArray(mPosAttribLoc);
        glEnableVertexAttribArray(mUVAttribLoc);

        float invDitherKernelSize = 1.0f / float(GLHelper::DITHER_KERNEL_SIZE);
        float invDitherKernelSizeSq = invDitherKernelSize * invDitherKernelSize;

        glUniformMatrix4fv(mObjToNdcUniformLoc, 1, GL_FALSE, identity);
        glUniformMatrix4fv(mUVToInterpUniformLoc, 1, GL_FALSE, identity);
        glUniform1f(mInvDitherKernelSizeUniformLoc, invDitherKernelSize);
        glUniform1f(mInvDitherKernelSizeSqUniformLoc, invDitherKernelSizeSq);
        glUniform4fv(mColor0UniformLoc, 1, color0);
        glUniform4fv(mColor1UniformLoc, 1, color1);

        if (glGetError() != GL_NO_ERROR) {
            fprintf(stderr, "GL error! 0\n");
        }

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mDitherTexName);

        if (glGetError() != GL_NO_ERROR) {
            fprintf(stderr, "GL error! 1\n");
        }

        glUniform1i(mDitherKernelSamplerLoc, 0);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glDisableVertexAttribArray(mPosAttribLoc);
        glDisableVertexAttribArray(mUVAttribLoc);

        if (glGetError() != GL_NO_ERROR) {
            fprintf(stderr, "GL error! 2\n");
        }

        return true;
    }

    GLuint mGradPgm;
    GLuint mDitherTexName;
    GLuint mPosAttribLoc;
    GLuint mUVAttribLoc;
    GLuint mObjToNdcUniformLoc;
    GLuint mUVToInterpUniformLoc;
    GLuint mDitherKernelSamplerLoc;
    GLuint mInvDitherKernelSizeUniformLoc;
    GLuint mInvDitherKernelSizeSqUniformLoc;
    GLuint mColor0UniformLoc;
    GLuint mColor1UniformLoc;
};

Renderer* staticGradient() {
    class NoRenderer : public Renderer {
        virtual bool setUp(GLHelper* helper) {
            mIsFirstFrame = true;
            mGLHelper = helper;
            return mGradientRenderer.setUp(helper);
        }

        virtual void tearDown() {
            mGradientRenderer.tearDown();
        }

        virtual bool render(EGLSurface surface) {
            if (mIsFirstFrame) {
                bool result;
                mIsFirstFrame = false;

                result = mGLHelper->makeCurrent(surface);
                if (!result) {
                    return false;
                }

                result = mGradientRenderer.drawGradient();
                if (!result) {
                    return false;
                }

                result = mGLHelper->swapBuffers(surface);
                if (!result) {
                    return false;
                }
            }
            return true;
        }

        bool mIsFirstFrame;
        GLHelper* mGLHelper;
        GradientRenderer mGradientRenderer;
    };
    return new NoRenderer;
}


} // namespace android