//
// Copyright 2016 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

// queryutils.cpp: Utilities for querying values from GL objects

#include "libANGLE/queryutils.h"

#include "common/utilities.h"

#include "libANGLE/Buffer.h"
#include "libANGLE/Config.h"
#include "libANGLE/Context.h"
#include "libANGLE/Display.h"
#include "libANGLE/EGLSync.h"
#include "libANGLE/Fence.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/GLES1State.h"
#include "libANGLE/MemoryObject.h"
#include "libANGLE/Program.h"
#include "libANGLE/Renderbuffer.h"
#include "libANGLE/Sampler.h"
#include "libANGLE/Shader.h"
#include "libANGLE/Surface.h"
#include "libANGLE/Texture.h"
#include "libANGLE/Uniform.h"
#include "libANGLE/VertexAttribute.h"
#include "libANGLE/queryconversions.h"

namespace gl
{

namespace
{

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLfloat *params)
{
    if (isPureInteger)
    {
        UNREACHABLE();
        return ColorGeneric(ColorI());
    }
    else
    {
        return ColorGeneric(ColorF::fromData(params));
    }
}

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLint *params)
{
    if (isPureInteger)
    {
        return ColorGeneric(ColorI(params[0], params[1], params[2], params[3]));
    }
    else
    {
        return ColorGeneric(ColorF(normalizedToFloat(params[0]), normalizedToFloat(params[1]),
                                   normalizedToFloat(params[2]), normalizedToFloat(params[3])));
    }
}

template <bool isPureInteger>
ColorGeneric ConvertToColor(const GLuint *params)
{
    if (isPureInteger)
    {
        return ColorGeneric(ColorUI(params[0], params[1], params[2], params[3]));
    }
    else
    {
        UNREACHABLE();
        return ColorGeneric(ColorF());
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLfloat *outParams)
{
    if (isPureInteger)
    {
        UNREACHABLE();
    }
    else
    {
        ASSERT(color.type == ColorGeneric::Type::Float);
        color.colorF.writeData(outParams);
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLint *outParams)
{
    if (isPureInteger)
    {
        ASSERT(color.type == ColorGeneric::Type::Int);
        outParams[0] = color.colorI.red;
        outParams[1] = color.colorI.green;
        outParams[2] = color.colorI.blue;
        outParams[3] = color.colorI.alpha;
    }
    else
    {
        ASSERT(color.type == ColorGeneric::Type::Float);
        outParams[0] = floatToNormalized<GLint>(color.colorF.red);
        outParams[1] = floatToNormalized<GLint>(color.colorF.green);
        outParams[2] = floatToNormalized<GLint>(color.colorF.blue);
        outParams[3] = floatToNormalized<GLint>(color.colorF.alpha);
    }
}

template <bool isPureInteger>
void ConvertFromColor(const ColorGeneric &color, GLuint *outParams)
{
    if (isPureInteger)
    {
        ASSERT(color.type == ColorGeneric::Type::UInt);
        outParams[0] = color.colorUI.red;
        outParams[1] = color.colorUI.green;
        outParams[2] = color.colorUI.blue;
        outParams[3] = color.colorUI.alpha;
    }
    else
    {
        UNREACHABLE();
    }
}

template <typename ParamType>
void QueryTexLevelParameterBase(const Texture *texture,
                                TextureTarget target,
                                GLint level,
                                GLenum pname,
                                ParamType *params)
{
    ASSERT(texture != nullptr);
    const InternalFormat *info = texture->getTextureState().getImageDesc(target, level).format.info;

    switch (pname)
    {
        case GL_TEXTURE_RED_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->redBits ? info->componentType : GL_NONE);
            break;
        case GL_TEXTURE_GREEN_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->greenBits ? info->componentType : GL_NONE);
            break;
        case GL_TEXTURE_BLUE_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->blueBits ? info->componentType : GL_NONE);
            break;
        case GL_TEXTURE_ALPHA_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->alphaBits ? info->componentType : GL_NONE);
            break;
        case GL_TEXTURE_DEPTH_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->depthBits ? info->componentType : GL_NONE);
            break;
        case GL_TEXTURE_RED_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->redBits);
            break;
        case GL_TEXTURE_GREEN_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->greenBits);
            break;
        case GL_TEXTURE_BLUE_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->blueBits);
            break;
        case GL_TEXTURE_ALPHA_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->alphaBits);
            break;
        case GL_TEXTURE_DEPTH_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->depthBits);
            break;
        case GL_TEXTURE_STENCIL_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->stencilBits);
            break;
        case GL_TEXTURE_SHARED_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, info->sharedBits);
            break;
        case GL_TEXTURE_INTERNAL_FORMAT:
            *params = CastFromGLintStateValue<ParamType>(
                pname, info->internalFormat ? info->internalFormat : GL_RGBA);
            break;
        case GL_TEXTURE_WIDTH:
            *params = CastFromGLintStateValue<ParamType>(
                pname, static_cast<uint32_t>(texture->getWidth(target, level)));
            break;
        case GL_TEXTURE_HEIGHT:
            *params = CastFromGLintStateValue<ParamType>(
                pname, static_cast<uint32_t>(texture->getHeight(target, level)));
            break;
        case GL_TEXTURE_DEPTH:
            *params = CastFromGLintStateValue<ParamType>(
                pname, static_cast<uint32_t>(texture->getDepth(target, level)));
            break;
        case GL_TEXTURE_SAMPLES:
            *params = CastFromStateValue<ParamType>(pname, texture->getSamples(target, level));
            break;
        case GL_TEXTURE_FIXED_SAMPLE_LOCATIONS:
            *params = CastFromStateValue<ParamType>(
                pname, static_cast<GLint>(texture->getFixedSampleLocations(target, level)));
            break;
        case GL_TEXTURE_COMPRESSED:
            *params = CastFromStateValue<ParamType>(pname, static_cast<GLint>(info->compressed));
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params =
                CastFromStateValue<ParamType>(pname, texture->getLevelMemorySize(target, level));
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->initState(ImageIndex::MakeFromTarget(target, level)) ==
                           InitState::Initialized);
            break;
        case GL_TEXTURE_BUFFER_DATA_STORE_BINDING:
            *params = CastFromStateValue<ParamType>(
                pname, static_cast<GLint>(texture->getBuffer().id().value));
            break;
        case GL_TEXTURE_BUFFER_OFFSET:
            *params = CastFromStateValue<ParamType>(
                pname, static_cast<GLint>(texture->getBuffer().getOffset()));
            break;
        case GL_TEXTURE_BUFFER_SIZE:
            *params = CastFromStateValue<ParamType>(
                pname, static_cast<GLint>(GetBoundBufferAvailableSize(texture->getBuffer())));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

// This function is needed to handle fixed_point data.
// It can be used when some pname need special conversion from int/float/bool to fixed_point.
template <bool isGLfixed, typename QueryT, typename ParamType>
QueryT CastFromSpecialValue(GLenum pname, const ParamType param)
{
    if (isGLfixed)
    {
        return static_cast<QueryT>(ConvertFloatToFixed(CastFromStateValue<GLfloat>(pname, param)));
    }
    else
    {
        return CastFromStateValue<QueryT>(pname, param);
    }
}

template <bool isPureInteger, bool isGLfixed, typename ParamType>
void QueryTexParameterBase(const Context *context,
                           const Texture *texture,
                           GLenum pname,
                           ParamType *params)
{
    ASSERT(texture != nullptr);

    switch (pname)
    {
        case GL_TEXTURE_MAG_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMagFilter());
            break;
        case GL_TEXTURE_MIN_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMinFilter());
            break;
        case GL_TEXTURE_WRAP_S:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapS());
            break;
        case GL_TEXTURE_WRAP_T:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapT());
            break;
        case GL_TEXTURE_WRAP_R:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getWrapR());
            break;
        case GL_TEXTURE_IMMUTABLE_FORMAT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getImmutableFormat());
            break;
        case GL_TEXTURE_IMMUTABLE_LEVELS:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getImmutableLevels());
            break;
        case GL_TEXTURE_USAGE_ANGLE:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getUsage());
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            *params =
                CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMaxAnisotropy());
            break;
        case GL_TEXTURE_SWIZZLE_R:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleRed());
            break;
        case GL_TEXTURE_SWIZZLE_G:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleGreen());
            break;
        case GL_TEXTURE_SWIZZLE_B:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleBlue());
            break;
        case GL_TEXTURE_SWIZZLE_A:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSwizzleAlpha());
            break;
        case GL_TEXTURE_BASE_LEVEL:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getBaseLevel());
            break;
        case GL_TEXTURE_MAX_LEVEL:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getMaxLevel());
            break;
        case GL_TEXTURE_MIN_LOD:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMinLod());
            break;
        case GL_TEXTURE_MAX_LOD:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMaxLod());
            break;
        case GL_TEXTURE_COMPARE_MODE:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getCompareMode());
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getCompareFunc());
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSRGBDecode());
            break;
        case GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getSRGBOverride());
            break;
        case GL_DEPTH_STENCIL_TEXTURE_MODE:
            *params =
                CastFromGLintStateValue<ParamType>(pname, texture->getDepthStencilTextureMode());
            break;
        case GL_TEXTURE_CROP_RECT_OES:
        {
            const gl::Rectangle &crop = texture->getCrop();
            params[0]                 = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.x);
            params[1]                 = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.y);
            params[2] = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.width);
            params[3] = CastFromSpecialValue<isGLfixed, ParamType>(pname, crop.height);
            break;
        }
        case GL_GENERATE_MIPMAP:
            *params = CastFromGLintStateValue<ParamType>(pname, texture->getGenerateMipmapHint());
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getMemorySize());
            break;
        case GL_TEXTURE_BORDER_COLOR:
            ConvertFromColor<isPureInteger>(texture->getBorderColor(), params);
            break;
        case GL_TEXTURE_NATIVE_ID_ANGLE:
            *params = CastFromSpecialValue<isGLfixed, ParamType>(pname, texture->getNativeID());
            break;
        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getImplementationColorReadFormat(context));
            break;
        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getImplementationColorReadType(context));
            break;
        case GL_IMAGE_FORMAT_COMPATIBILITY_TYPE:
            *params =
                CastFromGLintStateValue<ParamType>(pname, GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE);
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->initState() == InitState::Initialized);
            break;
        case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES:
            *params = CastFromGLintStateValue<ParamType>(
                pname, texture->getRequiredTextureImageUnits(context));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

// this function is needed to handle OES_FIXED_POINT.
// Some pname values can take in GLfixed values and may need to be converted
template <bool isGLfixed, typename ReturnType, typename ParamType>
ReturnType ConvertTexParam(GLenum pname, const ParamType param)
{
    if (isGLfixed)
    {
        return CastQueryValueTo<ReturnType>(pname,
                                            ConvertFixedToFloat(static_cast<GLfixed>(param)));
    }
    else
    {
        return CastQueryValueTo<ReturnType>(pname, param);
    }
}

template <bool isPureInteger, bool isGLfixed, typename ParamType>
void SetTexParameterBase(Context *context, Texture *texture, GLenum pname, const ParamType *params)
{
    ASSERT(texture != nullptr);

    switch (pname)
    {
        case GL_TEXTURE_WRAP_S:
            texture->setWrapS(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_T:
            texture->setWrapT(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_R:
            texture->setWrapR(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MIN_FILTER:
            texture->setMinFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAG_FILTER:
            texture->setMagFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_USAGE_ANGLE:
            texture->setUsage(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            texture->setMaxAnisotropy(context,
                                      ConvertTexParam<isGLfixed, GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_MODE:
            texture->setCompareMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            texture->setCompareFunc(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_R:
            texture->setSwizzleRed(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_G:
            texture->setSwizzleGreen(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_B:
            texture->setSwizzleBlue(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SWIZZLE_A:
            texture->setSwizzleAlpha(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_BASE_LEVEL:
        {
            (void)(texture->setBaseLevel(
                context, clampCast<GLuint>(CastQueryValueTo<GLint>(pname, params[0]))));
            break;
        }
        case GL_TEXTURE_MAX_LEVEL:
            texture->setMaxLevel(context,
                                 clampCast<GLuint>(CastQueryValueTo<GLint>(pname, params[0])));
            break;
        case GL_TEXTURE_MIN_LOD:
            texture->setMinLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_LOD:
            texture->setMaxLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_DEPTH_STENCIL_TEXTURE_MODE:
            texture->setDepthStencilTextureMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            texture->setSRGBDecode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT:
            texture->setSRGBOverride(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_CROP_RECT_OES:
            texture->setCrop(gl::Rectangle(ConvertTexParam<isGLfixed, GLint>(pname, params[0]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[1]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[2]),
                                           ConvertTexParam<isGLfixed, GLint>(pname, params[3])));
            break;
        case GL_GENERATE_MIPMAP:
            texture->setGenerateMipmapHint(ConvertToGLenum(params[0]));
            break;
        case GL_TEXTURE_BORDER_COLOR:
            texture->setBorderColor(context, ConvertToColor<isPureInteger>(params));
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            texture->setInitState(ConvertToBool(params[0]) ? InitState::Initialized
                                                           : InitState::MayNeedInit);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <bool isPureInteger, typename ParamType>
void QuerySamplerParameterBase(const Sampler *sampler, GLenum pname, ParamType *params)
{
    switch (pname)
    {
        case GL_TEXTURE_MIN_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getMinFilter());
            break;
        case GL_TEXTURE_MAG_FILTER:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getMagFilter());
            break;
        case GL_TEXTURE_WRAP_S:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getWrapS());
            break;
        case GL_TEXTURE_WRAP_T:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getWrapT());
            break;
        case GL_TEXTURE_WRAP_R:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getWrapR());
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            *params = CastFromStateValue<ParamType>(pname, sampler->getMaxAnisotropy());
            break;
        case GL_TEXTURE_MIN_LOD:
            *params = CastFromStateValue<ParamType>(pname, sampler->getMinLod());
            break;
        case GL_TEXTURE_MAX_LOD:
            *params = CastFromStateValue<ParamType>(pname, sampler->getMaxLod());
            break;
        case GL_TEXTURE_COMPARE_MODE:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getCompareMode());
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getCompareFunc());
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, sampler->getSRGBDecode());
            break;
        case GL_TEXTURE_BORDER_COLOR:
            ConvertFromColor<isPureInteger>(sampler->getBorderColor(), params);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <bool isPureInteger, typename ParamType>
void SetSamplerParameterBase(Context *context,
                             Sampler *sampler,
                             GLenum pname,
                             const ParamType *params)
{
    switch (pname)
    {
        case GL_TEXTURE_WRAP_S:
            sampler->setWrapS(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_T:
            sampler->setWrapT(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_WRAP_R:
            sampler->setWrapR(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MIN_FILTER:
            sampler->setMinFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAG_FILTER:
            sampler->setMagFilter(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
            sampler->setMaxAnisotropy(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_MODE:
            sampler->setCompareMode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_COMPARE_FUNC:
            sampler->setCompareFunc(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_MIN_LOD:
            sampler->setMinLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_MAX_LOD:
            sampler->setMaxLod(context, CastQueryValueTo<GLfloat>(pname, params[0]));
            break;
        case GL_TEXTURE_SRGB_DECODE_EXT:
            sampler->setSRGBDecode(context, ConvertToGLenum(pname, params[0]));
            break;
        case GL_TEXTURE_BORDER_COLOR:
            sampler->setBorderColor(context, ConvertToColor<isPureInteger>(params));
            break;
        default:
            UNREACHABLE();
            break;
    }

    sampler->onStateChange(angle::SubjectMessage::ContentsChanged);
}

// Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
template <typename ParamType, typename CurrentDataType, size_t CurrentValueCount>
void QueryVertexAttribBase(const VertexAttribute &attrib,
                           const VertexBinding &binding,
                           const CurrentDataType (&currentValueData)[CurrentValueCount],
                           GLenum pname,
                           ParamType *params)
{
    switch (pname)
    {
        case GL_CURRENT_VERTEX_ATTRIB:
            for (size_t i = 0; i < CurrentValueCount; ++i)
            {
                params[i] = CastFromStateValue<ParamType>(pname, currentValueData[i]);
            }
            break;
        case GL_VERTEX_ATTRIB_ARRAY_ENABLED:
            *params = CastFromStateValue<ParamType>(pname, static_cast<GLint>(attrib.enabled));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_SIZE:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.format->channelCount);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_STRIDE:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.vertexAttribArrayStride);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_TYPE:
            *params = CastFromGLintStateValue<ParamType>(
                pname, gl::ToGLenum(attrib.format->vertexAttribType));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
            *params =
                CastFromStateValue<ParamType>(pname, static_cast<GLint>(attrib.format->isNorm()));
            break;
        case GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
            *params = CastFromGLintStateValue<ParamType>(pname, binding.getBuffer().id().value);
            break;
        case GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
            *params = CastFromStateValue<ParamType>(pname, binding.getDivisor());
            break;
        case GL_VERTEX_ATTRIB_ARRAY_INTEGER:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.format->isPureInt());
            break;
        case GL_VERTEX_ATTRIB_BINDING:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.bindingIndex);
            break;
        case GL_VERTEX_ATTRIB_RELATIVE_OFFSET:
            *params = CastFromGLintStateValue<ParamType>(pname, attrib.relativeOffset);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

template <typename ParamType>
void QueryBufferParameterBase(const Buffer *buffer, GLenum pname, ParamType *params)
{
    ASSERT(buffer != nullptr);

    switch (pname)
    {
        case GL_BUFFER_USAGE:
            *params = CastFromGLintStateValue<ParamType>(pname, ToGLenum(buffer->getUsage()));
            break;
        case GL_BUFFER_SIZE:
            *params = CastFromStateValue<ParamType>(pname, buffer->getSize());
            break;
        case GL_BUFFER_ACCESS_FLAGS:
            *params = CastFromGLintStateValue<ParamType>(pname, buffer->getAccessFlags());
            break;
        case GL_BUFFER_ACCESS_OES:
            *params = CastFromGLintStateValue<ParamType>(pname, buffer->getAccess());
            break;
        case GL_BUFFER_MAPPED:
            *params = CastFromStateValue<ParamType>(pname, buffer->isMapped());
            break;
        case GL_BUFFER_MAP_OFFSET:
            *params = CastFromStateValue<ParamType>(pname, buffer->getMapOffset());
            break;
        case GL_BUFFER_MAP_LENGTH:
            *params = CastFromStateValue<ParamType>(pname, buffer->getMapLength());
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params = CastFromStateValue<ParamType>(pname, buffer->getMemorySize());
            break;
        case GL_BUFFER_IMMUTABLE_STORAGE_EXT:
            *params = CastFromStateValue<ParamType>(pname, buffer->isImmutable());
            break;
        case GL_BUFFER_STORAGE_FLAGS_EXT:
            *params = CastFromGLintStateValue<ParamType>(pname, buffer->getStorageExtUsageFlags());
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = CastFromStateValue<ParamType>(
                pname, ConvertToGLBoolean(buffer->initState() == InitState::Initialized));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

GLint GetCommonVariableProperty(const sh::ShaderVariable &var, GLenum prop)
{
    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(var.type);

        case GL_ARRAY_SIZE:
            // Queryable variables are guaranteed not to be arrays of arrays or arrays of structs,
            // see GLES 3.1 spec section 7.3.1.1 page 77.
            return clampCast<GLint>(var.getBasicTypeElementCount());

        case GL_NAME_LENGTH:
            // ES31 spec p84: This counts the terminating null char.
            return clampCast<GLint>(var.name.size() + 1u);

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetInputResourceProperty(const Program *program, GLuint index, GLenum prop)
{
    const sh::ShaderVariable &variable = program->getInputResource(index);

    switch (prop)
    {
        case GL_TYPE:
        case GL_ARRAY_SIZE:
            return GetCommonVariableProperty(variable, prop);

        case GL_NAME_LENGTH:
            return clampCast<GLint>(program->getInputResourceName(index).size() + 1u);

        case GL_LOCATION:
            return variable.isBuiltIn() ? GL_INVALID_INDEX : variable.location;

        // The query is targeted at the set of active input variables used by the first shader stage
        // of program. If program contains multiple shader stages then input variables from any
        // stage other than the first will not be enumerated. Since we found the variable to get
        // this far, we know it exists in the first attached shader stage.
        case GL_REFERENCED_BY_VERTEX_SHADER:
            return program->getState().getFirstAttachedShaderStageType() == ShaderType::Vertex;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return program->getState().getFirstAttachedShaderStageType() == ShaderType::Fragment;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return program->getState().getFirstAttachedShaderStageType() == ShaderType::Compute;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return program->getState().getFirstAttachedShaderStageType() == ShaderType::Geometry;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return program->getState().getFirstAttachedShaderStageType() == ShaderType::TessControl;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return program->getState().getFirstAttachedShaderStageType() ==
                   ShaderType::TessEvaluation;
        case GL_IS_PER_PATCH_EXT:
            return variable.isPatch;

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetOutputResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const sh::ShaderVariable &outputVariable = program->getOutputResource(index);

    switch (prop)
    {
        case GL_TYPE:
        case GL_ARRAY_SIZE:
            return GetCommonVariableProperty(outputVariable, prop);

        case GL_NAME_LENGTH:
            return clampCast<GLint>(program->getOutputResourceName(index).size() + 1u);

        case GL_LOCATION:
            return outputVariable.location;

        case GL_LOCATION_INDEX_EXT:
            // EXT_blend_func_extended
            if (program->getState().getLastAttachedShaderStageType() == gl::ShaderType::Fragment)
            {
                return program->getFragDataIndex(outputVariable.name);
            }
            return GL_INVALID_INDEX;

        // The set of active user-defined outputs from the final shader stage in this program. If
        // the final stage is a Fragment Shader, then this represents the fragment outputs that get
        // written to individual color buffers. If the program only contains a Compute Shader, then
        // there are no user-defined outputs.
        case GL_REFERENCED_BY_VERTEX_SHADER:
            return program->getState().getLastAttachedShaderStageType() == ShaderType::Vertex;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return program->getState().getLastAttachedShaderStageType() == ShaderType::Fragment;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return program->getState().getLastAttachedShaderStageType() == ShaderType::Compute;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return program->getState().getLastAttachedShaderStageType() == ShaderType::Geometry;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return program->getState().getLastAttachedShaderStageType() == ShaderType::TessControl;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return program->getState().getLastAttachedShaderStageType() ==
                   ShaderType::TessEvaluation;
        case GL_IS_PER_PATCH_EXT:
            return outputVariable.isPatch;

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint GetTransformFeedbackVaryingResourceProperty(const Program *program,
                                                  GLuint index,
                                                  const GLenum prop)
{
    const auto &tfVariable = program->getTransformFeedbackVaryingResource(index);
    switch (prop)
    {
        case GL_TYPE:
            return clampCast<GLint>(tfVariable.type);

        case GL_ARRAY_SIZE:
            return clampCast<GLint>(tfVariable.size());

        case GL_NAME_LENGTH:
            return clampCast<GLint>(tfVariable.nameWithArrayIndex().size() + 1);

        default:
            UNREACHABLE();
            return GL_INVALID_VALUE;
    }
}

GLint QueryProgramInterfaceActiveResources(const Program *program, GLenum programInterface)
{
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return clampCast<GLint>(program->getState().getProgramInputs().size());

        case GL_PROGRAM_OUTPUT:
            return clampCast<GLint>(program->getState().getOutputVariables().size());

        case GL_UNIFORM:
            return clampCast<GLint>(program->getState().getUniforms().size());

        case GL_UNIFORM_BLOCK:
            return clampCast<GLint>(program->getState().getUniformBlocks().size());

        case GL_ATOMIC_COUNTER_BUFFER:
            return clampCast<GLint>(program->getState().getAtomicCounterBuffers().size());

        case GL_BUFFER_VARIABLE:
            return clampCast<GLint>(program->getState().getBufferVariables().size());

        case GL_SHADER_STORAGE_BLOCK:
            return clampCast<GLint>(program->getState().getShaderStorageBlocks().size());

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return clampCast<GLint>(program->getTransformFeedbackVaryingCount());

        default:
            UNREACHABLE();
            return 0;
    }
}

template <typename T, typename M>
GLint FindMaxSize(const std::vector<T> &resources, M member)
{
    GLint max = 0;
    for (const T &resource : resources)
    {
        max = std::max(max, clampCast<GLint>((resource.*member).size()));
    }
    return max;
}

GLint QueryProgramInterfaceMaxNameLength(const Program *program, GLenum programInterface)
{
    GLint maxNameLength = 0;
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            maxNameLength = program->getInputResourceMaxNameSize();
            break;

        case GL_PROGRAM_OUTPUT:
            maxNameLength = program->getOutputResourceMaxNameSize();
            break;

        case GL_UNIFORM:
            maxNameLength = FindMaxSize(program->getState().getUniforms(), &LinkedUniform::name);
            break;

        case GL_UNIFORM_BLOCK:
            return program->getActiveUniformBlockMaxNameLength();

        case GL_BUFFER_VARIABLE:
            maxNameLength =
                FindMaxSize(program->getState().getBufferVariables(), &BufferVariable::name);
            break;

        case GL_SHADER_STORAGE_BLOCK:
            return program->getActiveShaderStorageBlockMaxNameLength();

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return clampCast<GLint>(program->getTransformFeedbackVaryingMaxLength());

        default:
            UNREACHABLE();
            return 0;
    }
    // This length includes an extra character for the null terminator.
    return (maxNameLength == 0 ? 0 : maxNameLength + 1);
}

GLint QueryProgramInterfaceMaxNumActiveVariables(const Program *program, GLenum programInterface)
{
    switch (programInterface)
    {
        case GL_UNIFORM_BLOCK:
            return FindMaxSize(program->getState().getUniformBlocks(),
                               &InterfaceBlock::memberIndexes);
        case GL_ATOMIC_COUNTER_BUFFER:
            return FindMaxSize(program->getState().getAtomicCounterBuffers(),
                               &AtomicCounterBuffer::memberIndexes);

        case GL_SHADER_STORAGE_BLOCK:
            return FindMaxSize(program->getState().getShaderStorageBlocks(),
                               &InterfaceBlock::memberIndexes);

        default:
            UNREACHABLE();
            return 0;
    }
}

GLenum GetUniformPropertyEnum(GLenum prop)
{
    switch (prop)
    {
        case GL_UNIFORM_TYPE:
            return GL_TYPE;
        case GL_UNIFORM_SIZE:
            return GL_ARRAY_SIZE;
        case GL_UNIFORM_NAME_LENGTH:
            return GL_NAME_LENGTH;
        case GL_UNIFORM_BLOCK_INDEX:
            return GL_BLOCK_INDEX;
        case GL_UNIFORM_OFFSET:
            return GL_OFFSET;
        case GL_UNIFORM_ARRAY_STRIDE:
            return GL_ARRAY_STRIDE;
        case GL_UNIFORM_MATRIX_STRIDE:
            return GL_MATRIX_STRIDE;
        case GL_UNIFORM_IS_ROW_MAJOR:
            return GL_IS_ROW_MAJOR;

        default:
            return prop;
    }
}

GLenum GetUniformBlockPropertyEnum(GLenum prop)
{
    switch (prop)
    {
        case GL_UNIFORM_BLOCK_BINDING:
            return GL_BUFFER_BINDING;

        case GL_UNIFORM_BLOCK_DATA_SIZE:
            return GL_BUFFER_DATA_SIZE;

        case GL_UNIFORM_BLOCK_NAME_LENGTH:
            return GL_NAME_LENGTH;

        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS:
            return GL_NUM_ACTIVE_VARIABLES;

        case GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES:
            return GL_ACTIVE_VARIABLES;

        case GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER:
            return GL_REFERENCED_BY_VERTEX_SHADER;

        case GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER:
            return GL_REFERENCED_BY_FRAGMENT_SHADER;

        default:
            return prop;
    }
}

void GetShaderVariableBufferResourceProperty(const ShaderVariableBuffer &buffer,
                                             GLenum pname,
                                             GLint *params,
                                             GLsizei bufSize,
                                             GLsizei *outputPosition)

{
    switch (pname)
    {
        case GL_BUFFER_BINDING:
            params[(*outputPosition)++] = buffer.binding;
            break;
        case GL_BUFFER_DATA_SIZE:
            params[(*outputPosition)++] = clampCast<GLint>(buffer.dataSize);
            break;
        case GL_NUM_ACTIVE_VARIABLES:
            params[(*outputPosition)++] = buffer.numActiveVariables();
            break;
        case GL_ACTIVE_VARIABLES:
            for (size_t memberIndex = 0;
                 memberIndex < buffer.memberIndexes.size() && *outputPosition < bufSize;
                 ++memberIndex)
            {
                params[(*outputPosition)++] = clampCast<GLint>(buffer.memberIndexes[memberIndex]);
            }
            break;
        case GL_REFERENCED_BY_VERTEX_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Vertex));
            break;
        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Fragment));
            break;
        case GL_REFERENCED_BY_COMPUTE_SHADER:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Compute));
            break;
        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            params[(*outputPosition)++] = static_cast<GLint>(buffer.isActive(ShaderType::Geometry));
            break;
        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            params[(*outputPosition)++] =
                static_cast<GLint>(buffer.isActive(ShaderType::TessControl));
            break;
        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            params[(*outputPosition)++] =
                static_cast<GLint>(buffer.isActive(ShaderType::TessEvaluation));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void GetInterfaceBlockResourceProperty(const InterfaceBlock &block,
                                       GLenum pname,
                                       GLint *params,
                                       GLsizei bufSize,
                                       GLsizei *outputPosition)
{
    if (pname == GL_NAME_LENGTH)
    {
        params[(*outputPosition)++] = clampCast<GLint>(block.nameWithArrayIndex().size() + 1);
        return;
    }
    GetShaderVariableBufferResourceProperty(block, pname, params, bufSize, outputPosition);
}

void GetUniformBlockResourceProperty(const Program *program,
                                     GLuint blockIndex,
                                     GLenum pname,
                                     GLint *params,
                                     GLsizei bufSize,
                                     GLsizei *outputPosition)

{
    ASSERT(*outputPosition < bufSize);
    const auto &block = program->getUniformBlockByIndex(blockIndex);
    GetInterfaceBlockResourceProperty(block, pname, params, bufSize, outputPosition);
}

void GetShaderStorageBlockResourceProperty(const Program *program,
                                           GLuint blockIndex,
                                           GLenum pname,
                                           GLint *params,
                                           GLsizei bufSize,
                                           GLsizei *outputPosition)

{
    ASSERT(*outputPosition < bufSize);
    const auto &block = program->getShaderStorageBlockByIndex(blockIndex);
    GetInterfaceBlockResourceProperty(block, pname, params, bufSize, outputPosition);
}

void GetAtomicCounterBufferResourceProperty(const Program *program,
                                            GLuint index,
                                            GLenum pname,
                                            GLint *params,
                                            GLsizei bufSize,
                                            GLsizei *outputPosition)

{
    ASSERT(*outputPosition < bufSize);
    const auto &buffer = program->getState().getAtomicCounterBuffers()[index];
    GetShaderVariableBufferResourceProperty(buffer, pname, params, bufSize, outputPosition);
}

bool IsTextureEnvEnumParameter(TextureEnvParameter pname)
{
    switch (pname)
    {
        case TextureEnvParameter::Mode:
        case TextureEnvParameter::CombineRgb:
        case TextureEnvParameter::CombineAlpha:
        case TextureEnvParameter::Src0Rgb:
        case TextureEnvParameter::Src1Rgb:
        case TextureEnvParameter::Src2Rgb:
        case TextureEnvParameter::Src0Alpha:
        case TextureEnvParameter::Src1Alpha:
        case TextureEnvParameter::Src2Alpha:
        case TextureEnvParameter::Op0Rgb:
        case TextureEnvParameter::Op1Rgb:
        case TextureEnvParameter::Op2Rgb:
        case TextureEnvParameter::Op0Alpha:
        case TextureEnvParameter::Op1Alpha:
        case TextureEnvParameter::Op2Alpha:
        case TextureEnvParameter::PointCoordReplace:
            return true;
        default:
            return false;
    }
}

}  // namespace

void QueryFramebufferAttachmentParameteriv(const Context *context,
                                           const Framebuffer *framebuffer,
                                           GLenum attachment,
                                           GLenum pname,
                                           GLint *params)
{
    ASSERT(framebuffer);

    const FramebufferAttachment *attachmentObject = framebuffer->getAttachment(context, attachment);

    if (attachmentObject == nullptr)
    {
        // ES 2.0.25 spec pg 127 states that if the value of FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
        // is NONE, then querying any other pname will generate INVALID_ENUM.

        // ES 3.0.2 spec pg 235 states that if the attachment type is none,
        // GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME will return zero and be an
        // INVALID_OPERATION for all other pnames

        switch (pname)
        {
            case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
                *params = GL_NONE;
                break;

            case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
                *params = 0;
                break;

            default:
                UNREACHABLE();
                break;
        }

        return;
    }

    switch (pname)
    {
        case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE:
            *params = attachmentObject->type();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
            *params = attachmentObject->id();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL:
            *params = attachmentObject->mipLevel();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE:
        {
            TextureTarget face = attachmentObject->cubeMapFace();
            if (face != TextureTarget::InvalidEnum)
            {
                *params = ToGLenum(attachmentObject->cubeMapFace());
            }
            else
            {
                // This happens when the attachment isn't a texture cube map face
                *params = GL_NONE;
            }
        }
        break;

        case GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
            *params = attachmentObject->getRedSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
            *params = attachmentObject->getGreenSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
            *params = attachmentObject->getBlueSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE:
            *params = attachmentObject->getAlphaSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE:
            *params = attachmentObject->getDepthSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE:
            *params = attachmentObject->getStencilSize();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE:
            *params = attachmentObject->getComponentType();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING:
            *params = attachmentObject->getColorEncoding();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER:
            *params = attachmentObject->layer();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR:
            *params = attachmentObject->getNumViews();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR:
            *params = attachmentObject->getBaseViewIndex();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT:
            *params = attachmentObject->isLayered();
            break;

        case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT:
            if (attachmentObject->type() == GL_TEXTURE)
            {
                *params = attachmentObject->getSamples();
            }
            else
            {
                *params = 0;
            }
            break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryBufferParameteriv(const Buffer *buffer, GLenum pname, GLint *params)
{
    QueryBufferParameterBase(buffer, pname, params);
}

void QueryBufferParameteri64v(const Buffer *buffer, GLenum pname, GLint64 *params)
{
    QueryBufferParameterBase(buffer, pname, params);
}

void QueryBufferPointerv(const Buffer *buffer, GLenum pname, void **params)
{
    switch (pname)
    {
        case GL_BUFFER_MAP_POINTER:
            *params = buffer->getMapPointer();
            break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryProgramiv(Context *context, const Program *program, GLenum pname, GLint *params)
{
    ASSERT(program != nullptr || pname == GL_COMPLETION_STATUS_KHR);

    switch (pname)
    {
        case GL_DELETE_STATUS:
            *params = program->isFlaggedForDeletion();
            return;
        case GL_LINK_STATUS:
            *params = program->isLinked();
            return;
        case GL_COMPLETION_STATUS_KHR:
            if (context->isContextLost())
            {
                *params = GL_TRUE;
            }
            else
            {
                *params = program->isLinking() ? GL_FALSE : GL_TRUE;
            }
            return;
        case GL_VALIDATE_STATUS:
            *params = program->isValidated();
            return;
        case GL_INFO_LOG_LENGTH:
            *params = program->getExecutable().getInfoLogLength();
            return;
        case GL_ATTACHED_SHADERS:
            *params = program->getAttachedShadersCount();
            return;
        case GL_ACTIVE_ATTRIBUTES:
            *params = program->getActiveAttributeCount();
            return;
        case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
            *params = program->getActiveAttributeMaxLength();
            return;
        case GL_ACTIVE_UNIFORMS:
            *params = program->getActiveUniformCount();
            return;
        case GL_ACTIVE_UNIFORM_MAX_LENGTH:
            *params = program->getActiveUniformMaxLength();
            return;
        case GL_PROGRAM_BINARY_LENGTH_OES:
            *params = context->getCaps().programBinaryFormats.empty()
                          ? 0
                          : program->getBinaryLength(context);
            return;
        case GL_ACTIVE_UNIFORM_BLOCKS:
            *params = program->getActiveUniformBlockCount();
            return;
        case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH:
            *params = program->getActiveUniformBlockMaxNameLength();
            break;
        case GL_TRANSFORM_FEEDBACK_BUFFER_MODE:
            *params = program->getTransformFeedbackBufferMode();
            break;
        case GL_TRANSFORM_FEEDBACK_VARYINGS:
            *params = clampCast<GLint>(program->getTransformFeedbackVaryingCount());
            break;
        case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:
            *params = program->getTransformFeedbackVaryingMaxLength();
            break;
        case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
            *params = program->getBinaryRetrievableHint();
            break;
        case GL_PROGRAM_SEPARABLE:
            // From es31cSeparateShaderObjsTests.cpp:
            // ProgramParameteri PROGRAM_SEPARABLE
            // NOTE: The query for PROGRAM_SEPARABLE must query latched
            //       state. In other words, the state of the binary after
            //       it was linked. So in the tests below, the queries
            //       should return the default state GL_FALSE since the
            //       program has no linked binary.
            *params = program->isSeparable() && program->isLinked();
            break;
        case GL_COMPUTE_WORK_GROUP_SIZE:
        {
            const sh::WorkGroupSize &localSize = program->getComputeShaderLocalSize();
            params[0]                          = localSize[0];
            params[1]                          = localSize[1];
            params[2]                          = localSize[2];
        }
        break;
        case GL_ACTIVE_ATOMIC_COUNTER_BUFFERS:
            *params = program->getActiveAtomicCounterBufferCount();
            break;
        case GL_GEOMETRY_LINKED_INPUT_TYPE_EXT:
            *params = ToGLenum(program->getGeometryShaderInputPrimitiveType());
            break;
        case GL_GEOMETRY_LINKED_OUTPUT_TYPE_EXT:
            *params = ToGLenum(program->getGeometryShaderOutputPrimitiveType());
            break;
        case GL_GEOMETRY_LINKED_VERTICES_OUT_EXT:
            *params = program->getGeometryShaderMaxVertices();
            break;
        case GL_GEOMETRY_SHADER_INVOCATIONS_EXT:
            *params = program->getGeometryShaderInvocations();
            break;
        case GL_TESS_CONTROL_OUTPUT_VERTICES_EXT:
            *params = program->getTessControlShaderVertices();
            break;
        case GL_TESS_GEN_MODE_EXT:
            *params = program->getTessGenMode();
            break;
        case GL_TESS_GEN_SPACING_EXT:
            *params = program->getTessGenSpacing() ? program->getTessGenSpacing() : GL_EQUAL;
            break;
        case GL_TESS_GEN_VERTEX_ORDER:
            *params = program->getTessGenVertexOrder() ? program->getTessGenVertexOrder() : GL_CCW;
            break;
        case GL_TESS_GEN_POINT_MODE_EXT:
            *params = program->getTessGenPointMode() ? GL_TRUE : GL_FALSE;
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryRenderbufferiv(const Context *context,
                         const Renderbuffer *renderbuffer,
                         GLenum pname,
                         GLint *params)
{
    ASSERT(renderbuffer != nullptr);

    switch (pname)
    {
        case GL_RENDERBUFFER_WIDTH:
            *params = renderbuffer->getWidth();
            break;
        case GL_RENDERBUFFER_HEIGHT:
            *params = renderbuffer->getHeight();
            break;
        case GL_RENDERBUFFER_INTERNAL_FORMAT:
            // Special case the WebGL 1 DEPTH_STENCIL format.
            if (context->isWebGL1() &&
                renderbuffer->getFormat().info->internalFormat == GL_DEPTH24_STENCIL8)
            {
                *params = GL_DEPTH_STENCIL;
            }
            else
            {
                *params = renderbuffer->getFormat().info->internalFormat;
            }
            break;
        case GL_RENDERBUFFER_RED_SIZE:
            *params = renderbuffer->getRedSize();
            break;
        case GL_RENDERBUFFER_GREEN_SIZE:
            *params = renderbuffer->getGreenSize();
            break;
        case GL_RENDERBUFFER_BLUE_SIZE:
            *params = renderbuffer->getBlueSize();
            break;
        case GL_RENDERBUFFER_ALPHA_SIZE:
            *params = renderbuffer->getAlphaSize();
            break;
        case GL_RENDERBUFFER_DEPTH_SIZE:
            *params = renderbuffer->getDepthSize();
            break;
        case GL_RENDERBUFFER_STENCIL_SIZE:
            *params = renderbuffer->getStencilSize();
            break;
        case GL_RENDERBUFFER_SAMPLES_ANGLE:
            *params = renderbuffer->getState().getSamples();
            break;
        case GL_MEMORY_SIZE_ANGLE:
            *params = renderbuffer->getMemorySize();
            break;
        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
            *params = static_cast<GLint>(renderbuffer->getImplementationColorReadFormat(context));
            break;
        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
            *params = static_cast<GLint>(renderbuffer->getImplementationColorReadType(context));
            break;
        case GL_RESOURCE_INITIALIZED_ANGLE:
            *params = (renderbuffer->initState(ImageIndex()) == InitState::Initialized);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryShaderiv(const Context *context, Shader *shader, GLenum pname, GLint *params)
{
    ASSERT(shader != nullptr || pname == GL_COMPLETION_STATUS_KHR);

    switch (pname)
    {
        case GL_SHADER_TYPE:
            *params = static_cast<GLint>(ToGLenum(shader->getType()));
            return;
        case GL_DELETE_STATUS:
            *params = shader->isFlaggedForDeletion();
            return;
        case GL_COMPILE_STATUS:
            *params = shader->isCompiled() ? GL_TRUE : GL_FALSE;
            return;
        case GL_COMPLETION_STATUS_KHR:
            if (context->isContextLost())
            {
                *params = GL_TRUE;
            }
            else
            {
                *params = shader->isCompleted() ? GL_TRUE : GL_FALSE;
            }
            return;
        case GL_INFO_LOG_LENGTH:
            *params = shader->getInfoLogLength();
            return;
        case GL_SHADER_SOURCE_LENGTH:
            *params = shader->getSourceLength();
            return;
        case GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE:
            *params = shader->getTranslatedSourceWithDebugInfoLength();
            return;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryTexLevelParameterfv(const Texture *texture,
                              TextureTarget target,
                              GLint level,
                              GLenum pname,
                              GLfloat *params)
{
    QueryTexLevelParameterBase(texture, target, level, pname, params);
}

void QueryTexLevelParameteriv(const Texture *texture,
                              TextureTarget target,
                              GLint level,
                              GLenum pname,
                              GLint *params)
{
    QueryTexLevelParameterBase(texture, target, level, pname, params);
}

void QueryTexParameterfv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLfloat *params)
{
    QueryTexParameterBase<false, false>(context, texture, pname, params);
}

void QueryTexParameterxv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLfixed *params)
{
    QueryTexParameterBase<false, true>(context, texture, pname, params);
}

void QueryTexParameteriv(const Context *context,
                         const Texture *texture,
                         GLenum pname,
                         GLint *params)
{
    QueryTexParameterBase<false, false>(context, texture, pname, params);
}

void QueryTexParameterIiv(const Context *context,
                          const Texture *texture,
                          GLenum pname,
                          GLint *params)
{
    QueryTexParameterBase<true, false>(context, texture, pname, params);
}

void QueryTexParameterIuiv(const Context *context,
                           const Texture *texture,
                           GLenum pname,
                           GLuint *params)
{
    QueryTexParameterBase<true, false>(context, texture, pname, params);
}

void QuerySamplerParameterfv(const Sampler *sampler, GLenum pname, GLfloat *params)
{
    QuerySamplerParameterBase<false>(sampler, pname, params);
}

void QuerySamplerParameteriv(const Sampler *sampler, GLenum pname, GLint *params)
{
    QuerySamplerParameterBase<false>(sampler, pname, params);
}

void QuerySamplerParameterIiv(const Sampler *sampler, GLenum pname, GLint *params)
{
    QuerySamplerParameterBase<true>(sampler, pname, params);
}

void QuerySamplerParameterIuiv(const Sampler *sampler, GLenum pname, GLuint *params)
{
    QuerySamplerParameterBase<true>(sampler, pname, params);
}

void QueryVertexAttribfv(const VertexAttribute &attrib,
                         const VertexBinding &binding,
                         const VertexAttribCurrentValueData &currentValueData,
                         GLenum pname,
                         GLfloat *params)
{
    QueryVertexAttribBase(attrib, binding, currentValueData.Values.FloatValues, pname, params);
}

void QueryVertexAttribiv(const VertexAttribute &attrib,
                         const VertexBinding &binding,
                         const VertexAttribCurrentValueData &currentValueData,
                         GLenum pname,
                         GLint *params)
{
    QueryVertexAttribBase(attrib, binding, currentValueData.Values.FloatValues, pname, params);
}

void QueryVertexAttribPointerv(const VertexAttribute &attrib, GLenum pname, void **pointer)
{
    switch (pname)
    {
        case GL_VERTEX_ATTRIB_ARRAY_POINTER:
            *pointer = const_cast<void *>(attrib.pointer);
            break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryVertexAttribIiv(const VertexAttribute &attrib,
                          const VertexBinding &binding,
                          const VertexAttribCurrentValueData &currentValueData,
                          GLenum pname,
                          GLint *params)
{
    QueryVertexAttribBase(attrib, binding, currentValueData.Values.IntValues, pname, params);
}

void QueryVertexAttribIuiv(const VertexAttribute &attrib,
                           const VertexBinding &binding,
                           const VertexAttribCurrentValueData &currentValueData,
                           GLenum pname,
                           GLuint *params)
{
    QueryVertexAttribBase(attrib, binding, currentValueData.Values.UnsignedIntValues, pname,
                          params);
}

void QueryActiveUniformBlockiv(const Program *program,
                               UniformBlockIndex uniformBlockIndex,
                               GLenum pname,
                               GLint *params)
{
    GLenum prop = GetUniformBlockPropertyEnum(pname);
    QueryProgramResourceiv(program, GL_UNIFORM_BLOCK, uniformBlockIndex, 1, &prop,
                           std::numeric_limits<GLsizei>::max(), nullptr, params);
}

void QueryInternalFormativ(const TextureCaps &format, GLenum pname, GLsizei bufSize, GLint *params)
{
    switch (pname)
    {
        case GL_NUM_SAMPLE_COUNTS:
            if (bufSize != 0)
            {
                *params = clampCast<GLint>(format.sampleCounts.size());
            }
            break;

        case GL_SAMPLES:
        {
            size_t returnCount   = std::min<size_t>(bufSize, format.sampleCounts.size());
            auto sampleReverseIt = format.sampleCounts.rbegin();
            for (size_t sampleIndex = 0; sampleIndex < returnCount; ++sampleIndex)
            {
                params[sampleIndex] = *sampleReverseIt++;
            }
        }
        break;

        default:
            UNREACHABLE();
            break;
    }
}

void QueryFramebufferParameteriv(const Framebuffer *framebuffer, GLenum pname, GLint *params)
{
    ASSERT(framebuffer);

    switch (pname)
    {
        case GL_FRAMEBUFFER_DEFAULT_WIDTH:
            *params = framebuffer->getDefaultWidth();
            break;
        case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
            *params = framebuffer->getDefaultHeight();
            break;
        case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
            *params = framebuffer->getDefaultSamples();
            break;
        case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
            *params = ConvertToGLBoolean(framebuffer->getDefaultFixedSampleLocations());
            break;
        case GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT:
            *params = framebuffer->getDefaultLayers();
            break;
        default:
            UNREACHABLE();
            break;
    }
}

angle::Result QuerySynciv(const Context *context,
                          const Sync *sync,
                          GLenum pname,
                          GLsizei bufSize,
                          GLsizei *length,
                          GLint *values)
{
    ASSERT(sync != nullptr || pname == GL_SYNC_STATUS);

    // All queries return one value, exit early if the buffer can't fit anything.
    if (bufSize < 1)
    {
        if (length != nullptr)
        {
            *length = 0;
        }
        return angle::Result::Continue;
    }

    switch (pname)
    {
        case GL_OBJECT_TYPE:
            *values = clampCast<GLint>(GL_SYNC_FENCE);
            break;
        case GL_SYNC_CONDITION:
            *values = clampCast<GLint>(sync->getCondition());
            break;
        case GL_SYNC_FLAGS:
            *values = clampCast<GLint>(sync->getFlags());
            break;
        case GL_SYNC_STATUS:
            if (context->isContextLost())
            {
                *values = GL_SIGNALED;
            }
            else
            {
                ANGLE_TRY(sync->getStatus(context, values));
            }
            break;

        default:
            UNREACHABLE();
            break;
    }

    if (length != nullptr)
    {
        *length = 1;
    }

    return angle::Result::Continue;
}

void SetTexParameterx(Context *context, Texture *texture, GLenum pname, GLfixed param)
{
    SetTexParameterBase<false, true>(context, texture, pname, &param);
}

void SetTexParameterxv(Context *context, Texture *texture, GLenum pname, const GLfixed *params)
{
    SetTexParameterBase<false, true>(context, texture, pname, params);
}

void SetTexParameterf(Context *context, Texture *texture, GLenum pname, GLfloat param)
{
    SetTexParameterBase<false, false>(context, texture, pname, &param);
}

void SetTexParameterfv(Context *context, Texture *texture, GLenum pname, const GLfloat *params)
{
    SetTexParameterBase<false, false>(context, texture, pname, params);
}

void SetTexParameteri(Context *context, Texture *texture, GLenum pname, GLint param)
{
    SetTexParameterBase<false, false>(context, texture, pname, &param);
}

void SetTexParameteriv(Context *context, Texture *texture, GLenum pname, const GLint *params)
{
    SetTexParameterBase<false, false>(context, texture, pname, params);
}

void SetTexParameterIiv(Context *context, Texture *texture, GLenum pname, const GLint *params)
{
    SetTexParameterBase<true, false>(context, texture, pname, params);
}

void SetTexParameterIuiv(Context *context, Texture *texture, GLenum pname, const GLuint *params)
{
    SetTexParameterBase<true, false>(context, texture, pname, params);
}

void SetSamplerParameterf(Context *context, Sampler *sampler, GLenum pname, GLfloat param)
{
    SetSamplerParameterBase<false>(context, sampler, pname, &param);
}

void SetSamplerParameterfv(Context *context, Sampler *sampler, GLenum pname, const GLfloat *params)
{
    SetSamplerParameterBase<false>(context, sampler, pname, params);
}

void SetSamplerParameteri(Context *context, Sampler *sampler, GLenum pname, GLint param)
{
    SetSamplerParameterBase<false>(context, sampler, pname, &param);
}

void SetSamplerParameteriv(Context *context, Sampler *sampler, GLenum pname, const GLint *params)
{
    SetSamplerParameterBase<false>(context, sampler, pname, params);
}

void SetSamplerParameterIiv(Context *context, Sampler *sampler, GLenum pname, const GLint *params)
{
    SetSamplerParameterBase<true>(context, sampler, pname, params);
}

void SetSamplerParameterIuiv(Context *context, Sampler *sampler, GLenum pname, const GLuint *params)
{
    SetSamplerParameterBase<true>(context, sampler, pname, params);
}

void SetFramebufferParameteri(const Context *context,
                              Framebuffer *framebuffer,
                              GLenum pname,
                              GLint param)
{
    ASSERT(framebuffer);

    switch (pname)
    {
        case GL_FRAMEBUFFER_DEFAULT_WIDTH:
            framebuffer->setDefaultWidth(context, param);
            break;
        case GL_FRAMEBUFFER_DEFAULT_HEIGHT:
            framebuffer->setDefaultHeight(context, param);
            break;
        case GL_FRAMEBUFFER_DEFAULT_SAMPLES:
            framebuffer->setDefaultSamples(context, param);
            break;
        case GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS:
            framebuffer->setDefaultFixedSampleLocations(context, ConvertToBool(param));
            break;
        case GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT:
            framebuffer->setDefaultLayers(param);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void SetProgramParameteri(Program *program, GLenum pname, GLint value)
{
    ASSERT(program);

    switch (pname)
    {
        case GL_PROGRAM_BINARY_RETRIEVABLE_HINT:
            program->setBinaryRetrievableHint(ConvertToBool(value));
            break;
        case GL_PROGRAM_SEPARABLE:
            program->setSeparable(ConvertToBool(value));
            break;
        default:
            UNREACHABLE();
            break;
    }
}

GLint GetUniformResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const auto &uniform = program->getUniformByIndex(index);
    GLenum resourceProp = GetUniformPropertyEnum(prop);
    switch (resourceProp)
    {
        case GL_TYPE:
        case GL_ARRAY_SIZE:
        case GL_NAME_LENGTH:
            return GetCommonVariableProperty(uniform, resourceProp);

        case GL_LOCATION:
            return program->getUniformLocation(uniform.name).value;

        case GL_BLOCK_INDEX:
            return (uniform.isAtomicCounter() ? -1 : uniform.bufferIndex);

        case GL_OFFSET:
            return uniform.blockInfo.offset;

        case GL_ARRAY_STRIDE:
            return uniform.blockInfo.arrayStride;

        case GL_MATRIX_STRIDE:
            return uniform.blockInfo.matrixStride;

        case GL_IS_ROW_MAJOR:
            return static_cast<GLint>(uniform.blockInfo.isRowMajorMatrix);

        case GL_REFERENCED_BY_VERTEX_SHADER:
            return uniform.isActive(ShaderType::Vertex);

        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return uniform.isActive(ShaderType::Fragment);

        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return uniform.isActive(ShaderType::Compute);

        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return uniform.isActive(ShaderType::Geometry);

        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return uniform.isActive(ShaderType::TessControl);

        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return uniform.isActive(ShaderType::TessEvaluation);

        case GL_ATOMIC_COUNTER_BUFFER_INDEX:
            return (uniform.isAtomicCounter() ? uniform.bufferIndex : -1);

        default:
            UNREACHABLE();
            return 0;
    }
}

GLint GetBufferVariableResourceProperty(const Program *program, GLuint index, const GLenum prop)
{
    const BufferVariable &bufferVariable = program->getBufferVariableByIndex(index);
    switch (prop)
    {
        case GL_TYPE:
        case GL_ARRAY_SIZE:
        case GL_NAME_LENGTH:
            return GetCommonVariableProperty(bufferVariable, prop);

        case GL_BLOCK_INDEX:
            return bufferVariable.bufferIndex;

        case GL_OFFSET:
            return bufferVariable.blockInfo.offset;

        case GL_ARRAY_STRIDE:
            return bufferVariable.blockInfo.arrayStride;

        case GL_MATRIX_STRIDE:
            return bufferVariable.blockInfo.matrixStride;

        case GL_IS_ROW_MAJOR:
            return static_cast<GLint>(bufferVariable.blockInfo.isRowMajorMatrix);

        case GL_REFERENCED_BY_VERTEX_SHADER:
            return bufferVariable.isActive(ShaderType::Vertex);

        case GL_REFERENCED_BY_FRAGMENT_SHADER:
            return bufferVariable.isActive(ShaderType::Fragment);

        case GL_REFERENCED_BY_COMPUTE_SHADER:
            return bufferVariable.isActive(ShaderType::Compute);

        case GL_REFERENCED_BY_GEOMETRY_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::Geometry);

        case GL_REFERENCED_BY_TESS_CONTROL_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::TessControl);

        case GL_REFERENCED_BY_TESS_EVALUATION_SHADER_EXT:
            return bufferVariable.isActive(ShaderType::TessEvaluation);

        case GL_TOP_LEVEL_ARRAY_SIZE:
            return bufferVariable.topLevelArraySize;

        case GL_TOP_LEVEL_ARRAY_STRIDE:
            return bufferVariable.blockInfo.topLevelArrayStride;

        default:
            UNREACHABLE();
            return 0;
    }
}

GLuint QueryProgramResourceIndex(const Program *program,
                                 GLenum programInterface,
                                 const GLchar *name)
{
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return program->getInputResourceIndex(name);

        case GL_PROGRAM_OUTPUT:
            return program->getOutputResourceIndex(name);

        case GL_UNIFORM:
            return program->getState().getUniformIndexFromName(name);

        case GL_BUFFER_VARIABLE:
            return program->getState().getBufferVariableIndexFromName(name);

        case GL_SHADER_STORAGE_BLOCK:
            return program->getShaderStorageBlockIndex(name);

        case GL_UNIFORM_BLOCK:
            return program->getUniformBlockIndex(name);

        case GL_TRANSFORM_FEEDBACK_VARYING:
            return program->getTransformFeedbackVaryingResourceIndex(name);

        default:
            UNREACHABLE();
            return GL_INVALID_INDEX;
    }
}

void QueryProgramResourceName(const Program *program,
                              GLenum programInterface,
                              GLuint index,
                              GLsizei bufSize,
                              GLsizei *length,
                              GLchar *name)
{
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            program->getInputResourceName(index, bufSize, length, name);
            break;

        case GL_PROGRAM_OUTPUT:
            program->getOutputResourceName(index, bufSize, length, name);
            break;

        case GL_UNIFORM:
            program->getUniformResourceName(index, bufSize, length, name);
            break;

        case GL_BUFFER_VARIABLE:
            program->getBufferVariableResourceName(index, bufSize, length, name);
            break;

        case GL_SHADER_STORAGE_BLOCK:
            program->getActiveShaderStorageBlockName(index, bufSize, length, name);
            break;

        case GL_UNIFORM_BLOCK:
            program->getActiveUniformBlockName({index}, bufSize, length, name);
            break;

        case GL_TRANSFORM_FEEDBACK_VARYING:
            program->getTransformFeedbackVarying(index, bufSize, length, nullptr, nullptr, name);
            break;

        default:
            UNREACHABLE();
    }
}

GLint QueryProgramResourceLocation(const Program *program,
                                   GLenum programInterface,
                                   const GLchar *name)
{
    switch (programInterface)
    {
        case GL_PROGRAM_INPUT:
            return program->getInputResourceLocation(name);

        case GL_PROGRAM_OUTPUT:
            return program->getOutputResourceLocation(name);

        case GL_UNIFORM:
            return program->getUniformLocation(name).value;

        default:
            UNREACHABLE();
            return -1;
    }
}

void QueryProgramResourceiv(const Program *program,
                            GLenum programInterface,
                            UniformBlockIndex index,
                            GLsizei propCount,
                            const GLenum *props,
                            GLsizei bufSize,
                            GLsizei *length,
                            GLint *params)
{
    if (!program->isLinked())
    {
        return;
    }

    if (length != nullptr)
    {
        *length = 0;
    }

    if (bufSize == 0)
    {
        // No room to write the results
        return;
    }

    GLsizei pos = 0;
    for (GLsizei i = 0; i < propCount; i++)
    {
        switch (programInterface)
        {
            case GL_PROGRAM_INPUT:
                params[i] = GetInputResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_PROGRAM_OUTPUT:
                params[i] = GetOutputResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_UNIFORM:
                params[i] = GetUniformResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_BUFFER_VARIABLE:
                params[i] = GetBufferVariableResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            case GL_UNIFORM_BLOCK:
                GetUniformBlockResourceProperty(program, index.value, props[i], params, bufSize,
                                                &pos);
                break;

            case GL_SHADER_STORAGE_BLOCK:
                GetShaderStorageBlockResourceProperty(program, index.value, props[i], params,
                                                      bufSize, &pos);
                break;

            case GL_ATOMIC_COUNTER_BUFFER:
                GetAtomicCounterBufferResourceProperty(program, index.value, props[i], params,
                                                       bufSize, &pos);
                break;

            case GL_TRANSFORM_FEEDBACK_VARYING:
                params[i] =
                    GetTransformFeedbackVaryingResourceProperty(program, index.value, props[i]);
                ++pos;
                break;

            default:
                UNREACHABLE();
                params[i] = GL_INVALID_VALUE;
        }
        if (pos == bufSize)
        {
            // Most properties return one value, but GL_ACTIVE_VARIABLES returns an array of values.
            // This checks not to break buffer bounds for such case.
            break;
        }
    }

    if (length != nullptr)
    {
        *length = pos;
    }
}

void QueryProgramInterfaceiv(const Program *program,
                             GLenum programInterface,
                             GLenum pname,
                             GLint *params)
{
    switch (pname)
    {
        case GL_ACTIVE_RESOURCES:
            *params = QueryProgramInterfaceActiveResources(program, programInterface);
            break;

        case GL_MAX_NAME_LENGTH:
            *params = QueryProgramInterfaceMaxNameLength(program, programInterface);
            break;

        case GL_MAX_NUM_ACTIVE_VARIABLES:
            *params = QueryProgramInterfaceMaxNumActiveVariables(program, programInterface);
            break;

        default:
            UNREACHABLE();
    }
}

angle::Result SetMemoryObjectParameteriv(const Context *context,
                                         MemoryObject *memoryObject,
                                         GLenum pname,
                                         const GLint *params)
{
    switch (pname)
    {
        case GL_DEDICATED_MEMORY_OBJECT_EXT:
            ANGLE_TRY(memoryObject->setDedicatedMemory(context, ConvertToBool(params[0])));
            break;

        default:
            UNREACHABLE();
    }

    return angle::Result::Continue;
}

void QueryMemoryObjectParameteriv(const MemoryObject *memoryObject, GLenum pname, GLint *params)
{
    switch (pname)
    {
        case GL_DEDICATED_MEMORY_OBJECT_EXT:
            *params = memoryObject->isDedicatedMemory();
            break;

        default:
            UNREACHABLE();
    }
}

ClientVertexArrayType ParamToVertexArrayType(GLenum param)
{
    switch (param)
    {
        case GL_VERTEX_ARRAY:
        case GL_VERTEX_ARRAY_BUFFER_BINDING:
        case GL_VERTEX_ARRAY_STRIDE:
        case GL_VERTEX_ARRAY_SIZE:
        case GL_VERTEX_ARRAY_TYPE:
        case GL_VERTEX_ARRAY_POINTER:
            return ClientVertexArrayType::Vertex;
        case GL_NORMAL_ARRAY:
        case GL_NORMAL_ARRAY_BUFFER_BINDING:
        case GL_NORMAL_ARRAY_STRIDE:
        case GL_NORMAL_ARRAY_TYPE:
        case GL_NORMAL_ARRAY_POINTER:
            return ClientVertexArrayType::Normal;
        case GL_COLOR_ARRAY:
        case GL_COLOR_ARRAY_BUFFER_BINDING:
        case GL_COLOR_ARRAY_STRIDE:
        case GL_COLOR_ARRAY_SIZE:
        case GL_COLOR_ARRAY_TYPE:
        case GL_COLOR_ARRAY_POINTER:
            return ClientVertexArrayType::Color;
        case GL_POINT_SIZE_ARRAY_OES:
        case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
        case GL_POINT_SIZE_ARRAY_STRIDE_OES:
        case GL_POINT_SIZE_ARRAY_TYPE_OES:
        case GL_POINT_SIZE_ARRAY_POINTER_OES:
            return ClientVertexArrayType::PointSize;
        case GL_TEXTURE_COORD_ARRAY:
        case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
        case GL_TEXTURE_COORD_ARRAY_STRIDE:
        case GL_TEXTURE_COORD_ARRAY_SIZE:
        case GL_TEXTURE_COORD_ARRAY_TYPE:
        case GL_TEXTURE_COORD_ARRAY_POINTER:
            return ClientVertexArrayType::TextureCoord;
        default:
            UNREACHABLE();
            return ClientVertexArrayType::InvalidEnum;
    }
}

void SetLightModelParameters(GLES1State *state, GLenum pname, const GLfloat *params)
{
    LightModelParameters &lightModel = state->lightModelParameters();

    switch (pname)
    {
        case GL_LIGHT_MODEL_AMBIENT:
            lightModel.color = ColorF::fromData(params);
            break;
        case GL_LIGHT_MODEL_TWO_SIDE:
            lightModel.twoSided = *params == 1.0f ? true : false;
            break;
        default:
            break;
    }
}

void GetLightModelParameters(const GLES1State *state, GLenum pname, GLfloat *params)
{
    const LightModelParameters &lightModel = state->lightModelParameters();

    switch (pname)
    {
        case GL_LIGHT_MODEL_TWO_SIDE:
            *params = lightModel.twoSided ? 1.0f : 0.0f;
            break;
        case GL_LIGHT_MODEL_AMBIENT:
            lightModel.color.writeData(params);
            break;
        default:
            break;
    }
}

bool IsLightModelTwoSided(const GLES1State *state)
{
    return state->lightModelParameters().twoSided;
}

void SetLightParameters(GLES1State *state,
                        GLenum light,
                        LightParameter pname,
                        const GLfloat *params)
{
    uint32_t lightIndex = light - GL_LIGHT0;

    LightParameters &lightParams = state->lightParameters(lightIndex);

    switch (pname)
    {
        case LightParameter::Ambient:
            lightParams.ambient = ColorF::fromData(params);
            break;
        case LightParameter::Diffuse:
            lightParams.diffuse = ColorF::fromData(params);
            break;
        case LightParameter::Specular:
            lightParams.specular = ColorF::fromData(params);
            break;
        case LightParameter::Position:
        {
            angle::Mat4 mv = state->getModelviewMatrix();
            angle::Vector4 transformedPos =
                mv.product(angle::Vector4(params[0], params[1], params[2], params[3]));
            lightParams.position[0] = transformedPos[0];
            lightParams.position[1] = transformedPos[1];
            lightParams.position[2] = transformedPos[2];
            lightParams.position[3] = transformedPos[3];
        }
        break;
        case LightParameter::SpotDirection:
        {
            angle::Mat4 mv = state->getModelviewMatrix();
            angle::Vector4 transformedPos =
                mv.product(angle::Vector4(params[0], params[1], params[2], 0.0f));
            lightParams.direction[0] = transformedPos[0];
            lightParams.direction[1] = transformedPos[1];
            lightParams.direction[2] = transformedPos[2];
        }
        break;
        case LightParameter::SpotExponent:
            lightParams.spotlightExponent = *params;
            break;
        case LightParameter::SpotCutoff:
            lightParams.spotlightCutoffAngle = *params;
            break;
        case LightParameter::ConstantAttenuation:
            lightParams.attenuationConst = *params;
            break;
        case LightParameter::LinearAttenuation:
            lightParams.attenuationLinear = *params;
            break;
        case LightParameter::QuadraticAttenuation:
            lightParams.attenuationQuadratic = *params;
            break;
        default:
            return;
    }
}

void GetLightParameters(const GLES1State *state,
                        GLenum light,
                        LightParameter pname,
                        GLfloat *params)
{
    uint32_t lightIndex                = light - GL_LIGHT0;
    const LightParameters &lightParams = state->lightParameters(lightIndex);

    switch (pname)
    {
        case LightParameter::Ambient:
            lightParams.ambient.writeData(params);
            break;
        case LightParameter::Diffuse:
            lightParams.diffuse.writeData(params);
            break;
        case LightParameter::Specular:
            lightParams.specular.writeData(params);
            break;
        case LightParameter::Position:
            memcpy(params, lightParams.position.data(), 4 * sizeof(GLfloat));
            break;
        case LightParameter::SpotDirection:
            memcpy(params, lightParams.direction.data(), 3 * sizeof(GLfloat));
            break;
        case LightParameter::SpotExponent:
            *params = lightParams.spotlightExponent;
            break;
        case LightParameter::SpotCutoff:
            *params = lightParams.spotlightCutoffAngle;
            break;
        case LightParameter::ConstantAttenuation:
            *params = lightParams.attenuationConst;
            break;
        case LightParameter::LinearAttenuation:
            *params = lightParams.attenuationLinear;
            break;
        case LightParameter::QuadraticAttenuation:
            *params = lightParams.attenuationQuadratic;
            break;
        default:
            break;
    }
}

void SetMaterialParameters(GLES1State *state,
                           GLenum face,
                           MaterialParameter pname,
                           const GLfloat *params)
{
    MaterialParameters &material = state->materialParameters();
    switch (pname)
    {
        case MaterialParameter::Ambient:
            material.ambient = ColorF::fromData(params);
            break;
        case MaterialParameter::Diffuse:
            material.diffuse = ColorF::fromData(params);
            break;
        case MaterialParameter::AmbientAndDiffuse:
            material.ambient = ColorF::fromData(params);
            material.diffuse = ColorF::fromData(params);
            break;
        case MaterialParameter::Specular:
            material.specular = ColorF::fromData(params);
            break;
        case MaterialParameter::Emission:
            material.emissive = ColorF::fromData(params);
            break;
        case MaterialParameter::Shininess:
            material.specularExponent = *params;
            break;
        default:
            return;
    }
}

void GetMaterialParameters(const GLES1State *state,
                           GLenum face,
                           MaterialParameter pname,
                           GLfloat *params)
{
    const ColorF &currentColor         = state->getCurrentColor();
    const MaterialParameters &material = state->materialParameters();
    const bool colorMaterialEnabled    = state->isColorMaterialEnabled();

    switch (pname)
    {
        case MaterialParameter::Ambient:
            if (colorMaterialEnabled)
            {
                currentColor.writeData(params);
            }
            else
            {
                material.ambient.writeData(params);
            }
            break;
        case MaterialParameter::Diffuse:
            if (colorMaterialEnabled)
            {
                currentColor.writeData(params);
            }
            else
            {
                material.diffuse.writeData(params);
            }
            break;
        case MaterialParameter::Specular:
            material.specular.writeData(params);
            break;
        case MaterialParameter::Emission:
            material.emissive.writeData(params);
            break;
        case MaterialParameter::Shininess:
            *params = material.specularExponent;
            break;
        default:
            return;
    }
}

unsigned int GetLightModelParameterCount(GLenum pname)
{
    switch (pname)
    {
        case GL_LIGHT_MODEL_AMBIENT:
            return 4;
        case GL_LIGHT_MODEL_TWO_SIDE:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

unsigned int GetLightParameterCount(LightParameter pname)
{
    switch (pname)
    {
        case LightParameter::Ambient:
        case LightParameter::Diffuse:
        case LightParameter::AmbientAndDiffuse:
        case LightParameter::Specular:
        case LightParameter::Position:
            return 4;
        case LightParameter::SpotDirection:
            return 3;
        case LightParameter::SpotExponent:
        case LightParameter::SpotCutoff:
        case LightParameter::ConstantAttenuation:
        case LightParameter::LinearAttenuation:
        case LightParameter::QuadraticAttenuation:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

unsigned int GetMaterialParameterCount(MaterialParameter pname)
{
    switch (pname)
    {
        case MaterialParameter::Ambient:
        case MaterialParameter::Diffuse:
        case MaterialParameter::AmbientAndDiffuse:
        case MaterialParameter::Specular:
        case MaterialParameter::Emission:
            return 4;
        case MaterialParameter::Shininess:
            return 1;
        default:
            UNREACHABLE();
            return 0;
    }
}

void SetFogParameters(GLES1State *state, GLenum pname, const GLfloat *params)
{
    FogParameters &fog = state->fogParameters();
    switch (pname)
    {
        case GL_FOG_MODE:
            fog.mode = FromGLenum<FogMode>(static_cast<GLenum>(params[0]));
            break;
        case GL_FOG_DENSITY:
            fog.density = params[0];
            break;
        case GL_FOG_START:
            fog.start = params[0];
            break;
        case GL_FOG_END:
            fog.end = params[0];
            break;
        case GL_FOG_COLOR:
            fog.color = ColorF::fromData(params);
            break;
        default:
            return;
    }
}

void GetFogParameters(const GLES1State *state, GLenum pname, GLfloat *params)
{
    const FogParameters &fog = state->fogParameters();
    switch (pname)
    {
        case GL_FOG_MODE:
            params[0] = static_cast<GLfloat>(ToGLenum(fog.mode));
            break;
        case GL_FOG_DENSITY:
            params[0] = fog.density;
            break;
        case GL_FOG_START:
            params[0] = fog.start;
            break;
        case GL_FOG_END:
            params[0] = fog.end;
            break;
        case GL_FOG_COLOR:
            fog.color.writeData(params);
            break;
        default:
            return;
    }
}

unsigned int GetFogParameterCount(GLenum pname)
{
    switch (pname)
    {
        case GL_FOG_MODE:
        case GL_FOG_DENSITY:
        case GL_FOG_START:
        case GL_FOG_END:
            return 1;
        case GL_FOG_COLOR:
            return 4;
        default:
            return 0;
    }
}

unsigned int GetTextureEnvParameterCount(TextureEnvParameter pname)
{
    switch (pname)
    {
        case TextureEnvParameter::Mode:
        case TextureEnvParameter::CombineRgb:
        case TextureEnvParameter::CombineAlpha:
        case TextureEnvParameter::Src0Rgb:
        case TextureEnvParameter::Src1Rgb:
        case TextureEnvParameter::Src2Rgb:
        case TextureEnvParameter::Src0Alpha:
        case TextureEnvParameter::Src1Alpha:
        case TextureEnvParameter::Src2Alpha:
        case TextureEnvParameter::Op0Rgb:
        case TextureEnvParameter::Op1Rgb:
        case TextureEnvParameter::Op2Rgb:
        case TextureEnvParameter::Op0Alpha:
        case TextureEnvParameter::Op1Alpha:
        case TextureEnvParameter::Op2Alpha:
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
        case TextureEnvParameter::PointCoordReplace:
            return 1;
        case TextureEnvParameter::Color:
            return 4;
        default:
            return 0;
    }
}

void ConvertTextureEnvFromInt(TextureEnvParameter pname, const GLint *input, GLfloat *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
            output[0] = static_cast<GLfloat>(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = input[i] / 255.0f;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvFromFixed(TextureEnvParameter pname, const GLfixed *input, GLfloat *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
            output[0] = ConvertFixedToFloat(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = ConvertFixedToFloat(input[i]);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvToInt(TextureEnvParameter pname, const GLfloat *input, GLint *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
            output[0] = static_cast<GLint>(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = static_cast<GLint>(input[i] * 255.0f);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void ConvertTextureEnvToFixed(TextureEnvParameter pname, const GLfloat *input, GLfixed *output)
{
    if (IsTextureEnvEnumParameter(pname))
    {
        ConvertGLenumValue(input[0], output);
        return;
    }

    switch (pname)
    {
        case TextureEnvParameter::RgbScale:
        case TextureEnvParameter::AlphaScale:
            output[0] = ConvertFloatToFixed(input[0]);
            break;
        case TextureEnvParameter::Color:
            for (int i = 0; i < 4; i++)
            {
                output[i] = ConvertFloatToFixed(input[i]);
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void SetTextureEnv(unsigned int unit,
                   GLES1State *state,
                   TextureEnvTarget target,
                   TextureEnvParameter pname,
                   const GLfloat *params)
{
    TextureEnvironmentParameters &env = state->textureEnvironment(unit);
    GLenum asEnum                     = ConvertToGLenum(params[0]);

    switch (target)
    {
        case TextureEnvTarget::Env:
            switch (pname)
            {
                case TextureEnvParameter::Mode:
                    env.mode = FromGLenum<TextureEnvMode>(asEnum);
                    break;
                case TextureEnvParameter::CombineRgb:
                    env.combineRgb = FromGLenum<TextureCombine>(asEnum);
                    break;
                case TextureEnvParameter::CombineAlpha:
                    env.combineAlpha = FromGLenum<TextureCombine>(asEnum);
                    break;
                case TextureEnvParameter::Src0Rgb:
                    env.src0Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src1Rgb:
                    env.src1Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src2Rgb:
                    env.src2Rgb = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src0Alpha:
                    env.src0Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src1Alpha:
                    env.src1Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Src2Alpha:
                    env.src2Alpha = FromGLenum<TextureSrc>(asEnum);
                    break;
                case TextureEnvParameter::Op0Rgb:
                    env.op0Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op1Rgb:
                    env.op1Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op2Rgb:
                    env.op2Rgb = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op0Alpha:
                    env.op0Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op1Alpha:
                    env.op1Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Op2Alpha:
                    env.op2Alpha = FromGLenum<TextureOp>(asEnum);
                    break;
                case TextureEnvParameter::Color:
                    env.color = ColorF::fromData(params);
                    break;
                case TextureEnvParameter::RgbScale:
                    env.rgbScale = params[0];
                    break;
                case TextureEnvParameter::AlphaScale:
                    env.alphaScale = params[0];
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::PointSprite:
            switch (pname)
            {
                case TextureEnvParameter::PointCoordReplace:
                    env.pointSpriteCoordReplace = static_cast<bool>(params[0]);
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void GetTextureEnv(unsigned int unit,
                   const GLES1State *state,
                   TextureEnvTarget target,
                   TextureEnvParameter pname,
                   GLfloat *params)
{
    const TextureEnvironmentParameters &env = state->textureEnvironment(unit);

    switch (target)
    {
        case TextureEnvTarget::Env:
            switch (pname)
            {
                case TextureEnvParameter::Mode:
                    ConvertPackedEnum(env.mode, params);
                    break;
                case TextureEnvParameter::CombineRgb:
                    ConvertPackedEnum(env.combineRgb, params);
                    break;
                case TextureEnvParameter::CombineAlpha:
                    ConvertPackedEnum(env.combineAlpha, params);
                    break;
                case TextureEnvParameter::Src0Rgb:
                    ConvertPackedEnum(env.src0Rgb, params);
                    break;
                case TextureEnvParameter::Src1Rgb:
                    ConvertPackedEnum(env.src1Rgb, params);
                    break;
                case TextureEnvParameter::Src2Rgb:
                    ConvertPackedEnum(env.src2Rgb, params);
                    break;
                case TextureEnvParameter::Src0Alpha:
                    ConvertPackedEnum(env.src0Alpha, params);
                    break;
                case TextureEnvParameter::Src1Alpha:
                    ConvertPackedEnum(env.src1Alpha, params);
                    break;
                case TextureEnvParameter::Src2Alpha:
                    ConvertPackedEnum(env.src2Alpha, params);
                    break;
                case TextureEnvParameter::Op0Rgb:
                    ConvertPackedEnum(env.op0Rgb, params);
                    break;
                case TextureEnvParameter::Op1Rgb:
                    ConvertPackedEnum(env.op1Rgb, params);
                    break;
                case TextureEnvParameter::Op2Rgb:
                    ConvertPackedEnum(env.op2Rgb, params);
                    break;
                case TextureEnvParameter::Op0Alpha:
                    ConvertPackedEnum(env.op0Alpha, params);
                    break;
                case TextureEnvParameter::Op1Alpha:
                    ConvertPackedEnum(env.op1Alpha, params);
                    break;
                case TextureEnvParameter::Op2Alpha:
                    ConvertPackedEnum(env.op2Alpha, params);
                    break;
                case TextureEnvParameter::Color:
                    env.color.writeData(params);
                    break;
                case TextureEnvParameter::RgbScale:
                    *params = env.rgbScale;
                    break;
                case TextureEnvParameter::AlphaScale:
                    *params = env.alphaScale;
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        case TextureEnvTarget::PointSprite:
            switch (pname)
            {
                case TextureEnvParameter::PointCoordReplace:
                    *params = static_cast<GLfloat>(env.pointSpriteCoordReplace);
                    break;
                default:
                    UNREACHABLE();
                    break;
            }
            break;
        default:
            UNREACHABLE();
            break;
    }
}

unsigned int GetPointParameterCount(PointParameter pname)
{
    switch (pname)
    {
        case PointParameter::PointSizeMin:
        case PointParameter::PointSizeMax:
        case PointParameter::PointFadeThresholdSize:
            return 1;
        case PointParameter::PointDistanceAttenuation:
            return 3;
        default:
            return 0;
    }
}

void SetPointParameter(GLES1State *state, PointParameter pname, const GLfloat *params)
{

    PointParameters &pointParams = state->pointParameters();

    switch (pname)
    {
        case PointParameter::PointSizeMin:
            pointParams.pointSizeMin = params[0];
            break;
        case PointParameter::PointSizeMax:
            pointParams.pointSizeMax = params[0];
            break;
        case PointParameter::PointFadeThresholdSize:
            pointParams.pointFadeThresholdSize = params[0];
            break;
        case PointParameter::PointDistanceAttenuation:
            for (unsigned int i = 0; i < 3; i++)
            {
                pointParams.pointDistanceAttenuation[i] = params[i];
            }
            break;
        default:
            UNREACHABLE();
    }
}

void GetPointParameter(const GLES1State *state, PointParameter pname, GLfloat *params)
{
    const PointParameters &pointParams = state->pointParameters();

    switch (pname)
    {
        case PointParameter::PointSizeMin:
            params[0] = pointParams.pointSizeMin;
            break;
        case PointParameter::PointSizeMax:
            params[0] = pointParams.pointSizeMax;
            break;
        case PointParameter::PointFadeThresholdSize:
            params[0] = pointParams.pointFadeThresholdSize;
            break;
        case PointParameter::PointDistanceAttenuation:
            for (unsigned int i = 0; i < 3; i++)
            {
                params[i] = pointParams.pointDistanceAttenuation[i];
            }
            break;
        default:
            UNREACHABLE();
    }
}

void SetPointSize(GLES1State *state, GLfloat size)
{
    PointParameters &params = state->pointParameters();
    params.pointSize        = size;
}

void GetPointSize(const GLES1State *state, GLfloat *sizeOut)
{
    const PointParameters &params = state->pointParameters();
    *sizeOut                      = params.pointSize;
}

unsigned int GetTexParameterCount(GLenum pname)
{
    switch (pname)
    {
        case GL_TEXTURE_CROP_RECT_OES:
        case GL_TEXTURE_BORDER_COLOR:
            return 4;
        case GL_TEXTURE_MAG_FILTER:
        case GL_TEXTURE_MIN_FILTER:
        case GL_TEXTURE_WRAP_S:
        case GL_TEXTURE_WRAP_T:
        case GL_TEXTURE_USAGE_ANGLE:
        case GL_TEXTURE_MAX_ANISOTROPY_EXT:
        case GL_TEXTURE_IMMUTABLE_FORMAT:
        case GL_TEXTURE_WRAP_R:
        case GL_TEXTURE_IMMUTABLE_LEVELS:
        case GL_TEXTURE_SWIZZLE_R:
        case GL_TEXTURE_SWIZZLE_G:
        case GL_TEXTURE_SWIZZLE_B:
        case GL_TEXTURE_SWIZZLE_A:
        case GL_TEXTURE_BASE_LEVEL:
        case GL_TEXTURE_MAX_LEVEL:
        case GL_TEXTURE_MIN_LOD:
        case GL_TEXTURE_MAX_LOD:
        case GL_TEXTURE_COMPARE_MODE:
        case GL_TEXTURE_COMPARE_FUNC:
        case GL_TEXTURE_SRGB_DECODE_EXT:
        case GL_DEPTH_STENCIL_TEXTURE_MODE:
        case GL_TEXTURE_NATIVE_ID_ANGLE:
            return 1;
        default:
            return 0;
    }
}

bool GetQueryParameterInfo(const State &glState,
                           GLenum pname,
                           GLenum *type,
                           unsigned int *numParams)
{
    const Caps &caps             = glState.getCaps();
    const Extensions &extensions = glState.getExtensions();
    GLint clientMajorVersion     = glState.getClientMajorVersion();

    // Please note: the query type returned for DEPTH_CLEAR_VALUE in this implementation
    // is FLOAT rather than INT, as would be suggested by the GL ES 2.0 spec. This is due
    // to the fact that it is stored internally as a float, and so would require conversion
    // if returned from Context::getIntegerv. Since this conversion is already implemented
    // in the case that one calls glGetIntegerv to retrieve a float-typed state variable, we
    // place DEPTH_CLEAR_VALUE with the floats. This should make no difference to the calling
    // application.
    switch (pname)
    {
        case GL_COMPRESSED_TEXTURE_FORMATS:
        {
            *type      = GL_INT;
            *numParams = static_cast<unsigned int>(caps.compressedTextureFormats.size());
            return true;
        }
        case GL_SHADER_BINARY_FORMATS:
        {
            *type      = GL_INT;
            *numParams = static_cast<unsigned int>(caps.shaderBinaryFormats.size());
            return true;
        }

        case GL_MAX_VERTEX_ATTRIBS:
        case GL_MAX_VERTEX_UNIFORM_VECTORS:
        case GL_MAX_VARYING_VECTORS:
        case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
        case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
        case GL_MAX_TEXTURE_IMAGE_UNITS:
        case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
        case GL_MAX_RENDERBUFFER_SIZE:
        case GL_NUM_SHADER_BINARY_FORMATS:
        case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
        case GL_ARRAY_BUFFER_BINDING:
        case GL_FRAMEBUFFER_BINDING:  // GL_FRAMEBUFFER_BINDING now equivalent to
                                      // GL_DRAW_FRAMEBUFFER_BINDING
        case GL_RENDERBUFFER_BINDING:
        case GL_CURRENT_PROGRAM:
        case GL_PACK_ALIGNMENT:
        case GL_UNPACK_ALIGNMENT:
        case GL_GENERATE_MIPMAP_HINT:
        case GL_TEXTURE_FILTERING_HINT_CHROMIUM:
        case GL_RED_BITS:
        case GL_GREEN_BITS:
        case GL_BLUE_BITS:
        case GL_ALPHA_BITS:
        case GL_DEPTH_BITS:
        case GL_STENCIL_BITS:
        case GL_ELEMENT_ARRAY_BUFFER_BINDING:
        case GL_CULL_FACE_MODE:
        case GL_FRONT_FACE:
        case GL_ACTIVE_TEXTURE:
        case GL_STENCIL_FUNC:
        case GL_STENCIL_VALUE_MASK:
        case GL_STENCIL_REF:
        case GL_STENCIL_FAIL:
        case GL_STENCIL_PASS_DEPTH_FAIL:
        case GL_STENCIL_PASS_DEPTH_PASS:
        case GL_STENCIL_BACK_FUNC:
        case GL_STENCIL_BACK_VALUE_MASK:
        case GL_STENCIL_BACK_REF:
        case GL_STENCIL_BACK_FAIL:
        case GL_STENCIL_BACK_PASS_DEPTH_FAIL:
        case GL_STENCIL_BACK_PASS_DEPTH_PASS:
        case GL_DEPTH_FUNC:
        case GL_BLEND_SRC_RGB:
        case GL_BLEND_SRC_ALPHA:
        case GL_BLEND_DST_RGB:
        case GL_BLEND_DST_ALPHA:
        case GL_BLEND_EQUATION_RGB:
        case GL_BLEND_EQUATION_ALPHA:
        case GL_STENCIL_WRITEMASK:
        case GL_STENCIL_BACK_WRITEMASK:
        case GL_STENCIL_CLEAR_VALUE:
        case GL_SUBPIXEL_BITS:
        case GL_MAX_TEXTURE_SIZE:
        case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
        case GL_SAMPLE_BUFFERS:
        case GL_SAMPLES:
        case GL_IMPLEMENTATION_COLOR_READ_TYPE:
        case GL_IMPLEMENTATION_COLOR_READ_FORMAT:
        case GL_TEXTURE_BINDING_2D:
        case GL_TEXTURE_BINDING_CUBE_MAP:
        case GL_RESET_NOTIFICATION_STRATEGY_EXT:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_PACK_REVERSE_ROW_ORDER_ANGLE:
        {
            if (!extensions.packReverseRowOrder)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE:
        case GL_TEXTURE_BINDING_RECTANGLE_ANGLE:
        {
            if (!extensions.textureRectangle)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_MAX_DRAW_BUFFERS_EXT:
        case GL_MAX_COLOR_ATTACHMENTS_EXT:
        {
            if ((clientMajorVersion < 3) && !extensions.drawBuffers)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }
        case GL_MAX_VIEWPORT_DIMS:
        {
            *type      = GL_INT;
            *numParams = 2;
            return true;
        }
        case GL_VIEWPORT:
        case GL_SCISSOR_BOX:
        {
            *type      = GL_INT;
            *numParams = 4;
            return true;
        }
        case GL_SHADER_COMPILER:
        case GL_SAMPLE_COVERAGE_INVERT:
        case GL_DEPTH_WRITEMASK:
        case GL_CULL_FACE:                 // CULL_FACE through DITHER are natural to IsEnabled,
        case GL_POLYGON_OFFSET_FILL:       // but can be retrieved through the Get{Type}v queries.
        case GL_SAMPLE_ALPHA_TO_COVERAGE:  // For this purpose, they are treated here as
                                           // bool-natural
        case GL_SAMPLE_COVERAGE:
        case GL_SCISSOR_TEST:
        case GL_STENCIL_TEST:
        case GL_DEPTH_TEST:
        case GL_BLEND:
        case GL_DITHER:
        case GL_CONTEXT_ROBUST_ACCESS_EXT:
        {
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }
        case GL_COLOR_WRITEMASK:
        {
            *type      = GL_BOOL;
            *numParams = 4;
            return true;
        }
        case GL_POLYGON_OFFSET_FACTOR:
        case GL_POLYGON_OFFSET_UNITS:
        case GL_SAMPLE_COVERAGE_VALUE:
        case GL_DEPTH_CLEAR_VALUE:
        case GL_LINE_WIDTH:
        {
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        }
        case GL_ALIASED_LINE_WIDTH_RANGE:
        case GL_ALIASED_POINT_SIZE_RANGE:
        case GL_DEPTH_RANGE:
        {
            *type      = GL_FLOAT;
            *numParams = 2;
            return true;
        }
        case GL_COLOR_CLEAR_VALUE:
        case GL_BLEND_COLOR:
        {
            *type      = GL_FLOAT;
            *numParams = 4;
            return true;
        }
        case GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT:
            if (!extensions.textureFilterAnisotropic)
            {
                return false;
            }
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        case GL_TIMESTAMP_EXT:
            if (!extensions.disjointTimerQuery)
            {
                return false;
            }
            *type      = GL_INT_64_ANGLEX;
            *numParams = 1;
            return true;
        case GL_GPU_DISJOINT_EXT:
            if (!extensions.disjointTimerQuery)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_COVERAGE_MODULATION_CHROMIUM:
            if (!extensions.framebufferMixedSamples)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_TEXTURE_BINDING_EXTERNAL_OES:
            if (!extensions.eglStreamConsumerExternalNV && !extensions.eglImageExternalOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_CLIP_DISTANCES_EXT:  // case GL_MAX_CLIP_PLANES
            if (clientMajorVersion < 2)
            {
                break;
            }
            if (!extensions.clipDistanceAPPLE)
            {
                // NOTE(hqle): if client version is 1. GL_MAX_CLIP_DISTANCES_EXT is equal
                // to GL_MAX_CLIP_PLANES which is a valid enum.
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_CULL_DISTANCES_EXT:
        case GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT:
            if (!extensions.clipCullDistanceEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_CLIP_ORIGIN_EXT:
        case GL_CLIP_DEPTH_MODE_EXT:
            if (!extensions.clipControlEXT)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
    }

    if (glState.getClientType() == EGL_OPENGL_API)
    {
        switch (pname)
        {
            case GL_CONTEXT_FLAGS:
            case GL_CONTEXT_PROFILE_MASK:
            {
                *type      = GL_INT;
                *numParams = 1;
                return true;
            }
        }
    }

    if (extensions.debug)
    {
        switch (pname)
        {
            case GL_DEBUG_LOGGED_MESSAGES:
            case GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH:
            case GL_DEBUG_GROUP_STACK_DEPTH:
            case GL_MAX_DEBUG_MESSAGE_LENGTH:
            case GL_MAX_DEBUG_LOGGED_MESSAGES:
            case GL_MAX_DEBUG_GROUP_STACK_DEPTH:
            case GL_MAX_LABEL_LENGTH:
                *type      = GL_INT;
                *numParams = 1;
                return true;

            case GL_DEBUG_OUTPUT_SYNCHRONOUS:
            case GL_DEBUG_OUTPUT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.multisampleCompatibility)
    {
        switch (pname)
        {
            case GL_MULTISAMPLE_EXT:
            case GL_SAMPLE_ALPHA_TO_ONE_EXT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.bindGeneratesResource)
    {
        switch (pname)
        {
            case GL_BIND_GENERATES_RESOURCE_CHROMIUM:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.clientArrays)
    {
        switch (pname)
        {
            case GL_CLIENT_ARRAYS_ANGLE:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.sRGBWriteControl)
    {
        switch (pname)
        {
            case GL_FRAMEBUFFER_SRGB_EXT:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.robustResourceInitialization && pname == GL_ROBUST_RESOURCE_INITIALIZATION_ANGLE)
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    if (extensions.programCacheControl && pname == GL_PROGRAM_CACHE_ENABLED_ANGLE)
    {
        *type      = GL_BOOL;
        *numParams = 1;
        return true;
    }

    if (extensions.parallelShaderCompile && pname == GL_MAX_SHADER_COMPILER_THREADS_KHR)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (extensions.blendFuncExtended && pname == GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
    switch (pname)
    {
        // GL_DRAW_FRAMEBUFFER_BINDING equivalent to GL_FRAMEBUFFER_BINDING
        case GL_READ_FRAMEBUFFER_BINDING:
            if ((clientMajorVersion < 3) && !extensions.framebufferBlitAny())
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;

        case GL_NUM_PROGRAM_BINARY_FORMATS_OES:
            if ((clientMajorVersion < 3) && !extensions.getProgramBinaryOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;

        case GL_PROGRAM_BINARY_FORMATS_OES:
            if ((clientMajorVersion < 3) && !extensions.getProgramBinaryOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = static_cast<unsigned int>(caps.programBinaryFormats.size());
            return true;

        case GL_PACK_ROW_LENGTH:
        case GL_PACK_SKIP_ROWS:
        case GL_PACK_SKIP_PIXELS:
            if ((clientMajorVersion < 3) && !extensions.packSubimage)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_UNPACK_ROW_LENGTH:
        case GL_UNPACK_SKIP_ROWS:
        case GL_UNPACK_SKIP_PIXELS:
            if ((clientMajorVersion < 3) && !extensions.unpackSubimage)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_VERTEX_ARRAY_BINDING:
            if ((clientMajorVersion < 3) && !extensions.vertexArrayObjectOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_PIXEL_PACK_BUFFER_BINDING:
        case GL_PIXEL_UNPACK_BUFFER_BINDING:
            if ((clientMajorVersion < 3) && !extensions.pixelBufferObjectNV)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_SAMPLES:
        {
            static_assert(GL_MAX_SAMPLES_ANGLE == GL_MAX_SAMPLES,
                          "GL_MAX_SAMPLES_ANGLE not equal to GL_MAX_SAMPLES");
            if ((clientMajorVersion < 3) &&
                !(extensions.framebufferMultisample || extensions.multisampledRenderToTexture))
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;

            case GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
                if ((clientMajorVersion < 3) && !extensions.standardDerivativesOES)
                {
                    return false;
                }
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
        case GL_TEXTURE_BINDING_3D:
            if ((clientMajorVersion < 3) && !extensions.texture3DOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_3D_TEXTURE_SIZE:
            if ((clientMajorVersion < 3) && !extensions.texture3DOES)
            {
                return false;
            }
            *type      = GL_INT;
            *numParams = 1;
            return true;
    }

    if (pname >= GL_DRAW_BUFFER0_EXT && pname <= GL_DRAW_BUFFER15_EXT)
    {
        if ((glState.getClientVersion() < Version(3, 0)) && !extensions.drawBuffers)
        {
            return false;
        }
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if ((extensions.multiview2 || extensions.multiview) && pname == GL_MAX_VIEWS_OVR)
    {
        *type      = GL_INT;
        *numParams = 1;
        return true;
    }

    if (glState.getClientVersion() < Version(2, 0))
    {
        switch (pname)
        {
            case GL_ALPHA_TEST_FUNC:
            case GL_CLIENT_ACTIVE_TEXTURE:
            case GL_MATRIX_MODE:
            case GL_MAX_TEXTURE_UNITS:
            case GL_MAX_MODELVIEW_STACK_DEPTH:
            case GL_MAX_PROJECTION_STACK_DEPTH:
            case GL_MAX_TEXTURE_STACK_DEPTH:
            case GL_MAX_LIGHTS:
            case GL_MAX_CLIP_PLANES:
            case GL_VERTEX_ARRAY_STRIDE:
            case GL_NORMAL_ARRAY_STRIDE:
            case GL_COLOR_ARRAY_STRIDE:
            case GL_TEXTURE_COORD_ARRAY_STRIDE:
            case GL_VERTEX_ARRAY_SIZE:
            case GL_COLOR_ARRAY_SIZE:
            case GL_TEXTURE_COORD_ARRAY_SIZE:
            case GL_VERTEX_ARRAY_TYPE:
            case GL_NORMAL_ARRAY_TYPE:
            case GL_COLOR_ARRAY_TYPE:
            case GL_TEXTURE_COORD_ARRAY_TYPE:
            case GL_VERTEX_ARRAY_BUFFER_BINDING:
            case GL_NORMAL_ARRAY_BUFFER_BINDING:
            case GL_COLOR_ARRAY_BUFFER_BINDING:
            case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
            case GL_POINT_SIZE_ARRAY_STRIDE_OES:
            case GL_POINT_SIZE_ARRAY_TYPE_OES:
            case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
            case GL_SHADE_MODEL:
            case GL_MODELVIEW_STACK_DEPTH:
            case GL_PROJECTION_STACK_DEPTH:
            case GL_TEXTURE_STACK_DEPTH:
            case GL_LOGIC_OP_MODE:
            case GL_BLEND_SRC:
            case GL_BLEND_DST:
            case GL_PERSPECTIVE_CORRECTION_HINT:
            case GL_POINT_SMOOTH_HINT:
            case GL_LINE_SMOOTH_HINT:
            case GL_FOG_HINT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
            case GL_ALPHA_TEST_REF:
            case GL_FOG_DENSITY:
            case GL_FOG_START:
            case GL_FOG_END:
            case GL_FOG_MODE:
            case GL_POINT_SIZE:
            case GL_POINT_SIZE_MIN:
            case GL_POINT_SIZE_MAX:
            case GL_POINT_FADE_THRESHOLD_SIZE:
                *type      = GL_FLOAT;
                *numParams = 1;
                return true;
            case GL_SMOOTH_POINT_SIZE_RANGE:
            case GL_SMOOTH_LINE_WIDTH_RANGE:
                *type      = GL_FLOAT;
                *numParams = 2;
                return true;
            case GL_CURRENT_COLOR:
            case GL_CURRENT_TEXTURE_COORDS:
            case GL_LIGHT_MODEL_AMBIENT:
            case GL_FOG_COLOR:
                *type      = GL_FLOAT;
                *numParams = 4;
                return true;
            case GL_CURRENT_NORMAL:
            case GL_POINT_DISTANCE_ATTENUATION:
                *type      = GL_FLOAT;
                *numParams = 3;
                return true;
            case GL_MODELVIEW_MATRIX:
            case GL_PROJECTION_MATRIX:
            case GL_TEXTURE_MATRIX:
                *type      = GL_FLOAT;
                *numParams = 16;
                return true;
            case GL_LIGHT_MODEL_TWO_SIDE:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() < Version(3, 0))
    {
        return false;
    }

    // Check for ES3.0+ parameter names
    switch (pname)
    {
        case GL_MAX_UNIFORM_BUFFER_BINDINGS:
        case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT:
        case GL_UNIFORM_BUFFER_BINDING:
        case GL_TRANSFORM_FEEDBACK_BINDING:
        case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
        case GL_COPY_READ_BUFFER_BINDING:
        case GL_COPY_WRITE_BUFFER_BINDING:
        case GL_SAMPLER_BINDING:
        case GL_READ_BUFFER:
        case GL_TEXTURE_BINDING_3D:
        case GL_TEXTURE_BINDING_2D_ARRAY:
        case GL_MAX_ARRAY_TEXTURE_LAYERS:
        case GL_MAX_VERTEX_UNIFORM_BLOCKS:
        case GL_MAX_FRAGMENT_UNIFORM_BLOCKS:
        case GL_MAX_COMBINED_UNIFORM_BLOCKS:
        case GL_MAX_VERTEX_OUTPUT_COMPONENTS:
        case GL_MAX_FRAGMENT_INPUT_COMPONENTS:
        case GL_MAX_VARYING_COMPONENTS:
        case GL_MAX_VERTEX_UNIFORM_COMPONENTS:
        case GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
        case GL_MIN_PROGRAM_TEXEL_OFFSET:
        case GL_MAX_PROGRAM_TEXEL_OFFSET:
        case GL_NUM_EXTENSIONS:
        case GL_MAJOR_VERSION:
        case GL_MINOR_VERSION:
        case GL_MAX_ELEMENTS_INDICES:
        case GL_MAX_ELEMENTS_VERTICES:
        case GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS:
        case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS:
        case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS:
        case GL_UNPACK_IMAGE_HEIGHT:
        case GL_UNPACK_SKIP_IMAGES:
        case GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES:
        {
            *type      = GL_INT;
            *numParams = 1;
            return true;
        }

        case GL_MAX_ELEMENT_INDEX:
        case GL_MAX_UNIFORM_BLOCK_SIZE:
        case GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
        case GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
        case GL_MAX_SERVER_WAIT_TIMEOUT:
        {
            *type      = GL_INT_64_ANGLEX;
            *numParams = 1;
            return true;
        }

        case GL_TRANSFORM_FEEDBACK_ACTIVE:
        case GL_TRANSFORM_FEEDBACK_PAUSED:
        case GL_PRIMITIVE_RESTART_FIXED_INDEX:
        case GL_RASTERIZER_DISCARD:
        {
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        }

        case GL_MAX_TEXTURE_LOD_BIAS:
        case GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES:
        case GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES:
        {
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
        }
    }

    if (extensions.requestExtension)
    {
        switch (pname)
        {
            case GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.textureMultisample)
    {
        switch (pname)
        {
            case GL_MAX_COLOR_TEXTURE_SAMPLES_ANGLE:
            case GL_MAX_INTEGER_SAMPLES_ANGLE:
            case GL_MAX_DEPTH_TEXTURE_SAMPLES_ANGLE:
            case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ANGLE:
            case GL_MAX_SAMPLE_MASK_WORDS:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.textureCubeMapArrayAny())
    {
        switch (pname)
        {
            case GL_TEXTURE_BINDING_CUBE_MAP_ARRAY:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.textureBufferAny())
    {
        switch (pname)
        {
            case GL_TEXTURE_BUFFER_BINDING:
            case GL_TEXTURE_BINDING_BUFFER:
            case GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT:
            case GL_MAX_TEXTURE_BUFFER_SIZE:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (glState.getClientVersion() < Version(3, 1))
    {
        return false;
    }

    // Check for ES3.1+ parameter names
    switch (pname)
    {
        case GL_ATOMIC_COUNTER_BUFFER_BINDING:
        case GL_DRAW_INDIRECT_BUFFER_BINDING:
        case GL_DISPATCH_INDIRECT_BUFFER_BINDING:
        case GL_MAX_FRAMEBUFFER_WIDTH:
        case GL_MAX_FRAMEBUFFER_HEIGHT:
        case GL_MAX_FRAMEBUFFER_SAMPLES:
        case GL_MAX_SAMPLE_MASK_WORDS:
        case GL_MAX_COLOR_TEXTURE_SAMPLES:
        case GL_MAX_DEPTH_TEXTURE_SAMPLES:
        case GL_MAX_INTEGER_SAMPLES:
        case GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET:
        case GL_MAX_VERTEX_ATTRIB_BINDINGS:
        case GL_MAX_VERTEX_ATTRIB_STRIDE:
        case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_VERTEX_ATOMIC_COUNTERS:
        case GL_MAX_VERTEX_IMAGE_UNIFORMS:
        case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS:
        case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_FRAGMENT_ATOMIC_COUNTERS:
        case GL_MAX_FRAGMENT_IMAGE_UNIFORMS:
        case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS:
        case GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET:
        case GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET:
        case GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS:
        case GL_MAX_COMPUTE_UNIFORM_BLOCKS:
        case GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS:
        case GL_MAX_COMPUTE_SHARED_MEMORY_SIZE:
        case GL_MAX_COMPUTE_UNIFORM_COMPONENTS:
        case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_COMPUTE_ATOMIC_COUNTERS:
        case GL_MAX_COMPUTE_IMAGE_UNIFORMS:
        case GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS:
        case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS:
        case GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES:
        case GL_MAX_UNIFORM_LOCATIONS:
        case GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS:
        case GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE:
        case GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS:
        case GL_MAX_COMBINED_ATOMIC_COUNTERS:
        case GL_MAX_IMAGE_UNITS:
        case GL_MAX_COMBINED_IMAGE_UNIFORMS:
        case GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS:
        case GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS:
        case GL_SHADER_STORAGE_BUFFER_BINDING:
        case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
        case GL_TEXTURE_BINDING_2D_MULTISAMPLE:
        case GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY:
        case GL_PROGRAM_PIPELINE_BINDING:
            *type      = GL_INT;
            *numParams = 1;
            return true;
        case GL_MAX_SHADER_STORAGE_BLOCK_SIZE:
            *type      = GL_INT_64_ANGLEX;
            *numParams = 1;
            return true;
        case GL_SAMPLE_MASK:
        case GL_SAMPLE_SHADING:
            *type      = GL_BOOL;
            *numParams = 1;
            return true;
        case GL_MIN_SAMPLE_SHADING_VALUE:
            *type      = GL_FLOAT;
            *numParams = 1;
            return true;
    }

    if (extensions.geometryShader)
    {
        switch (pname)
        {
            case GL_MAX_FRAMEBUFFER_LAYERS_EXT:
            case GL_LAYER_PROVOKING_VERTEX_EXT:
            case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT:
            case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT:
            case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT:
            case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT:
            case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT:
            case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    if (extensions.tessellationShaderEXT)
    {
        switch (pname)
        {
            case GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED:
                *type      = GL_BOOL;
                *numParams = 1;
                return true;
            case GL_PATCH_VERTICES:
            case GL_MAX_PATCH_VERTICES_EXT:
            case GL_MAX_TESS_GEN_LEVEL_EXT:
            case GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_EXT:
            case GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_PATCH_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_EXT:
            case GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_EXT:
            case GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_EXT:
            case GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_EXT:
            case GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_EXT:
            case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_EXT:
            case GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_EXT:
            case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_EXT:
            case GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_EXT:
            case GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_EXT:
            case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_EXT:
            case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_EXT:
                *type      = GL_INT;
                *numParams = 1;
                return true;
        }
    }

    return false;
}

void QueryProgramPipelineiv(const Context *context,
                            ProgramPipeline *programPipeline,
                            GLenum pname,
                            GLint *params)
{
    if (!params)
    {
        // Can't write the result anywhere, so just return immediately.
        return;
    }

    switch (pname)
    {
        case GL_ACTIVE_PROGRAM:
        {
            // the name of the active program object of the program pipeline object is returned in
            // params
            *params = 0;
            if (programPipeline)
            {
                const Program *program = programPipeline->getActiveShaderProgram();
                if (program)
                {
                    *params = program->id().value;
                }
            }
            break;
        }

        case GL_VERTEX_SHADER:
        {
            // the name of the current program object for the vertex shader type of the program
            // pipeline object is returned in params
            *params = 0;
            if (programPipeline)
            {
                const Program *program = programPipeline->getShaderProgram(ShaderType::Vertex);
                if (program)
                {
                    *params = program->id().value;
                }
            }
            break;
        }

        case GL_FRAGMENT_SHADER:
        {
            // the name of the current program object for the fragment shader type of the program
            // pipeline object is returned in params
            *params = 0;
            if (programPipeline)
            {
                const Program *program = programPipeline->getShaderProgram(ShaderType::Fragment);
                if (program)
                {
                    *params = program->id().value;
                }
            }
            break;
        }

        case GL_COMPUTE_SHADER:
        {
            // the name of the current program object for the compute shader type of the program
            // pipeline object is returned in params
            *params = 0;
            if (programPipeline)
            {
                const Program *program = programPipeline->getShaderProgram(ShaderType::Compute);
                if (program)
                {
                    *params = program->id().value;
                }
            }
            break;
        }

        case GL_INFO_LOG_LENGTH:
        {
            // the length of the info log, including the null terminator, is returned in params. If
            // there is no info log, zero is returned.
            *params = 0;
            if (programPipeline)
            {
                *params = programPipeline->getExecutable().getInfoLogLength();
            }
            break;
        }

        case GL_VALIDATE_STATUS:
        {
            // the validation status of pipeline, as determined by glValidateProgramPipeline, is
            // returned in params
            *params = 0;
            if (programPipeline)
            {
                *params = programPipeline->isValid();
            }
            break;
        }

        default:
            break;
    }
}

}  // namespace gl

namespace egl
{

void QueryConfigAttrib(const Config *config, EGLint attribute, EGLint *value)
{
    ASSERT(config != nullptr);
    switch (attribute)
    {
        case EGL_BUFFER_SIZE:
            *value = config->bufferSize;
            break;
        case EGL_ALPHA_SIZE:
            *value = config->alphaSize;
            break;
        case EGL_BLUE_SIZE:
            *value = config->blueSize;
            break;
        case EGL_GREEN_SIZE:
            *value = config->greenSize;
            break;
        case EGL_RED_SIZE:
            *value = config->redSize;
            break;
        case EGL_DEPTH_SIZE:
            *value = config->depthSize;
            break;
        case EGL_STENCIL_SIZE:
            *value = config->stencilSize;
            break;
        case EGL_CONFIG_CAVEAT:
            *value = config->configCaveat;
            break;
        case EGL_CONFIG_ID:
            *value = config->configID;
            break;
        case EGL_LEVEL:
            *value = config->level;
            break;
        case EGL_NATIVE_RENDERABLE:
            *value = config->nativeRenderable;
            break;
        case EGL_NATIVE_VISUAL_ID:
            *value = config->nativeVisualID;
            break;
        case EGL_NATIVE_VISUAL_TYPE:
            *value = config->nativeVisualType;
            break;
        case EGL_SAMPLES:
            *value = config->samples;
            break;
        case EGL_SAMPLE_BUFFERS:
            *value = config->sampleBuffers;
            break;
        case EGL_SURFACE_TYPE:
            *value = config->surfaceType;
            break;
        case EGL_BIND_TO_TEXTURE_TARGET_ANGLE:
            *value = config->bindToTextureTarget;
            break;
        case EGL_TRANSPARENT_TYPE:
            *value = config->transparentType;
            break;
        case EGL_TRANSPARENT_BLUE_VALUE:
            *value = config->transparentBlueValue;
            break;
        case EGL_TRANSPARENT_GREEN_VALUE:
            *value = config->transparentGreenValue;
            break;
        case EGL_TRANSPARENT_RED_VALUE:
            *value = config->transparentRedValue;
            break;
        case EGL_BIND_TO_TEXTURE_RGB:
            *value = config->bindToTextureRGB;
            break;
        case EGL_BIND_TO_TEXTURE_RGBA:
            *value = config->bindToTextureRGBA;
            break;
        case EGL_MIN_SWAP_INTERVAL:
            *value = config->minSwapInterval;
            break;
        case EGL_MAX_SWAP_INTERVAL:
            *value = config->maxSwapInterval;
            break;
        case EGL_LUMINANCE_SIZE:
            *value = config->luminanceSize;
            break;
        case EGL_ALPHA_MASK_SIZE:
            *value = config->alphaMaskSize;
            break;
        case EGL_COLOR_BUFFER_TYPE:
            *value = config->colorBufferType;
            break;
        case EGL_RENDERABLE_TYPE:
            *value = config->renderableType;
            break;
        case EGL_MATCH_NATIVE_PIXMAP:
            *value = false;
            UNIMPLEMENTED();
            break;
        case EGL_CONFORMANT:
            *value = config->conformant;
            break;
        case EGL_MAX_PBUFFER_WIDTH:
            *value = config->maxPBufferWidth;
            break;
        case EGL_MAX_PBUFFER_HEIGHT:
            *value = config->maxPBufferHeight;
            break;
        case EGL_MAX_PBUFFER_PIXELS:
            *value = config->maxPBufferPixels;
            break;
        case EGL_OPTIMAL_SURFACE_ORIENTATION_ANGLE:
            *value = config->optimalOrientation;
            break;
        case EGL_COLOR_COMPONENT_TYPE_EXT:
            *value = config->colorComponentType;
            break;
        case EGL_RECORDABLE_ANDROID:
            *value = config->recordable;
            break;
        case EGL_FRAMEBUFFER_TARGET_ANDROID:
            *value = config->framebufferTarget;
            break;
        default:
            UNREACHABLE();
            break;
    }
}

void QueryContextAttrib(const gl::Context *context, EGLint attribute, EGLint *value)
{
    switch (attribute)
    {
        case EGL_CONFIG_ID:
            if (context->getConfig() != EGL_NO_CONFIG_KHR)
            {
                *value = context->getConfig()->configID;
            }
            else
            {
                *value = 0;
            }
            break;
        case EGL_CONTEXT_CLIENT_TYPE:
            *value = context->getClientType();
            break;
        case EGL_CONTEXT_CLIENT_VERSION:
            *value = context->getClientMajorVersion();
            break;
        case EGL_RENDER_BUFFER:
            *value = context->getRenderBuffer();
            break;
        case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
            *value = context->isRobustResourceInitEnabled();
            break;
        case EGL_CONTEXT_PRIORITY_LEVEL_IMG:
            *value = static_cast<EGLint>(context->getContextPriority());
            break;
        default:
            UNREACHABLE();
            break;
    }
}

egl::Error QuerySurfaceAttrib(const Display *display,
                              const gl::Context *context,
                              const Surface *surface,
                              EGLint attribute,
                              EGLint *value)
{
    switch (attribute)
    {
        case EGL_GL_COLORSPACE:
            *value = surface->getGLColorspace();
            break;
        case EGL_VG_ALPHA_FORMAT:
            *value = surface->getVGAlphaFormat();
            break;
        case EGL_VG_COLORSPACE:
            *value = surface->getVGColorspace();
            break;
        case EGL_CONFIG_ID:
            *value = surface->getConfig()->configID;
            break;
        case EGL_HEIGHT:
            ANGLE_TRY(surface->getUserHeight(display, value));
            break;
        case EGL_HORIZONTAL_RESOLUTION:
            *value = surface->getHorizontalResolution();
            break;
        case EGL_LARGEST_PBUFFER:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getLargestPbuffer();
            }
            break;
        case EGL_MIPMAP_TEXTURE:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getMipmapTexture();
            }
            break;
        case EGL_MIPMAP_LEVEL:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getMipmapLevel();
            }
            break;
        case EGL_MULTISAMPLE_RESOLVE:
            *value = surface->getMultisampleResolve();
            break;
        case EGL_PIXEL_ASPECT_RATIO:
            *value = surface->getPixelAspectRatio();
            break;
        case EGL_RENDER_BUFFER:
            *value = surface->getRenderBuffer();
            break;
        case EGL_SWAP_BEHAVIOR:
            *value = surface->getSwapBehavior();
            break;
        case EGL_TEXTURE_FORMAT:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = ToEGLenum(surface->getTextureFormat());
            }
            break;
        case EGL_TEXTURE_TARGET:
            // The EGL spec states that value is not written if the surface is not a pbuffer
            if (surface->getType() == EGL_PBUFFER_BIT)
            {
                *value = surface->getTextureTarget();
            }
            break;
        case EGL_VERTICAL_RESOLUTION:
            *value = surface->getVerticalResolution();
            break;
        case EGL_WIDTH:
            ANGLE_TRY(surface->getUserWidth(display, value));
            break;
        case EGL_POST_SUB_BUFFER_SUPPORTED_NV:
            *value = surface->isPostSubBufferSupported();
            break;
        case EGL_FIXED_SIZE_ANGLE:
            *value = surface->isFixedSize();
            break;
        case EGL_FLEXIBLE_SURFACE_COMPATIBILITY_SUPPORTED_ANGLE:
            *value = surface->flexibleSurfaceCompatibilityRequested();
            break;
        case EGL_SURFACE_ORIENTATION_ANGLE:
            *value = surface->getOrientation();
            break;
        case EGL_DIRECT_COMPOSITION_ANGLE:
            *value = surface->directComposition();
            break;
        case EGL_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
            *value = surface->isRobustResourceInitEnabled();
            break;
        case EGL_TIMESTAMPS_ANDROID:
            *value = surface->isTimestampsEnabled();
            break;
        case EGL_BUFFER_AGE_EXT:
            ANGLE_TRY(surface->getBufferAge(context, value));
            break;
        default:
            UNREACHABLE();
            break;
    }
    return NoError();
}

void SetSurfaceAttrib(Surface *surface, EGLint attribute, EGLint value)
{
    switch (attribute)
    {
        case EGL_MIPMAP_LEVEL:
            surface->setMipmapLevel(value);
            break;
        case EGL_MULTISAMPLE_RESOLVE:
            surface->setMultisampleResolve(value);
            break;
        case EGL_SWAP_BEHAVIOR:
            surface->setSwapBehavior(value);
            break;
        case EGL_WIDTH:
            surface->setFixedWidth(value);
            break;
        case EGL_HEIGHT:
            surface->setFixedHeight(value);
            break;
        case EGL_TIMESTAMPS_ANDROID:
            surface->setTimestampsEnabled(value != EGL_FALSE);
            break;
        default:
            UNREACHABLE();
            break;
    }
}

Error GetSyncAttrib(Display *display, Sync *sync, EGLint attribute, EGLint *value)
{
    switch (attribute)
    {
        case EGL_SYNC_TYPE_KHR:
            *value = sync->getType();
            return NoError();

        case EGL_SYNC_STATUS_KHR:
            return sync->getStatus(display, value);

        case EGL_SYNC_CONDITION_KHR:
            *value = sync->getCondition();
            return NoError();

        default:
            break;
    }

    UNREACHABLE();
    return NoError();
}
}  // namespace egl
