// Copyright 2018 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.
//
// PackedGLEnums.cpp:
//   Declares ANGLE-specific enums classes for GLEnum and functions operating
//   on them.

#include "common/PackedEnums.h"

#include "common/utilities.h"

namespace gl
{

TextureType TextureTargetToType(TextureTarget target)
{
    switch (target)
    {
        case TextureTarget::CubeMapNegativeX:
        case TextureTarget::CubeMapNegativeY:
        case TextureTarget::CubeMapNegativeZ:
        case TextureTarget::CubeMapPositiveX:
        case TextureTarget::CubeMapPositiveY:
        case TextureTarget::CubeMapPositiveZ:
            return TextureType::CubeMap;
        case TextureTarget::External:
            return TextureType::External;
        case TextureTarget::Rectangle:
            return TextureType::Rectangle;
        case TextureTarget::_2D:
            return TextureType::_2D;
        case TextureTarget::_2DArray:
            return TextureType::_2DArray;
        case TextureTarget::_2DMultisample:
            return TextureType::_2DMultisample;
        case TextureTarget::_2DMultisampleArray:
            return TextureType::_2DMultisampleArray;
        case TextureTarget::_3D:
            return TextureType::_3D;
        case TextureTarget::VideoImage:
            return TextureType::VideoImage;
        case TextureTarget::InvalidEnum:
            return TextureType::InvalidEnum;
        default:
            UNREACHABLE();
            return TextureType::InvalidEnum;
    }
}

bool IsCubeMapFaceTarget(TextureTarget target)
{
    return TextureTargetToType(target) == TextureType::CubeMap;
}

TextureTarget NonCubeTextureTypeToTarget(TextureType type)
{
    switch (type)
    {
        case TextureType::External:
            return TextureTarget::External;
        case TextureType::Rectangle:
            return TextureTarget::Rectangle;
        case TextureType::_2D:
            return TextureTarget::_2D;
        case TextureType::_2DArray:
            return TextureTarget::_2DArray;
        case TextureType::_2DMultisample:
            return TextureTarget::_2DMultisample;
        case TextureType::_2DMultisampleArray:
            return TextureTarget::_2DMultisampleArray;
        case TextureType::_3D:
            return TextureTarget::_3D;
        case TextureType::VideoImage:
            return TextureTarget::VideoImage;
        default:
            UNREACHABLE();
            return TextureTarget::InvalidEnum;
    }
}

// Check that we can do arithmetic on TextureTarget to convert from / to cube map faces
static_assert(static_cast<uint8_t>(TextureTarget::CubeMapNegativeX) -
                      static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) ==
                  1u,
              "");
static_assert(static_cast<uint8_t>(TextureTarget::CubeMapPositiveY) -
                      static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) ==
                  2u,
              "");
static_assert(static_cast<uint8_t>(TextureTarget::CubeMapNegativeY) -
                      static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) ==
                  3u,
              "");
static_assert(static_cast<uint8_t>(TextureTarget::CubeMapPositiveZ) -
                      static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) ==
                  4u,
              "");
static_assert(static_cast<uint8_t>(TextureTarget::CubeMapNegativeZ) -
                      static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) ==
                  5u,
              "");

TextureTarget CubeFaceIndexToTextureTarget(size_t face)
{
    ASSERT(face < 6u);
    return static_cast<TextureTarget>(static_cast<uint8_t>(TextureTarget::CubeMapPositiveX) + face);
}

size_t CubeMapTextureTargetToFaceIndex(TextureTarget target)
{
    ASSERT(IsCubeMapFaceTarget(target));
    return static_cast<uint8_t>(target) - static_cast<uint8_t>(TextureTarget::CubeMapPositiveX);
}

TextureType SamplerTypeToTextureType(GLenum samplerType)
{
    switch (samplerType)
    {
        case GL_SAMPLER_2D:
        case GL_INT_SAMPLER_2D:
        case GL_UNSIGNED_INT_SAMPLER_2D:
        case GL_SAMPLER_2D_SHADOW:
            return TextureType::_2D;

        case GL_SAMPLER_EXTERNAL_OES:
            return TextureType::External;

        case GL_SAMPLER_CUBE:
        case GL_INT_SAMPLER_CUBE:
        case GL_UNSIGNED_INT_SAMPLER_CUBE:
        case GL_SAMPLER_CUBE_SHADOW:
            return TextureType::CubeMap;

        case GL_SAMPLER_2D_ARRAY:
        case GL_INT_SAMPLER_2D_ARRAY:
        case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
        case GL_SAMPLER_2D_ARRAY_SHADOW:
            return TextureType::_2DArray;

        case GL_SAMPLER_3D:
        case GL_INT_SAMPLER_3D:
        case GL_UNSIGNED_INT_SAMPLER_3D:
            return TextureType::_3D;

        case GL_SAMPLER_2D_MULTISAMPLE:
        case GL_INT_SAMPLER_2D_MULTISAMPLE:
        case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE:
            return TextureType::_2DMultisample;

        case GL_SAMPLER_2D_MULTISAMPLE_ARRAY:
        case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
        case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY:
            return TextureType::_2DMultisampleArray;

        case GL_SAMPLER_2D_RECT_ANGLE:
            return TextureType::Rectangle;

        case GL_SAMPLER_VIDEO_IMAGE_WEBGL:
            return TextureType::VideoImage;

        default:
            UNREACHABLE();
            return TextureType::InvalidEnum;
    }
}

bool IsMultisampled(TextureType type)
{
    switch (type)
    {
        case TextureType::_2DMultisample:
        case TextureType::_2DMultisampleArray:
            return true;
        default:
            return false;
    }
}

bool IsArrayTextureType(TextureType type)
{
    switch (type)
    {
        case TextureType::_2DArray:
        case TextureType::_2DMultisampleArray:
            return true;
        default:
            return false;
    }
}

std::ostream &operator<<(std::ostream &os, PrimitiveMode value)
{
    switch (value)
    {
        case PrimitiveMode::LineLoop:
            os << "GL_LINE_LOOP";
            break;
        case PrimitiveMode::Lines:
            os << "GL_LINES";
            break;
        case PrimitiveMode::LinesAdjacency:
            os << "GL_LINES_ADJACENCY";
            break;
        case PrimitiveMode::LineStrip:
            os << "GL_LINE_STRIP";
            break;
        case PrimitiveMode::LineStripAdjacency:
            os << "GL_LINE_STRIP_ADJANCENCY";
            break;
        case PrimitiveMode::Points:
            os << "GL_POINTS";
            break;
        case PrimitiveMode::TriangleFan:
            os << "GL_TRIANGLE_FAN";
            break;
        case PrimitiveMode::Triangles:
            os << "GL_TRIANGLES";
            break;
        case PrimitiveMode::TrianglesAdjacency:
            os << "GL_TRIANGLES_ADJANCENCY";
            break;
        case PrimitiveMode::TriangleStrip:
            os << "GL_TRIANGLE_STRIP";
            break;
        case PrimitiveMode::TriangleStripAdjacency:
            os << "GL_TRIANGLE_STRIP_ADJACENCY";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}

std::ostream &operator<<(std::ostream &os, DrawElementsType value)
{
    switch (value)
    {
        case DrawElementsType::UnsignedByte:
            os << "GL_UNSIGNED_BYTE";
            break;
        case DrawElementsType::UnsignedShort:
            os << "GL_UNSIGNED_SHORT";
            break;
        case DrawElementsType::UnsignedInt:
            os << "GL_UNSIGNED_INT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }

    return os;
}

std::ostream &operator<<(std::ostream &os, VertexAttribType value)
{
    switch (value)
    {
        case VertexAttribType::Byte:
            os << "GL_BYTE";
            break;
        case VertexAttribType::Fixed:
            os << "GL_FIXED";
            break;
        case VertexAttribType::Float:
            os << "GL_FLOAT";
            break;
        case VertexAttribType::HalfFloat:
            os << "GL_HALF_FLOAT";
            break;
        case VertexAttribType::Int:
            os << "GL_INT";
            break;
        case VertexAttribType::Int2101010:
            os << "GL_INT_10_10_10_2";
            break;
        case VertexAttribType::Int1010102:
            os << "GL_INT_10_10_10_2_OES";
            break;
        case VertexAttribType::Short:
            os << "GL_SHORT";
            break;
        case VertexAttribType::UnsignedByte:
            os << "GL_UNSIGNED_BYTE";
            break;
        case VertexAttribType::UnsignedInt:
            os << "GL_UNSIGNED_INT";
            break;
        case VertexAttribType::UnsignedInt2101010:
            os << "GL_UNSIGNED_INT_10_10_10_2";
            break;
        case VertexAttribType::UnsignedInt1010102:
            os << "GL_UNSIGNED_INT_10_10_10_2_OES";
            break;
        case VertexAttribType::UnsignedShort:
            os << "GL_UNSIGNED_SHORT";
            break;
        default:
            os << "GL_INVALID_ENUM";
            break;
    }
    return os;
}
}  // namespace gl

namespace egl
{
MessageType ErrorCodeToMessageType(EGLint errorCode)
{
    switch (errorCode)
    {
        case EGL_BAD_ALLOC:
        case EGL_CONTEXT_LOST:
        case EGL_NOT_INITIALIZED:
            return MessageType::Critical;

        case EGL_BAD_ACCESS:
        case EGL_BAD_ATTRIBUTE:
        case EGL_BAD_CONFIG:
        case EGL_BAD_CONTEXT:
        case EGL_BAD_CURRENT_SURFACE:
        case EGL_BAD_DISPLAY:
        case EGL_BAD_MATCH:
        case EGL_BAD_NATIVE_PIXMAP:
        case EGL_BAD_NATIVE_WINDOW:
        case EGL_BAD_PARAMETER:
        case EGL_BAD_SURFACE:
        case EGL_BAD_STREAM_KHR:
        case EGL_BAD_STATE_KHR:
        case EGL_BAD_DEVICE_EXT:
            return MessageType::Error;

        case EGL_SUCCESS:
        default:
            UNREACHABLE();
            return MessageType::InvalidEnum;
    }
}
}  // namespace egl

namespace egl_gl
{

gl::TextureTarget EGLCubeMapTargetToCubeMapTarget(EGLenum eglTarget)
{
    ASSERT(egl::IsCubeMapTextureTarget(eglTarget));
    return gl::CubeFaceIndexToTextureTarget(egl::CubeMapTextureTargetToLayerIndex(eglTarget));
}

gl::TextureTarget EGLImageTargetToTextureTarget(EGLenum eglTarget)
{
    switch (eglTarget)
    {
        case EGL_GL_TEXTURE_2D_KHR:
            return gl::TextureTarget::_2D;

        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_X_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_X_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Y_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_POSITIVE_Z_KHR:
        case EGL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_KHR:
            return EGLCubeMapTargetToCubeMapTarget(eglTarget);

        case EGL_GL_TEXTURE_3D_KHR:
            return gl::TextureTarget::_3D;

        default:
            UNREACHABLE();
            return gl::TextureTarget::InvalidEnum;
    }
}

gl::TextureType EGLTextureTargetToTextureType(EGLenum eglTarget)
{
    switch (eglTarget)
    {
        case EGL_TEXTURE_2D:
            return gl::TextureType::_2D;

        case EGL_TEXTURE_RECTANGLE_ANGLE:
            return gl::TextureType::Rectangle;

        default:
            UNREACHABLE();
            return gl::TextureType::InvalidEnum;
    }
}

}  // namespace egl_gl
