/*
* Copyright (C) 2017 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 "GLcommon/ScopedGLState.h"

#include "GLcommon/GLEScontext.h"

#include <GLES/gl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <GLES3/gl31.h>

#include <unordered_map>

void ScopedGLState::push(GLenum name) {
    auto& gl = GLEScontext::dispatcher();

    StateVector v;

    switch (name) {
    case GL_DRAW_FRAMEBUFFER_BINDING:
    case GL_READ_FRAMEBUFFER_BINDING:
    case GL_CURRENT_PROGRAM:
    case GL_VERTEX_ARRAY_BINDING:
    case GL_ARRAY_BUFFER_BINDING:
    case GL_TEXTURE_BINDING_2D:
    case GL_TEXTURE_BINDING_CUBE_MAP:
    case GL_VIEWPORT:
    case GL_COLOR_WRITEMASK:
        gl.glGetIntegerv(name, v.intData);
        break;
    case GL_DEPTH_RANGE:
        gl.glGetFloatv(name, v.floatData);
        break;
    // glIsEnabled queries
    case GL_BLEND:
    case GL_SCISSOR_TEST:
    case GL_DEPTH_TEST:
    case GL_STENCIL_TEST:
    case GL_CULL_FACE:
    case GL_RASTERIZER_DISCARD:
    case GL_SAMPLE_ALPHA_TO_COVERAGE:
    case GL_SAMPLE_COVERAGE:
    case GL_POLYGON_OFFSET_FILL:
        v.intData[0] = gl.glIsEnabled(name);
        break;
    default:
        fprintf(stderr,
                "%s: ScopedGLState doesn't support 0x%x yet, it's mainly for "
                "texture emulation by drawing fullscreen quads.\n", __func__,
                name);
        break;
    }

    mStateMap[name] = v;
}

void ScopedGLState::push(std::initializer_list<GLenum> names) {
    for (const auto name : names) {
        push(name);
    }
}

void ScopedGLState::pushForCoreProfileTextureEmulation() {
    push({ GL_DRAW_FRAMEBUFFER_BINDING,
           GL_READ_FRAMEBUFFER_BINDING,
           GL_VERTEX_ARRAY_BINDING,
           GL_CURRENT_PROGRAM,
           GL_VIEWPORT,
           GL_SCISSOR_TEST,
           GL_DEPTH_TEST,
           GL_BLEND,
           GL_DEPTH_RANGE,
           GL_COLOR_WRITEMASK,
           GL_SAMPLE_ALPHA_TO_COVERAGE,
           GL_SAMPLE_COVERAGE,
           GL_CULL_FACE,
           GL_POLYGON_OFFSET_FILL,
           GL_RASTERIZER_DISCARD,
           GL_TEXTURE_BINDING_2D });
}

ScopedGLState::~ScopedGLState() {
    auto& gl = GLEScontext::dispatcher();

    for (const auto& it : mStateMap) {
        GLenum name = it.first;
        const StateVector& v = it.second;
        switch (name) {
            case GL_DRAW_FRAMEBUFFER_BINDING:
                gl.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, v.intData[0]);
            case GL_READ_FRAMEBUFFER_BINDING:
                gl.glBindFramebuffer(GL_READ_FRAMEBUFFER, v.intData[0]);
                break;
            case GL_CURRENT_PROGRAM:
                gl.glUseProgram(v.intData[0]);
                break;
            case GL_VERTEX_ARRAY_BINDING:
                gl.glBindVertexArray(v.intData[0]);
                break;
            case GL_ARRAY_BUFFER_BINDING:
                gl.glBindBuffer(GL_ARRAY_BUFFER, v.intData[0]);
                break;
            case GL_TEXTURE_BINDING_2D:
                gl.glBindTexture(GL_TEXTURE_2D, v.intData[0]);
                break;
            case GL_TEXTURE_BINDING_CUBE_MAP:
                gl.glBindTexture(GL_TEXTURE_CUBE_MAP, v.intData[0]);
                break;
            case GL_VIEWPORT:
                gl.glViewport(v.intData[0], v.intData[1], v.intData[2], v.intData[3]);
                break;
            case GL_COLOR_WRITEMASK:
                gl.glColorMask(v.intData[0], v.intData[1], v.intData[2], v.intData[3]);
                break;
            case GL_DEPTH_RANGE:
                gl.glDepthRange(v.floatData[0], v.floatData[1]);
                break;
                // glIsEnabled queries
            case GL_BLEND:
            case GL_SCISSOR_TEST:
            case GL_DEPTH_TEST:
            case GL_STENCIL_TEST:
            case GL_CULL_FACE:
            case GL_RASTERIZER_DISCARD:
            case GL_SAMPLE_ALPHA_TO_COVERAGE:
            case GL_SAMPLE_COVERAGE:
            case GL_POLYGON_OFFSET_FILL:
                if (v.intData[0]) {
                    gl.glEnable(name);
                } else {
                    gl.glDisable(name);
                }
                break;
            default:
                fprintf(stderr,
                        "%s: ScopedGLState doesn't support 0x%x yet, it's mainly for "
                        "texture emulation by drawing fullscreen quads.\n", __func__,
                        name);
                break;
        }
    }
}
